From be8cf8f53326f7e320695cce9d535169a4a48b44 Mon Sep 17 00:00:00 2001 From: twi-devel Date: Fri, 14 Aug 2026 12:17:07 +0200 Subject: [PATCH] Adds migration repo as submod --- .gitmodules | 3 + CMakeLists.txt | 378 --- CTestCustom.ctest | 2 - Lts/CMakeLists.txt | 46 - Lts/Common/CMakeLists.txt | 41 - Lts/Common/CodeGen/CMakeLists.txt | 155 -- .../CodeGen/CppTopicGeneratorGrantlee.cpp | 345 --- .../CodeGen/CppTopicGeneratorGrantlee.h | 47 - Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp | 57 - Lts/Common/CodeGen/DaoGeneratorGrantlee.h | 25 - .../CodeGen/DatabaseGeneratorGrantlee.cpp | 59 - .../CodeGen/DatabaseGeneratorGrantlee.h | 27 - Lts/Common/CodeGen/GeneratorGrantleeBase.cpp | 200 -- Lts/Common/CodeGen/GeneratorGrantleeBase.h | 53 - Lts/Common/CodeGen/GrantleeWrappers.cpp | 226 -- Lts/Common/CodeGen/GrantleeWrappers.h | 643 ----- Lts/Common/CodeGen/README.txt | 37 - Lts/Common/CodeGen/SqlGenerator.cpp | 943 ------- Lts/Common/CodeGen/SqlGenerator.h | 175 -- Lts/Common/CodeGen/StructBuilder.cpp | 1431 ---------- Lts/Common/CodeGen/StructBuilder.h | 341 --- Lts/Common/CodeGen/XmlReader.cpp | 927 ------ Lts/Common/CodeGen/XmlReader.h | 427 --- Lts/Common/CodeGen/codegen_export.h | 17 - Lts/Common/CodeGen/codegen_log4cxx.props | 101 - Lts/Common/CodeGen/codegen_simple.py | 477 ---- Lts/Common/CodeGen/ea_export_script.txt | 1465 ---------- Lts/Common/CodeGen/main_grantlee.cpp | 172 -- Lts/Common/CodeGen/main_grantlee_gui.cpp | 158 -- Lts/Common/CodeGen/main_grantlee_gui.h | 39 - Lts/Common/CodeGen/main_grantlee_gui.ui | 140 - Lts/Common/CodeGen/svc.idl.grantlee | 39 - Lts/Common/CodeGen/svc_dao.cpp.grantlee | 204 -- Lts/Common/CodeGen/svc_dao.h.grantlee | 82 - Lts/Common/CodeGen/svc_db.h.grantlee | 145 - Lts/Common/CodeGen/svc_db_impl.cpp.grantlee | 732 ----- Lts/Common/CodeGen/svc_db_impl.h.grantlee | 102 - .../CodeGen/svc_db_structs.cpp.grantlee | 95 - Lts/Common/CodeGen/svc_db_structs.h.grantlee | 87 - Lts/Common/CodeGen/svc_dtc.h.grantlee | 88 - Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee | 304 -- Lts/Common/CodeGen/svc_dtc_impl.h.grantlee | 99 - Lts/Common/CodeGen/svc_dts.h.grantlee | 69 - Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee | 100 - Lts/Common/CodeGen/svc_dts_impl.h.grantlee | 58 - Lts/Common/CodeGen/svc_enums.idl.grantlee | 32 - Lts/Common/CodeGen/svc_ftc.h.grantlee | 78 - Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee | 254 -- Lts/Common/CodeGen/svc_ftc_impl.h.grantlee | 81 - Lts/Common/CodeGen/svc_fts.h.grantlee | 90 - Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee | 114 - Lts/Common/CodeGen/svc_fts_impl.h.grantlee | 66 - Lts/Common/CodeGen/svc_json.cpp.grantlee | 160 -- Lts/Common/CodeGen/svc_json.h.grantlee | 50 - .../svc_record_replay_plugin.cpp.grantlee | 876 ------ ...ay_plugin.cpp.grantlee_possible_extensions | 637 ----- .../svc_record_replay_plugin.h.grantlee | 126 - .../svc_topic_type_plugin.cpp.grantlee | 102 - .../CodeGen/svc_topic_type_plugin.h.grantlee | 39 - .../CodeGen/svc_translation.cpp.grantlee | 43 - Lts/Common/CodeGen/svc_translation.h.grantlee | 31 - .../CodeGen/tests/GrantleeParserSmoke.cpp | 62 - .../CommonUnitTestsPlaceholder/CMakeLists.txt | 32 - .../coordTransTests_gtest.cpp | 12 - .../coordTransTests_smoke.cpp | 15 - .../mocks/CMakeLists.txt | 5 - .../mocks/MockCoordTrans.h | 16 - Lts/Common/LtsAppDescriptor/CMakeLists.txt | 78 - Lts/Common/LtsAppDescriptor/checker/main.cpp | 138 - .../LtsAppDescriptor/lts_app_descriptor.cpp | 215 -- .../LtsAppDescriptor/lts_app_descriptor.h | 96 - .../lts_app_descriptor_editor.cpp | 554 ---- .../lts_app_descriptor_editor.h | 66 - .../lts_app_descriptor_editor.ui | 434 --- Lts/Common/LtsAppDescriptor/main.cpp | 56 - .../AppChooser/CustomViews/app.cpp | 35 - .../AppChooser/CustomViews/app.h | 30 - .../AppChooser/CustomViews/app.ui | 108 - .../LtsAppManager/AppChooser/app_chooser.cpp | 93 - .../LtsAppManager/AppChooser/app_chooser.h | 47 - .../LtsAppManager/AppChooser/app_chooser.ui | 92 - .../AppChooser/app_chooser_model.cpp | 50 - .../AppChooser/app_chooser_model.h | 29 - Lts/Common/LtsAppManager/CMakeLists.txt | 52 - Lts/Common/LtsAppManager/lts_app_manager.cpp | 1151 -------- Lts/Common/LtsAppManager/lts_app_manager.h | 123 - Lts/Common/LtsBotan/CMakeLists.txt | 12 - .../LtsCommandLineParser/CMakeLists.txt | 21 - .../LtsCommandLineParser.cpp | 183 -- .../LtsCommandLineParser.h | 56 - Lts/Common/LtsCoordTrans/CMakeLists.txt | 83 - Lts/Common/LtsCoordTrans/lonlat2mgrs.cpp | 67 - Lts/Common/LtsCoordTrans/lts_coord_trans.cpp | 445 --- Lts/Common/LtsCoordTrans/lts_coord_trans.h | 249 -- .../lts_coord_trans_unit_test.cpp | 469 ---- .../LtsCoordTrans/lts_coord_trans_unit_test.h | 34 - Lts/Common/LtsCoordTrans/lts_coord_widget.cpp | 196 -- Lts/Common/LtsCoordTrans/lts_coord_widget.h | 78 - .../LtsCoordTrans/lts_coord_widget_mgrs.ui | 55 - .../LtsCoordTrans/lts_coord_widget_p.cpp | 325 --- Lts/Common/LtsCoordTrans/lts_coord_widget_p.h | 93 - .../LtsCoordTrans/lts_coord_widget_test.cpp | 115 - .../LtsCoordTrans/lts_coord_widget_wgs84.ui | 58 - Lts/Common/LtsCoordTrans/mgrs2lonlat.cpp | 45 - Lts/Common/LtsCoordinate/CMakeLists.txt | 26 - .../UnitTest_Coordinate/CMakeLists.txt | 30 - .../lts_coordinate_unit_test.cpp | 971 ------- .../lts_coordinate_unit_test.h | 80 - Lts/Common/LtsCoordinate/lts_coord_vector.cpp | 90 - Lts/Common/LtsCoordinate/lts_coord_vector.h | 81 - Lts/Common/LtsCoordinate/lts_coordinate.cpp | 178 -- Lts/Common/LtsCoordinate/lts_coordinate.h | 367 --- Lts/Common/LtsElevation/CMakeLists.txt | 30 - .../UnitTest_Elevation/CMakeLists.txt | 29 - .../elevation_unit_test.cpp | 230 -- .../UnitTest_Elevation/elevation_unit_test.h | 56 - Lts/Common/LtsElevation/logger.cpp | 32 - Lts/Common/LtsElevation/logger.h | 18 - .../LtsElevation/lts_elevation_manager.cpp | 510 ---- .../LtsElevation/lts_elevation_manager.h | 129 - Lts/Common/LtsLicence/CMakeLists.txt | 41 - Lts/Common/LtsLicence/aes_crypt.cpp | 232 -- Lts/Common/LtsLicence/aes_crypt.h | 22 - Lts/Common/LtsLicence/aes_crypt_test.cpp | 54 - Lts/Common/LtsLicence/aes_crypt_test.h | 15 - Lts/Common/LtsLicence/lts_licence.cpp | 173 -- Lts/Common/LtsLicence/lts_licence.h | 33 - Lts/Common/LtsLogin/CMakeLists.txt | 42 - Lts/Common/LtsLogin/logout_info_dialog.cpp | 94 - Lts/Common/LtsLogin/logout_info_dialog.h | 43 - Lts/Common/LtsLogin/logout_info_dialog.ui | 76 - Lts/Common/LtsLogin/lts_login.cpp | 312 --- Lts/Common/LtsLogin/lts_login.h | 69 - Lts/Common/LtsMultiMonitor/CMakeLists.txt | 49 - .../LtsMultiMonitor/lts_dummy_widget.cpp | 44 - Lts/Common/LtsMultiMonitor/lts_dummy_widget.h | 43 - .../LtsMultiMonitor/lts_magic_corner.cpp | 214 -- Lts/Common/LtsMultiMonitor/lts_magic_corner.h | 55 - .../LtsMultiMonitor/lts_multi_monitor.cpp | 696 ----- .../LtsMultiMonitor/lts_multi_monitor.h | 85 - .../lts_multi_monitor_export.h | 17 - .../lts_multi_monitor_selector_base.cpp | 419 --- .../lts_multi_monitor_selector_base.h | 64 - .../lts_multi_monitor_selector_normal.cpp | 100 - .../lts_multi_monitor_selector_normal.h | 37 - .../lts_multi_monitor_selector_wide.cpp | 324 --- .../lts_multi_monitor_selector_wide.h | 36 - .../LtsMultiMonitor/lts_screen_base.cpp | 70 - Lts/Common/LtsMultiMonitor/lts_screen_base.h | 59 - .../LtsMultiMonitor/lts_screen_container.cpp | 555 ---- .../LtsMultiMonitor/lts_screen_container.h | 68 - .../LtsMultiMonitor/lts_screen_normal.cpp | 201 -- .../LtsMultiMonitor/lts_screen_normal.h | 58 - .../LtsMultiMonitor/lts_screen_wide.cpp | 236 -- Lts/Common/LtsMultiMonitor/lts_screen_wide.h | 51 - Lts/Common/LtsNotification/CMakeLists.txt | 29 - .../LtsNotification/lts_notification.cpp | 92 - Lts/Common/LtsNotification/lts_notification.h | 57 - .../LtsNotification/lts_notification_ctl.cpp | 172 -- .../LtsNotification/lts_notification_ctl.h | 67 - .../LtsNotification/lts_notification_export.h | 19 - Lts/Common/LtsPluginManager/CMakeLists.txt | 65 - .../Ui/lts_plugin_manager_dialog.cpp | 102 - .../Ui/lts_plugin_manager_dialog.h | 38 - .../Ui/lts_plugin_manager_dialog.ui | 71 - .../Ui/lts_plugin_manager_widget.cpp | 130 - .../Ui/lts_plugin_manager_widget.h | 45 - .../Ui/lts_plugin_manager_widget.ui | 196 -- .../LtsPluginManager/lts_plugin_loader.cpp | 449 --- .../LtsPluginManager/lts_plugin_loader.h | 85 - .../LtsPluginManager/lts_plugin_manager.cpp | 286 -- .../LtsPluginManager/lts_plugin_manager.h | 110 - .../lts_plugin_manager_export.h | 17 - Lts/Common/LtsPrinting/CMakeLists.txt | 38 - Lts/Common/LtsPrinting/printing.cpp | 430 --- Lts/Common/LtsPrinting/printing.h | 94 - Lts/Common/LtsPrinting/printing_base.cpp | 1305 --------- Lts/Common/LtsPrinting/printing_base.h | 299 -- .../LtsProjectionWorkstation/CMakeLists.txt | 47 - .../WebSocket/client_message.cpp | 63 - .../WebSocket/client_message.h | 65 - .../WebSocket/json_keys.h | 35 - .../WebSocket/message_factory.cpp | 53 - .../WebSocket/message_factory.h | 30 - .../WebSocket/messages.cpp | 210 -- .../WebSocket/messages.h | 192 -- .../WebSocket/reply_message.cpp | 21 - .../WebSocket/reply_message.h | 24 - .../LtsProjectionWorkstation/logger.cpp | 32 - Lts/Common/LtsProjectionWorkstation/logger.h | 18 - .../lts_projection_workstation.cpp | 247 -- .../lts_projection_workstation.h | 84 - .../lts_projection_workstation_export.h | 17 - .../vnc_server_manager.cpp | 194 -- .../vnc_server_manager.h | 69 - Lts/Common/LtsScriptTimeline/CMakeLists.txt | 70 - .../LtsScriptTimeline/Test/CMakeLists.txt | 24 - Lts/Common/LtsScriptTimeline/Test/main.cpp | 12 - .../LtsScriptTimeline/Test/mainwindow.cpp | 104 - .../LtsScriptTimeline/Test/mainwindow.h | 34 - .../LtsScriptTimeline/Test/mainwindow.ui | 564 ---- .../Ui/filter_track_widget.ui | 126 - Lts/Common/LtsScriptTimeline/Ui/indicator.ui | 63 - .../LtsScriptTimeline/Ui/script_timeline.ui | 205 -- .../Ui/track_entry_preview_widget.ui | 99 - .../LtsScriptTimeline/Ui/track_widget.ui | 245 -- .../filter_background_widget.cpp | 190 -- .../filter_background_widget.h | 53 - .../LtsScriptTimeline/filter_track_widget.cpp | 155 -- .../LtsScriptTimeline/filter_track_widget.h | 73 - Lts/Common/LtsScriptTimeline/indicator.cpp | 82 - Lts/Common/LtsScriptTimeline/indicator.h | 41 - Lts/Common/LtsScriptTimeline/newsrc.tar.xz | Bin 52292 -> 0 bytes .../LtsScriptTimeline/script_timeline.cpp | 1202 -------- .../LtsScriptTimeline/script_timeline.h | 125 - .../temporary_pointer_vector.h | 50 - Lts/Common/LtsScriptTimeline/tools.cpp | 84 - Lts/Common/LtsScriptTimeline/tools.h | 33 - .../track_content_widget.cpp | 1652 ----------- .../LtsScriptTimeline/track_content_widget.h | 186 -- .../LtsScriptTimeline/track_drag_handler.cpp | 181 -- .../LtsScriptTimeline/track_drag_handler.h | 35 - Lts/Common/LtsScriptTimeline/track_entry.h | 187 -- .../track_entry_painters.cpp | 321 --- .../LtsScriptTimeline/track_entry_painters.h | 90 - .../track_entry_preview_widget.cpp | 105 - .../track_entry_preview_widget.h | 43 - .../track_entry_previewer.cpp | 86 - .../LtsScriptTimeline/track_entry_previewer.h | 38 - Lts/Common/LtsScriptTimeline/track_header.cpp | 191 -- Lts/Common/LtsScriptTimeline/track_header.h | 43 - .../track_resize_handler.cpp | 182 -- .../LtsScriptTimeline/track_resize_handler.h | 40 - .../track_select_handler.cpp | 182 -- .../LtsScriptTimeline/track_select_handler.h | 39 - Lts/Common/LtsScriptTimeline/track_widget.cpp | 559 ---- Lts/Common/LtsScriptTimeline/track_widget.h | 89 - Lts/Common/LtsSerialPortQuerry/CMakeLists.txt | 31 - Lts/Common/LtsSerialPortQuerry/logger.cpp | 32 - Lts/Common/LtsSerialPortQuerry/logger.h | 18 - .../LtsSerialPortQuerry/lts_comport_querry.h | 52 - .../lts_serial_port_querry.cpp | 153 - .../lts_serial_port_querry.h | 56 - Lts/Common/LtsSession/CMakeLists.txt | 87 - Lts/Common/LtsSession/lts_session.cpp | 1649 ----------- Lts/Common/LtsSession/lts_session.h | 285 -- Lts/Common/LtsSession/lts_session_data.cpp | 107 - Lts/Common/LtsSession/lts_session_data.h | 133 - .../LtsSession/lts_session_data_dds.cpp | 389 --- Lts/Common/LtsSession/lts_session_data_dds.h | 349 --- .../LtsSession/lts_session_lock_handler.cpp | 290 -- .../LtsSession/lts_session_lock_handler.h | 87 - Lts/Common/LtsSession/lts_session_manager.cpp | 240 -- Lts/Common/LtsSession/lts_session_manager.h | 106 - Lts/Common/LtsTimeline/CMakeLists.txt | 74 - Lts/Common/LtsTimeline/item.cpp | 45 - Lts/Common/LtsTimeline/item.h | 87 - Lts/Common/LtsTimeline/item_delegate.cpp | 185 -- Lts/Common/LtsTimeline/item_delegate.h | 47 - Lts/Common/LtsTimeline/log4cxx_timeline.props | 66 - Lts/Common/LtsTimeline/main.cpp | 289 -- .../LtsTimeline/timeline_header_view.cpp | 679 ----- Lts/Common/LtsTimeline/timeline_header_view.h | 172 -- .../timeline_header_view_mouse_handler.cpp | 160 -- .../timeline_header_view_mouse_handler.h | 49 - Lts/Common/LtsTimeline/timeline_model.cpp | 761 ----- Lts/Common/LtsTimeline/timeline_model.h | 247 -- Lts/Common/LtsTimeline/timeline_utils.cpp | 343 --- Lts/Common/LtsTimeline/timeline_utils.h | 83 - Lts/Common/LtsTimeline/timeline_view.cpp | 672 ----- Lts/Common/LtsTimeline/timeline_view.h | 175 -- .../timeline_view_drag_handler.cpp | 505 ---- .../LtsTimeline/timeline_view_drag_handler.h | 82 - .../timeline_view_resize_handler.cpp | 279 -- .../timeline_view_resize_handler.h | 42 - .../timeline_view_selection_handler.cpp | 320 --- .../timeline_view_selection_handler.h | 48 - .../timeline_view_track_handler.cpp | 107 - .../LtsTimeline/timeline_view_track_handler.h | 38 - Lts/Common/LtsTimeline/timeline_widget.cpp | 402 --- Lts/Common/LtsTimeline/timeline_widget.h | 114 - Lts/Common/LtsTimeline/timeline_widget.ui | 434 --- Lts/Common/LtsTimeline/track.cpp | 853 ------ Lts/Common/LtsTimeline/track.h | 263 -- .../AbstractTimelineModel.cpp | 283 -- .../AbstractTimelineModel.h | 112 - Lts/Common/LtsTimelineClassic/CMakeLists.txt | 82 - Lts/Common/LtsTimelineClassic/Event.cpp | 219 -- Lts/Common/LtsTimelineClassic/Event.h | 155 -- .../LtsTimelineClassic/EventDelegate.cpp | 62 - Lts/Common/LtsTimelineClassic/EventDelegate.h | 55 - .../LtsTimelineClassic/EventGraphic.cpp | 175 -- Lts/Common/LtsTimelineClassic/EventGraphic.h | 48 - Lts/Common/LtsTimelineClassic/EventList.h | 17 - Lts/Common/LtsTimelineClassic/EventTools.cpp | 420 --- Lts/Common/LtsTimelineClassic/EventTools.h | 152 - Lts/Common/LtsTimelineClassic/Item.cpp | 388 --- Lts/Common/LtsTimelineClassic/Item.h | 128 - Lts/Common/LtsTimelineClassic/LineMap.h | 13 - .../PassiveEventContainer.cpp | 133 - .../PassiveEventContainer.h | 53 - Lts/Common/LtsTimelineClassic/Range.h | 18 - .../LtsTimelineClassic/TestTimelineModel.cpp | 40 - .../LtsTimelineClassic/TestTimelineModel.h | 25 - .../LtsTimelineClassic/TimelineHeaderView.cpp | 149 - .../LtsTimelineClassic/TimelineHeaderView.h | 63 - .../LtsTimelineClassic/TimelineIndicator.cpp | 519 ---- .../LtsTimelineClassic/TimelineIndicator.h | 111 - .../LtsTimelineClassic/TimelineView.cpp | 392 --- Lts/Common/LtsTimelineClassic/TimelineView.h | 107 - .../LtsTimelineClassic/TimelineWidget.cpp | 324 --- .../LtsTimelineClassic/TimelineWidget.h | 123 - .../LtsTimelineClassic/Ui/TimelineWidgetUi.ui | 167 -- .../LtsTimelineClassic/log4cxx_timeline.props | 66 - Lts/Common/LtsTimelineClassic/main.cpp | 82 - Lts/Common/LtsUtils/CMakeLists.txt | 12 - .../LtsUtils/LtsUtilsCore/CMakeLists.txt | 68 - .../LtsUtilsCore/UnitTests/utils_test.cpp | 1202 -------- .../LtsUtilsCore/UnitTests/utils_test.h | 62 - Lts/Common/LtsUtils/LtsUtilsCore/await.h | 116 - Lts/Common/LtsUtils/LtsUtilsCore/connection.h | 99 - .../LtsUtilsCore/data_modification_enum.h | 30 - .../LtsUtils/LtsUtilsCore/dds_bitarray.h | 98 - Lts/Common/LtsUtils/LtsUtilsCore/dds_vector.h | 35 - .../LtsUtilsCore/dds_vector_to_pixmap.h | 24 - Lts/Common/LtsUtils/LtsUtilsCore/json.h | 1 - .../LtsUtils/LtsUtilsCore/json_object.h | 519 ---- .../LtsUtils/LtsUtilsCore/lts_portrange.cpp | 14 - .../LtsUtils/LtsUtilsCore/lts_portrange.h | 15 - .../lts_translatable_identifier.cpp | 25 - .../lts_translatable_identifier.h | 33 - .../LtsUtils/LtsUtilsCore/outdated_position.h | 15 - Lts/Common/LtsUtils/LtsUtilsCore/pgarray.cpp | 169 -- Lts/Common/LtsUtils/LtsUtilsCore/pgarray.h | 34 - Lts/Common/LtsUtils/LtsUtilsCore/promise.cpp | 103 - Lts/Common/LtsUtils/LtsUtilsCore/promise.h | 72 - .../LtsUtilsCore/promise_examples.cpp | 165 -- .../LtsUtils/LtsUtilsCore/promise_examples.h | 54 - .../LtsUtilsCore/qbytearray_byteaccess.h | 241 -- Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.cpp | 88 - Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.h | 59 - Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.cpp | 48 - Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.h | 296 -- .../LtsUtilsCore/scoped_connection.cpp | 22 - .../LtsUtils/LtsUtilsCore/scoped_connection.h | 26 - .../sfm_bytemessage_converter.cpp | 301 -- .../LtsUtilsCore/sfm_bytemessage_converter.h | 64 - .../LtsUtils/LtsUtilsCore/variant_operators.h | 39 - .../LtsUtils/LtsUtilsGui/CMakeLists.txt | 11 - .../LtsUtilsGui/ConnectDialog/CMakeLists.txt | 28 - .../ConnectDialog/connect_dialog.cpp | 73 - .../ConnectDialog/connect_dialog.h | 82 - .../ConnectDialog/connect_dialog.ui | 76 - .../ConnectDialog/connect_dialog_nt.cpp | 157 -- .../ConnectDialog/connect_dialog_nt.h | 63 - .../LtsUtilsGui/LtsUtilsGuiDds/CMakeLists.txt | 49 - .../LtsUtilsGuiDds/dds_vector_to_pixmap.cpp | 134 - .../LtsUtilsGuiDds/dds_vector_to_pixmap.h | 33 - .../LtsUtilsGuiDds/request_helper.cpp | 276 -- .../LtsUtilsGuiDds/request_helper.h | 123 - .../LtsUtilsGui/StyleManager/CMakeLists.txt | 22 - .../StyleManager/style_manager.cpp | 114 - .../LtsUtilsGui/StyleManager/style_manager.h | 36 - .../LtsUtils/LtsUtilsSettings/CMakeLists.txt | 21 - .../LtsUtils/LtsUtilsSettings/settings.cpp | 264 -- .../LtsUtils/LtsUtilsSettings/settings.h | 73 - Lts/Common/LtsVirtualKeyboard/CMakeLists.txt | 36 - .../LtsVirtualKeyboard/LtsKeyPushButton.cpp | 150 - .../LtsVirtualKeyboard/LtsKeyPushButton.h | 89 - .../LtsVirtualKeyboard/LtsVirtualKeyboard.cpp | 953 ------- .../LtsVirtualKeyboard/LtsVirtualKeyboard.h | 97 - Lts/Common/LtsWebSocket/CMakeLists.txt | 79 - Lts/Common/LtsWebSocket/client_connection.cpp | 84 - Lts/Common/LtsWebSocket/client_connection.h | 70 - Lts/Common/LtsWebSocket/client_message.cpp | 38 - Lts/Common/LtsWebSocket/client_message.h | 70 - .../LtsWebSocket/client_message_factory.cpp | 69 - .../LtsWebSocket/client_message_factory.h | 71 - .../LtsWebSocket/connection_handler.cpp | 89 - Lts/Common/LtsWebSocket/connection_handler.h | 64 - .../LtsWebSocket/data_manager_base_dl.h | 48 - Lts/Common/LtsWebSocket/json_keys.h | 26 - Lts/Common/LtsWebSocket/logger.cpp | 32 - Lts/Common/LtsWebSocket/logger.h | 18 - .../LtsWebSocket/lts_web_socket_client.cpp | 202 -- .../LtsWebSocket/lts_web_socket_client.h | 67 - .../LtsWebSocket/lts_web_socket_server.cpp | 119 - .../LtsWebSocket/lts_web_socket_server.h | 88 - Lts/Common/LtsWebSocket/message_transmitter.h | 17 - Lts/Common/LtsWebSocket/reply_message.cpp | 41 - Lts/Common/LtsWebSocket/reply_message.h | 70 - Lts/Common/LtsWidgets/CMakeLists.txt | 210 -- Lts/Common/LtsWidgets/CMakeLists_rc.txt | 208 -- .../LtsImageCropper/lts_image_cropper.cpp | 131 - .../LtsImageCropper/lts_image_cropper.h | 80 - .../lts_abstract_sequence_widget.cpp | 120 - .../LtsWidgets/lts_abstract_sequence_widget.h | 89 - .../LtsWidgets/lts_abstract_table_model.h | 286 -- Lts/Common/LtsWidgets/lts_angle_edit.cpp | 246 -- Lts/Common/LtsWidgets/lts_angle_edit.h | 71 - Lts/Common/LtsWidgets/lts_angle_edit.ui | 49 - .../LtsWidgets/lts_background_widget.cpp | 137 - Lts/Common/LtsWidgets/lts_background_widget.h | 51 - Lts/Common/LtsWidgets/lts_base.h | 274 -- Lts/Common/LtsWidgets/lts_base_layout.ui | 645 ----- .../LtsWidgets/lts_base_layout_widget.cpp | 1828 ------------ .../LtsWidgets/lts_base_layout_widget.h | 346 --- Lts/Common/LtsWidgets/lts_bitfield_widget.cpp | 129 - Lts/Common/LtsWidgets/lts_bitfield_widget.h | 95 - .../LtsWidgets/lts_checkable_table_model.h | 131 - .../lts_column_filter_proxy_model.cpp | 156 -- .../lts_column_filter_proxy_model.h | 122 - .../LtsWidgets/lts_column_organiser_widget.ui | 75 - Lts/Common/LtsWidgets/lts_combo_box.cpp | 118 - Lts/Common/LtsWidgets/lts_combo_box.h | 59 - Lts/Common/LtsWidgets/lts_date_time_edit.cpp | 1785 ------------ Lts/Common/LtsWidgets/lts_date_time_edit.h | 213 -- .../LtsWidgets/lts_date_time_edit_p.cpp | 118 - Lts/Common/LtsWidgets/lts_date_time_edit_p.h | 43 - .../LtsWidgets/lts_date_time_edit_rc.cpp | 1792 ------------ Lts/Common/LtsWidgets/lts_date_time_edit_rc.h | 206 -- .../LtsWidgets/lts_date_time_edit_touch.cpp | 389 --- .../LtsWidgets/lts_date_time_edit_touch.h | 140 - .../LtsWidgets/lts_date_time_edit_touch.ui | 181 -- Lts/Common/LtsWidgets/lts_dialog.cpp | 810 ------ Lts/Common/LtsWidgets/lts_dialog.h | 473 ---- .../LtsWidgets/lts_dialog_button_box.cpp | 217 -- Lts/Common/LtsWidgets/lts_dialog_button_box.h | 196 -- .../LtsWidgets/lts_dialog_button_box.ui | 326 --- Lts/Common/LtsWidgets/lts_dialog_helper.cpp | 172 -- Lts/Common/LtsWidgets/lts_dialog_helper.h | 51 - Lts/Common/LtsWidgets/lts_dialog_helper.ui | 70 - Lts/Common/LtsWidgets/lts_drag_icon.cpp | 489 ---- Lts/Common/LtsWidgets/lts_drag_icon.h | 246 -- Lts/Common/LtsWidgets/lts_drop_handler.h | 31 - Lts/Common/LtsWidgets/lts_elided_label.cpp | 60 - Lts/Common/LtsWidgets/lts_elided_label.h | 36 - Lts/Common/LtsWidgets/lts_events.cpp | 58 - Lts/Common/LtsWidgets/lts_events.h | 94 - .../LtsWidgets/lts_frameless_dialog.cpp | 153 - Lts/Common/LtsWidgets/lts_frameless_dialog.h | 46 - .../LtsWidgets/lts_global_wait_dialog.cpp | 225 -- .../LtsWidgets/lts_global_wait_dialog.h | 69 - Lts/Common/LtsWidgets/lts_grid_layout.cpp | 128 - Lts/Common/LtsWidgets/lts_grid_layout.h | 61 - .../LtsWidgets/lts_height_plot_widget.cpp | 621 ----- .../LtsWidgets/lts_height_plot_widget.h | 69 - Lts/Common/LtsWidgets/lts_image_editor.cpp | 116 - Lts/Common/LtsWidgets/lts_image_editor.h | 60 - Lts/Common/LtsWidgets/lts_info_dialog.cpp | 81 - Lts/Common/LtsWidgets/lts_info_dialog.h | 28 - Lts/Common/LtsWidgets/lts_info_dialog.ui | 135 - Lts/Common/LtsWidgets/lts_led_widget.cpp | 148 - Lts/Common/LtsWidgets/lts_led_widget.h | 117 - Lts/Common/LtsWidgets/lts_message_dialog.cpp | 133 - Lts/Common/LtsWidgets/lts_message_dialog.h | 86 - .../LtsWidgets/lts_message_dialog_helper.cpp | 155 -- .../LtsWidgets/lts_message_dialog_helper.h | 52 - .../LtsWidgets/lts_msec_date_time_edit.cpp | 114 - .../LtsWidgets/lts_msec_date_time_edit.h | 41 - .../LtsWidgets/lts_multi_range_spinbox.cpp | 313 --- .../LtsWidgets/lts_multi_range_spinbox.h | 52 - .../LtsWidgets/lts_notification_box.cpp | 198 -- Lts/Common/LtsWidgets/lts_notification_box.h | 50 - Lts/Common/LtsWidgets/lts_notification_box.ui | 182 -- Lts/Common/LtsWidgets/lts_operator_menu.cpp | 114 - Lts/Common/LtsWidgets/lts_operator_menu.h | 42 - Lts/Common/LtsWidgets/lts_operator_menu.ui | 247 -- Lts/Common/LtsWidgets/lts_overlay_loading.cpp | 45 - Lts/Common/LtsWidgets/lts_overlay_loading.h | 34 - Lts/Common/LtsWidgets/lts_overlay_loading.ui | 76 - Lts/Common/LtsWidgets/lts_picture_view.cpp | 339 --- Lts/Common/LtsWidgets/lts_picture_view.h | 60 - Lts/Common/LtsWidgets/lts_profile_button.cpp | 133 - Lts/Common/LtsWidgets/lts_profile_button.h | 55 - .../LtsWidgets/lts_progress_dialog_helper.cpp | 147 - .../LtsWidgets/lts_progress_dialog_helper.h | 66 - Lts/Common/LtsWidgets/lts_range_slider.cpp | 823 ------ Lts/Common/LtsWidgets/lts_range_slider.h | 194 -- Lts/Common/LtsWidgets/lts_selection_popup.cpp | 212 -- Lts/Common/LtsWidgets/lts_selection_popup.h | 83 - Lts/Common/LtsWidgets/lts_selection_popup.ui | 232 -- Lts/Common/LtsWidgets/lts_spinbox.cpp | 173 -- Lts/Common/LtsWidgets/lts_spinbox.h | 60 - Lts/Common/LtsWidgets/lts_splash.cpp | 81 - Lts/Common/LtsWidgets/lts_splash.h | 28 - Lts/Common/LtsWidgets/lts_splitter.cpp | 57 - Lts/Common/LtsWidgets/lts_splitter.h | 37 - Lts/Common/LtsWidgets/lts_splitter_handle.cpp | 87 - Lts/Common/LtsWidgets/lts_splitter_handle.h | 39 - Lts/Common/LtsWidgets/lts_tab_bar.cpp | 120 - Lts/Common/LtsWidgets/lts_tab_bar.h | 39 - .../LtsWidgets/lts_tab_sequence_widget.cpp | 59 - .../LtsWidgets/lts_tab_sequence_widget.h | 37 - Lts/Common/LtsWidgets/lts_tab_widget.cpp | 894 ------ Lts/Common/LtsWidgets/lts_tab_widget.h | 163 -- .../LtsWidgets/lts_text_edit_dialog.cpp | 137 - Lts/Common/LtsWidgets/lts_text_edit_dialog.h | 55 - Lts/Common/LtsWidgets/lts_text_editor.cpp | 391 --- Lts/Common/LtsWidgets/lts_text_editor.h | 61 - Lts/Common/LtsWidgets/lts_text_editor.ui | 598 ---- Lts/Common/LtsWidgets/lts_text_editor2.cpp | 920 ------ Lts/Common/LtsWidgets/lts_text_editor2.h | 135 - Lts/Common/LtsWidgets/lts_tool_bar.cpp | 31 - Lts/Common/LtsWidgets/lts_tool_bar.h | 27 - Lts/Common/LtsWidgets/lts_tool_bar_area.cpp | 384 --- Lts/Common/LtsWidgets/lts_tool_bar_area.h | 115 - Lts/Common/LtsWidgets/lts_tool_bar_area.ui | 38 - Lts/Common/LtsWidgets/lts_tool_box.cpp | 85 - Lts/Common/LtsWidgets/lts_tool_box.h | 31 - Lts/Common/LtsWidgets/lts_wait_dialog.cpp | 17 - Lts/Common/LtsWidgets/lts_wait_dialog.h | 21 - Lts/Common/LtsWidgets/lts_widgets_export.h | 17 - Lts/Common/LtsWidgets/ltswidgetsplugin.cpp | 1196 -------- Lts/Common/LtsWidgets/ltswidgetsplugin.json | 5 - Lts/Common/LtsWidgets/resources/DMF.wav | Bin 355352 -> 0 bytes .../resources/European-Union-Flag.png | Bin 25659 -> 0 bytes .../LtsWidgets/resources/Germany-Flag.png | Bin 10580 -> 0 bytes .../resources/Great-Britain-Flag.png | Bin 19076 -> 0 bytes .../resources/ScriptEditor-Changelog-1.png | Bin 12806 -> 0 bytes Lts/Common/LtsWidgets/resources/US-Flag.png | Bin 27528 -> 0 bytes Lts/Common/LtsWidgets/resources/ack.svg | 56 - Lts/Common/LtsWidgets/resources/ack_dark.svg | 56 - Lts/Common/LtsWidgets/resources/beep.wav | Bin 26876 -> 0 bytes Lts/Common/LtsWidgets/resources/close.svg | 35 - .../LtsWidgets/resources/close_dark.svg | 69 - .../LtsWidgets/resources/effectarea-enter.wav | Bin 97136 -> 0 bytes .../LtsWidgets/resources/effectarea-leave.wav | Bin 88224 -> 0 bytes Lts/Common/LtsWidgets/resources/entryNew.wav | Bin 649808 -> 0 bytes Lts/Common/LtsWidgets/resources/error.svg | 66 - Lts/Common/LtsWidgets/resources/info.svg | 71 - .../LtsWidgets/resources/jquery-3.1.0.min.js | 4 - .../LtsWidgets/resources/jquery.orgchart.js | 1518 ---------- Lts/Common/LtsWidgets/resources/leaveNew.wav | Bin 653032 -> 0 bytes .../LtsWidgets/resources/lts_resources.qrc | 39 - .../LtsWidgets/resources/qwebchannel.js | 427 --- .../LtsWidgets/resources/rde_icon_256.ico | Bin 270398 -> 0 bytes .../LtsWidgets/resources/rde_icon_256.png | Bin 15637 -> 0 bytes .../rde_internal_dds_logo_256x256.ico | Bin 270398 -> 0 bytes .../rde_internal_dds_logo_256x256.png | Bin 43494 -> 0 bytes .../rde_internal_sql_logo_256x256.ico | Bin 270398 -> 0 bytes .../rde_internal_sql_logo_256x256.png | Bin 33206 -> 0 bytes .../rde_internal_sql_logo_256x256.svg | 176 -- .../LtsWidgets/resources/rde_komplett.png | Bin 51810 -> 0 bytes Lts/Common/LtsWidgets/resources/rde_logo.ico | Bin 106291 -> 0 bytes Lts/Common/LtsWidgets/resources/rde_logo.png | Bin 3649 -> 0 bytes .../resources/rde_splitter_left.svg | 1 - Lts/Common/LtsWidgets/resources/scripts.js | 112 - Lts/Common/LtsWidgets/resources/start.html | 600 ---- .../resources/stylesheet-branch-closed.png | Bin 361 -> 0 bytes .../resources/stylesheet-branch-open.png | Bin 345 -> 0 bytes Lts/Common/LtsWidgets/resources/success.svg | 66 - Lts/Common/LtsWidgets/resources/warning.svg | 72 - .../resources/warningrange-enter.wav | Bin 97136 -> 0 bytes .../resources/warningrange-leave.wav | Bin 97136 -> 0 bytes .../LtsWidgets/tests/lts_picture_editing.cpp | 221 -- .../LtsWidgets/tests/lts_picture_editing.h | 60 - .../LtsWidgets/tests/lts_picture_editing.ui | 363 --- .../tests/lts_picture_editing_main.cpp | 39 - Lts/Common/LtsWidgets/tests/main.cpp | 31 - Lts/Common/LtsWidgets/tests/window.cpp | 59 - Lts/Common/LtsWidgets/tests/window.h | 16 - Lts/Common/LtsWidgets/ui_requirement.cpp | 153 - Lts/Common/LtsWidgets/ui_requirement.h | 73 - Lts/Common/PluginInterfaces/CMakeLists.txt | 53 - .../PluginInterfaces/data_plugin_interface.h | 47 - .../PluginInterfaces/gui_plugin_interface.h | 99 - .../project_plugin_interface.h | 19 - .../PluginInterfaces/record_replay_plugin.h | 104 - .../PluginInterfaces/style_plugin_interface.h | 22 - .../PluginInterfaces/topic_replay_interface.h | 124 - Lts/Common/ProjectConfig/CMakeLists.txt | 11 - Lts/Common/ProjectConfig/project_defines.h | 73 - Lts/Common/TopicEditorBase/CMakeLists.txt | 48 - .../TopicEditorBase/topic_editor_base.cpp | 122 - .../TopicEditorBase/topic_editor_base.h | 124 - .../TopicEditorBase/topic_editor_interface.h | 28 - .../TopicEditorBase/topic_editor_utils.cpp | 318 --- .../TopicEditorBase/topic_editor_utils.h | 91 - .../TopicEditorBase/topic_preview_widget.cpp | 126 - .../TopicEditorBase/topic_preview_widget.h | 48 - .../TopicEditorBase/topic_preview_widget.ui | 109 - .../TopicEditorBase/topic_previewer.cpp | 83 - Lts/Common/TopicEditorBase/topic_previewer.h | 38 - Lts/Common/httq/AbstractServer.cpp | 91 - Lts/Common/httq/CMakeLists.txt | 32 - Lts/Common/httq/DataStream.cpp | 262 -- Lts/Common/httq/HttpRequest.cpp | 415 --- Lts/Common/httq/LICENSE_http-parser | 19 - Lts/Common/httq/LICENSE_httq | 21 - Lts/Common/httq/Logger.cpp | 71 - Lts/Common/httq/README.md | 6 - Lts/Common/httq/include/github-tag | 1 - Lts/Common/httq/include/http_parser.c | 2480 ----------------- Lts/Common/httq/include/http_parser.h | 313 --- Lts/Common/httq/include/httq/AbstractServer.h | 40 - Lts/Common/httq/include/httq/DataStream.h | 53 - Lts/Common/httq/include/httq/HttpRequest.h | 112 - Lts/Common/httq/include/httq/Logger.h | 51 - Lts/DDS/CMakeLists.txt | 24 - Lts/DDS/Core/CMakeLists.txt | 97 - Lts/DDS/Core/core.xml | 204 -- Lts/DDS/Core/core_export.h | 48 - Lts/DDS/Core/function_reply_object.cpp | 48 - Lts/DDS/Core/function_reply_object.h | 95 - Lts/DDS/Core/logger.cpp | 31 - Lts/DDS/Core/logger.h | 13 - Lts/DDS/Core/pacemaker.cpp | 117 - Lts/DDS/Core/pacemaker.h | 57 - Lts/DDS/Core/topic_lock_master.cpp | 47 - Lts/DDS/Core/topic_lock_master.h | 64 - Lts/DDS/DDSConnector/CMakeLists.txt | 36 - Lts/DDS/DDSConnector/dds_connector.cpp | 283 -- Lts/DDS/DDSConnector/dds_connector.h | 214 -- Lts/DDS/DDSConnector/pub_sub_base.cpp | 15 - Lts/DDS/DDSConnector/pub_sub_base.h | 39 - Lts/DDS/DDSConnector/pub_sub_base_p.h | 22 - Lts/DDS/DDSConnector/publisher.cpp | 106 - Lts/DDS/DDSConnector/publisher.h | 67 - Lts/DDS/DDSConnector/subscriber.cpp | 74 - Lts/DDS/DDSConnector/subscriber.h | 71 - Lts/DDS/DTC/CMakeLists.txt | 23 - Lts/DDS/DTC/generic_dtc.h | 66 - Lts/DDS/DTC/generic_dtc_impl.h | 313 --- Lts/DDS/DTS/CMakeLists.txt | 22 - Lts/DDS/DTS/generic_dts.h | 29 - Lts/DDS/DTS/generic_dts_impl.h | 88 - Lts/DDS/DdsUnitTests/CMakeLists.txt | 129 - Lts/DDS/DdsUnitTests/data_topic_tests.cpp | 155 -- Lts/DDS/DdsUnitTests/data_topic_tests.h | 66 - .../DdsUnitTests/data_topic_tests_main.cpp | 102 - Lts/DDS/DdsUnitTests/data_topic_tests_main.h | 38 - Lts/DDS/DdsUnitTests/ddsut.xml | 123 - Lts/DDS/DdsUnitTests/ddsut_export.h | 23 - Lts/DDS/DdsUnitTests/function_topic_tests.cpp | 257 -- Lts/DDS/DdsUnitTests/function_topic_tests.h | 73 - Lts/DDS/DdsUnitTests/generic_data_tests.cpp | 226 -- Lts/DDS/DdsUnitTests/generic_data_tests.h | 70 - Lts/DDS/DdsUnitTests/initialisation.cpp | 97 - Lts/DDS/DdsUnitTests/initialisation.h | 47 - .../DdsUnitTests/log4cxx_ddsUnitTests.props | 120 - .../DdsUnitTestsPlaceholder/CMakeLists.txt | 85 - .../ddsDataTopicTests_gtest.cpp | 24 - .../ddsDataTopicTests_smoke.cpp | 18 - .../ddsFunctionTopicTests_gtest.cpp | 20 - .../ddsFunctionTopicTests_smoke.cpp | 22 - .../ddsGenericDataTests_gtest.cpp | 55 - .../ddsGenericDataTests_smoke.cpp | 19 - .../DdsUnitTestsPlaceholder/mocks/MockDDS.cpp | 131 - .../DdsUnitTestsPlaceholder/mocks/MockDDS.h | 29 - Lts/DDS/FTC/CMakeLists.txt | 48 - Lts/DDS/FTC/abstract_requester.cpp | 6 - Lts/DDS/FTC/abstract_requester.h | 19 - Lts/DDS/FTC/generic_ftc.cpp | 5 - Lts/DDS/FTC/generic_ftc.h | 28 - Lts/DDS/FTC/generic_ftc_impl.h | 567 ---- Lts/DDS/FTC/generic_ftc_threading.cpp | 330 --- Lts/DDS/FTC/generic_ftc_threading.h | 91 - Lts/DDS/FTS/CMakeLists.txt | 45 - Lts/DDS/FTS/connext.h | 502 ---- Lts/DDS/FTS/generic_fts.h | 26 - Lts/DDS/FTS/generic_fts_impl.h | 287 -- Lts/DDS/FTS/generic_fts_internal.cpp | 48 - Lts/DDS/FTS/generic_fts_internal.h | 34 - Lts/DDS/Generic/CMakeLists.txt | 43 - Lts/DDS/Generic/dynamic_data_reader.h | 354 --- Lts/DDS/Generic/generic_base.cpp | 93 - Lts/DDS/Generic/generic_base.h | 57 - Lts/DDS/Generic/generic_data_reader.h | 382 --- Lts/DDS/Generic/generic_data_writer.h | 143 - Lts/DDS/Generic/generic_logger.cpp | 59 - Lts/DDS/Generic/generic_logger.h | 36 - Lts/DDS/Generic/subscriber_listener.cpp | 191 -- Lts/DDS/Generic/subscriber_listener.h | 140 - Lts/DDS/Generic/topic_description.h | 35 - Lts/DDS/Generic/topic_type_plugin.h | 37 - Lts/DDS/Generic/wait_set_handler.cpp | 147 - Lts/DDS/Generic/wait_set_handler.h | 65 - Lts/DDS/QosProfile/CMakeLists.txt | 35 - Lts/DDS/QosProfile/DDS_DefaultQos.cpp | 254 -- Lts/DDS/QosProfile/DDS_DefaultQos.h | 56 - Lts/DDS/QosProfile/default_profile.xml | 571 ---- Lts/DDS/QosProfile/default_profile_tablet.xml | 544 ---- Lts/DDS/README | 45 - Lts/DDS/Routing/CMakeLists.txt | 14 - Lts/DDS/Routing/routing_plugin.h | 363 --- Lts/Proxies/TrainingAreaBase/CMakeLists.txt | 271 -- TetraIF/CMakeLists.txt | 9 - TetraIF/CoPrtCnv/CMakeLists.txt | 38 - TetraIF/CoPrtCnv/CoPrtCnvCentralTelegram.cpp | 82 - TetraIF/CoPrtCnv/CoPrtCnvCentralTelegram.hpp | 60 - TetraIF/CoPrtCnv/CoPrtCnvHexDumpHelper.cpp | 240 -- TetraIF/CoPrtCnv/CoPrtCnvHexDumpHelper.hpp | 32 - TetraIF/CoPrtCnv/CoPrtCnvInjectionHeader.hpp | 52 - TetraIF/CoPrtCnv/CoPrtCnvLogHeader.hpp | 52 - TetraIF/CoPrtCnv/CoPrtCnvRadioTelegram.cpp | 1793 ------------ TetraIF/CoPrtCnv/CoPrtCnvRadioTelegram.hpp | 604 ---- .../CoPrtCnv/CoPrtCnvTelegramConverter.hpp | 57 - .../CoPrtCnvTelegramConverterNewIcd.cpp | 2131 -------------- TetraIF/CoPrtCnv/tetra_telegram_ids.h | 116 - TetraIF/LogView/CMakeLists.txt | 114 - TetraIF/LogView/Console/CMakeLists.txt | 17 - TetraIF/LogView/Console/main.cpp | 201 -- TetraIF/LogView/IntegerCalculator.cpp | 497 ---- TetraIF/LogView/IntegerCalculator.hpp | 143 - TetraIF/LogView/LogViewBooleanEdit.cpp | 36 - TetraIF/LogView/LogViewBooleanEdit.hpp | 19 - TetraIF/LogView/LogViewDateTimeEdit.cpp | 86 - TetraIF/LogView/LogViewDateTimeEdit.hpp | 40 - TetraIF/LogView/LogViewEnumEdit.cpp | 105 - TetraIF/LogView/LogViewEnumEdit.hpp | 43 - TetraIF/LogView/LogViewField.cpp | 275 -- TetraIF/LogView/LogViewField.hpp | 142 - TetraIF/LogView/LogViewGui.cpp | 1013 ------- TetraIF/LogView/LogViewGui.hpp | 189 -- TetraIF/LogView/LogViewInjectionThread.cpp | 143 - TetraIF/LogView/LogViewInjectionThread.hpp | 79 - TetraIF/LogView/LogViewMain.cpp | 57 - TetraIF/LogView/LogViewModels.cpp | 548 ---- TetraIF/LogView/LogViewModels.hpp | 109 - TetraIF/LogView/LogViewOptionDialog.cpp | 182 -- TetraIF/LogView/LogViewOptionDialog.hpp | 77 - TetraIF/LogView/LogViewResources.qrc | 18 - TetraIF/LogView/LogViewSpinBox.cpp | 59 - TetraIF/LogView/LogViewSpinBox.hpp | 45 - TetraIF/LogView/LogViewTabWidget.cpp | 139 - TetraIF/LogView/LogViewTabWidget.hpp | 49 - TetraIF/LogView/LogViewTableWidget.cpp | 311 --- TetraIF/LogView/LogViewTableWidget.hpp | 46 - TetraIF/LogView/LogViewTelegram.cpp | 1768 ------------ TetraIF/LogView/LogViewTelegram.hpp | 372 --- TetraIF/LogView/LogViewTelegramDialog.cpp | 497 ---- TetraIF/LogView/LogViewTelegramDialog.hpp | 77 - TetraIF/LogView/LogViewTelegramEdit.cpp | 586 ---- TetraIF/LogView/LogViewTelegramEdit.hpp | 121 - TetraIF/LogView/LogViewTelegramEditModel.cpp | 163 -- TetraIF/LogView/LogViewTelegramEditModel.hpp | 44 - .../LogViewTelegramSelectionDialog.cpp | 62 - .../LogViewTelegramSelectionDialog.hpp | 46 - .../LogView/LogViewTetraTelegramsParser.cpp | 516 ---- .../LogView/LogViewTetraTelegramsParser.hpp | 89 - TetraIF/LogView/LogViewTextEdit.cpp | 96 - TetraIF/LogView/LogViewTextEdit.hpp | 40 - TetraIF/LogView/LogViewValueEdit.cpp | 135 - TetraIF/LogView/LogViewValueEdit.hpp | 44 - TetraIF/LogView/LogViewWidget.cpp | 230 -- TetraIF/LogView/LogViewWidget.hpp | 84 - TetraIF/LogView/LogViewXdrElement.cpp | 61 - TetraIF/LogView/LogViewXdrElement.hpp | 62 - TetraIF/LogView/LogViewXdrParser.cpp | 1016 ------- TetraIF/LogView/LogViewXdrParser.hpp | 183 -- TetraIF/LogView/LogViewXdrStandardType.cpp | 84 - TetraIF/LogView/LogViewXdrStandardType.hpp | 117 - TetraIF/LogView/LogViewXdrStruct.cpp | 105 - TetraIF/LogView/LogViewXdrStruct.hpp | 60 - TetraIF/LogView/LogViewXdrType.cpp | 106 - TetraIF/LogView/LogViewXdrType.hpp | 72 - TetraIF/LogView/LogViewXdrValue.cpp | 40 - TetraIF/LogView/LogViewXdrValue.hpp | 34 - TetraIF/LogView/LogView_log4cxx.props | 17 - TetraIF/LogView/image/C2P.png | Bin 287 -> 0 bytes TetraIF/LogView/image/Central.png | Bin 191 -> 0 bytes TetraIF/LogView/image/GreenDarkLamp.png | Bin 586 -> 0 bytes TetraIF/LogView/image/GreenLamp.png | Bin 534 -> 0 bytes TetraIF/LogView/image/P2C.png | Bin 275 -> 0 bytes TetraIF/LogView/image/P2T.png | Bin 288 -> 0 bytes TetraIF/LogView/image/P2W.png | Bin 188 -> 0 bytes TetraIF/LogView/image/RedLamp.png | Bin 801 -> 0 bytes TetraIF/LogView/image/ShowSend.png | Bin 382 -> 0 bytes TetraIF/LogView/image/T2P.png | Bin 278 -> 0 bytes TetraIF/LogView/image/TNE.png | Bin 253 -> 0 bytes TetraIF/LogView/image/Tetra.png | Bin 307 -> 0 bytes TetraIF/LogView/image/W2P.png | Bin 186 -> 0 bytes TetraIF/LogView/image/Wls.png | Bin 191 -> 0 bytes TetraIF/LogView/image/YellowDarkLamp.png | Bin 586 -> 0 bytes TetraIF/LogView/image/YellowLamp.png | Bin 543 -> 0 bytes TetraIF/LogView/logger.cpp | 32 - TetraIF/LogView/logger.h | 17 - TetraIF/LogView/zsksxd_tetraForLogView.cpp | 1 - TetraIF/LogView/zsksxd_tetraForLogView.x | 1834 ------------ .../LogView/zsksxd_tetraForLogView_guez.cpp | 1 - TetraIF/LogView/zsksxd_tetraForLogView_guez.x | 1839 ------------ TetraIF/PrtCnv/AppComponent.cpp | 88 - TetraIF/PrtCnv/AppComponent.hpp | 45 - TetraIF/PrtCnv/CMakeLists.txt | 78 - TetraIF/PrtCnv/IAppComponent.hpp | 59 - TetraIF/PrtCnv/LastTest/CMakeLists.txt | 21 - TetraIF/PrtCnv/LastTest/main.cpp | 62 - TetraIF/PrtCnv/LastTest/tetra_if.cpp | 503 ---- TetraIF/PrtCnv/LastTest/tetra_if.h | 61 - .../PrtCnv/LastTestAuswertung/CMakeLists.txt | 25 - .../LastTestAuswertung/LastTestAuswertung.pro | 16 - TetraIF/PrtCnv/LastTestAuswertung/main.cpp | 10 - .../PrtCnv/LastTestAuswertung/mainwindow.cpp | 428 --- .../PrtCnv/LastTestAuswertung/mainwindow.h | 69 - .../PrtCnv/LastTestAuswertung/mainwindow.ui | 123 - TetraIF/PrtCnv/PrtCnv.dat.tts | 90 - TetraIF/PrtCnv/PrtCnvApplication.cpp | 410 --- TetraIF/PrtCnv/PrtCnvApplication.hpp | 180 -- TetraIF/PrtCnv/PrtCnvBroadcastMgr.cpp | 1709 ------------ TetraIF/PrtCnv/PrtCnvBroadcastMgr.hpp | 359 --- TetraIF/PrtCnv/PrtCnvCentralThread.cpp | 842 ------ TetraIF/PrtCnv/PrtCnvCentralThread.hpp | 238 -- TetraIF/PrtCnv/PrtCnvDbThread.cpp | 1647 ----------- TetraIF/PrtCnv/PrtCnvDbThread.hpp | 371 --- TetraIF/PrtCnv/PrtCnvInjectionSocket.cpp | 127 - TetraIF/PrtCnv/PrtCnvInjectionSocket.hpp | 66 - TetraIF/PrtCnv/PrtCnvInjectionThread.cpp | 182 -- TetraIF/PrtCnv/PrtCnvInjectionThread.hpp | 101 - TetraIF/PrtCnv/PrtCnvMain.cpp | 124 - TetraIF/PrtCnv/PrtCnvMember.cpp | 98 - TetraIF/PrtCnv/PrtCnvMember.hpp | 84 - TetraIF/PrtCnv/PrtCnvRadioThread.cpp | 2267 --------------- TetraIF/PrtCnv/PrtCnvRadioThread.hpp | 419 --- TetraIF/PrtCnv/PrtCnvRadioTrafficThread.cpp | 208 -- TetraIF/PrtCnv/PrtCnvRadioTrafficThread.hpp | 82 - TetraIF/PrtCnv/PrtCnvSocketThread.cpp | 382 --- TetraIF/PrtCnv/PrtCnvSocketThread.hpp | 190 -- .../PrtCnv/PrtCnvStoredCentralTelegram.hpp | 39 - TetraIF/PrtCnv/PrtCnvStoredRadioTelegram.hpp | 32 - TetraIF/PrtCnv/PrtCnvStoredTelegram.hpp | 23 - TetraIF/PrtCnv/PrtCnvTelegramLogThread.cpp | 182 -- TetraIF/PrtCnv/PrtCnvTelegramLogThread.hpp | 87 - TetraIF/PrtCnv/PrtCnvThread.cpp | 74 - TetraIF/PrtCnv/PrtCnvThread.hpp | 71 - TetraIF/PrtCnv/PrtCnvThreadStarter.cpp | 125 - TetraIF/PrtCnv/PrtCnvThreadStarter.hpp | 68 - TetraIF/PrtCnv/TetraTestServer/CMakeLists.txt | 29 - .../log4cxx_tetratestserver.props | 81 - TetraIF/PrtCnv/TetraTestServer/main.cpp | 188 -- TetraIF/PrtCnv/TetraTestServer/server.cpp | 38 - TetraIF/PrtCnv/TetraTestServer/server.h | 54 - TetraIF/PrtCnv/TetraTestServer/tts.txt | 111 - TetraIF/PrtCnv/TetraTestServer/worker.cpp | 645 ----- TetraIF/PrtCnv/TetraTestServer/worker.h | 128 - TetraIF/PrtCnv/log4cxx_prtcnv.props | 49 - TetraIF/PrtCnv/logger.cpp | 30 - TetraIF/PrtCnv/logger.h | 18 - Z/src/CMakeLists.txt | 17 - Z/src/NGLLogger/CMakeLists.txt | 58 - Z/src/NGLLogger/INGLLogger.h | 306 -- Z/src/NGLLogger/MurmurHash2.cpp | 77 - Z/src/NGLLogger/MurmurHash2.h | 9 - Z/src/NGLLogger/NGLLogger.cpp | 446 --- Z/src/NGLLogger/NGLLogger.h | 94 - Z/src/NGLLogger/NGLLoggerBootstrap.cpp | 30 - Z/src/NGLLogger/NGLLoggerBootstrap.h | 59 - Z/src/NGLLogger/NGLLogger_export.h | 22 - Z/src/NGLLogger/log4cxx.props | 42 - Z/src/NGLLogger/test_ngllogger.cpp | 34 - Z/src/QtSingleApplication/CMakeLists.txt | 50 - Z/src/QtSingleApplication/QtLockedFile | 1 - Z/src/QtSingleApplication/QtSingleApplication | 1 - .../QtSingleCoreApplication | 1 - Z/src/QtSingleApplication/qtlocalpeer.cpp | 170 -- Z/src/QtSingleApplication/qtlocalpeer.h | 40 - Z/src/QtSingleApplication/qtlockedfile.cpp | 68 - Z/src/QtSingleApplication/qtlockedfile.h | 60 - .../QtSingleApplication/qtlockedfile_unix.cpp | 79 - .../QtSingleApplication/qtlockedfile_win.cpp | 174 -- .../qtsingleapplication.cpp | 104 - .../QtSingleApplication/qtsingleapplication.h | 56 - .../qtsinglecoreapplication.cpp | 60 - .../qtsinglecoreapplication.h | 46 - Z/src/WMS_Interface/CMakeLists.txt | 35 - Z/src/WMS_Interface/IECC2WMS.h | 20 - Z/src/WMS_Interface/IWMS2ECC.h | 137 - Z/src/WMS_Interface/json_keys.h | 52 - .../wms_ecc_equipRequirements.cpp | 71 - .../WMS_Interface/wms_ecc_equipRequirements.h | 30 - .../WMS_Interface/wms_ecc_equipmenttypes.cpp | 132 - Z/src/WMS_Interface/wms_ecc_equipmenttypes.h | 37 - Z/src/WMS_Interface/wms_ecc_exercises.cpp | 135 - Z/src/WMS_Interface/wms_ecc_exercises.h | 41 - Z/src/WMS_Interface/wms_ecc_ibase.h | 15 - Z/src/WMS_Interface/wms_ecc_playerInfo.cpp | 176 -- Z/src/WMS_Interface/wms_ecc_playerInfo.h | 40 - Z/src/WMS_Interface/wms_ecc_registration.cpp | 89 - Z/src/WMS_Interface/wms_ecc_registration.h | 55 - Z/src/WMS_Interface/wms_ecc_unregister.cpp | 50 - Z/src/WMS_Interface/wms_ecc_unregister.h | 32 - Z/src/nglapplication/CMakeLists.txt | 134 - Z/src/nglapplication/ngl_qt_msg_handler.inc | 64 - Z/src/nglapplication/ngl_signal_handler.inc | 41 - Z/src/nglapplication/nglapplication.cpp | 31 - Z/src/nglapplication/nglapplication.h | 36 - Z/src/nglapplication/nglcoreapplication.cpp | 17 - Z/src/nglapplication/nglcoreapplication.h | 31 - Z/src/nglapplication/nglguiapplication.cpp | 17 - Z/src/nglapplication/nglguiapplication.h | 33 - Z/src/nglapplication/nglloggerhandler.cpp | 91 - Z/src/nglapplication/nglloggerhandler.h | 67 - Z/src/nglapplication/nglsingleapplication.cpp | 32 - Z/src/nglapplication/nglsingleapplication.h | 36 - .../nglsinglecoreapplication.cpp | 17 - .../nglapplication/nglsinglecoreapplication.h | 31 - Z/src/nglcompression/CMakeLists.txt | 47 - .../nglcompression/nglcomp_bzip2iodevice.cpp | 360 --- Z/src/nglcompression/nglcomp_bzip2iodevice.h | 42 - Z/src/nglcompression/nglcomp_compress.cpp | 570 ---- Z/src/nglcompression/nglcomp_compress.h | 140 - Z/src/nglcompression/nglcomp_export.h | 17 - Z/src/nglcompression/nglcomp_tarfilter.cpp | 522 ---- Z/src/nglcompression/nglcomp_tarfilter.h | 80 - Z/src/nglcompression/nglcomp_test.cpp | 175 -- Z/src/nglcompression/nglcomp_test.h | 36 - Z/src/ngldb/CMakeLists.txt | 45 - Z/src/ngldb/db_service_handler.h | 58 - Z/src/ngldb/dbcommon.h | 41 - Z/src/ngldb/dbconnection.cpp | 68 - Z/src/ngldb/dbconnection.h | 24 - Z/src/ngldb/dbtransaction.cpp | 82 - Z/src/ngldb/dbtransaction.h | 48 - Z/src/ngldb/ngldb_export.h | 17 - Z/src/ngldb/ngldb_logger.cpp | 24 - Z/src/ngldb/ngldb_logger.h | 31 - Z/src/ngldb/nsqlquery.cpp | 471 ---- Z/src/ngldb/nsqlquery.h | 222 -- Z/src/ngldb/sql_query_janitor.cpp | 24 - Z/src/ngldb/sql_query_janitor.h | 22 - Z/src/nglsettings/CMakeLists.txt | 32 - Z/src/nglsettings/nglsettings_connections.cpp | 175 -- Z/src/nglsettings/nglsettings_connections.h | 59 - Z/src/nglsettings/nglsettings_export.h | 17 - Z/src/nglsettings/nglsettings_settings.cpp | 320 --- Z/src/nglsettings/nglsettings_settings.h | 142 - Z/src/ngltools/CMakeLists.txt | 61 - Z/src/ngltools/ngltools_bitset.h | 100 - Z/src/ngltools/ngltools_convert.cpp | 91 - Z/src/ngltools/ngltools_convert.h | 23 - Z/src/ngltools/ngltools_dtg.cpp | 373 --- Z/src/ngltools/ngltools_dtg.h | 87 - Z/src/ngltools/ngltools_export.h | 19 - Z/src/ngltools/ngltools_filesystem.cpp | 290 -- Z/src/ngltools/ngltools_filesystem.h | 38 - Z/src/ngltools/ngltools_helpevent.cpp | 19 - Z/src/ngltools/ngltools_helpevent.h | 34 - Z/src/ngltools/tests/NGLToolsQtSmokeTest.cpp | 24 - Z/src/ngltranslator/CMakeLists.txt | 22 - Z/src/ngltranslator/ngltranslator.cpp | 229 -- Z/src/ngltranslator/ngltranslator.h | 40 - Z/src/ngltranslator/ngltranslator_export.h | 19 - Z/src/ngltranslator/ngltranslator_p.h | 34 - Z/src/nglwidgets/CMakeLists.txt | 69 - Z/src/nglwidgets/icons/16x16/LICENSE | 24 - .../icons/16x16/application-pdf.png | Bin 688 -> 0 bytes Z/src/nglwidgets/icons/16x16/arrow-left.png | Bin 512 -> 0 bytes Z/src/nglwidgets/icons/16x16/arrow-right.png | Bin 527 -> 0 bytes .../icons/16x16/audio-x-generic.png | Bin 643 -> 0 bytes Z/src/nglwidgets/icons/16x16/bg.gif | Bin 958 -> 0 bytes Z/src/nglwidgets/icons/16x16/camera-photo.png | Bin 372 -> 0 bytes Z/src/nglwidgets/icons/16x16/configure.png | Bin 717 -> 0 bytes Z/src/nglwidgets/icons/16x16/dialog-close.png | Bin 813 -> 0 bytes .../icons/16x16/dialog-ok-apply.png | Bin 597 -> 0 bytes Z/src/nglwidgets/icons/16x16/dialog-ok.png | Bin 601 -> 0 bytes Z/src/nglwidgets/icons/16x16/dirs.png | Bin 1049 -> 0 bytes .../nglwidgets/icons/16x16/document-edit.png | Bin 726 -> 0 bytes .../icons/16x16/document-import.png | Bin 609 -> 0 bytes Z/src/nglwidgets/icons/16x16/document-new.png | Bin 579 -> 0 bytes .../icons/16x16/document-open-data.png | Bin 611 -> 0 bytes .../nglwidgets/icons/16x16/document-open.png | Bin 531 -> 0 bytes .../icons/16x16/document-preview.png | Bin 808 -> 0 bytes .../nglwidgets/icons/16x16/document-print.png | Bin 619 -> 0 bytes .../icons/16x16/document-properties.png | Bin 668 -> 0 bytes .../icons/16x16/document-save-all.png | Bin 635 -> 0 bytes .../icons/16x16/document-save-as.png | Bin 839 -> 0 bytes .../nglwidgets/icons/16x16/document-save.png | Bin 563 -> 0 bytes Z/src/nglwidgets/icons/16x16/download.png | Bin 901 -> 0 bytes .../icons/16x16/draw-bezier-curves.png | Bin 540 -> 0 bytes Z/src/nglwidgets/icons/16x16/draw-ellipse.png | Bin 587 -> 0 bytes Z/src/nglwidgets/icons/16x16/draw-line.png | Bin 352 -> 0 bytes Z/src/nglwidgets/icons/16x16/draw-polygon.png | Bin 679 -> 0 bytes .../nglwidgets/icons/16x16/draw-polyline.png | Bin 529 -> 0 bytes .../nglwidgets/icons/16x16/draw-rectangle.png | Bin 360 -> 0 bytes Z/src/nglwidgets/icons/16x16/draw-text.png | Bin 465 -> 0 bytes .../16x16/edit-clear-locationbar-rtl.png | Bin 644 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-copy.png | Bin 485 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-cut.png | Bin 368 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-delete.png | Bin 641 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-paste.png | Bin 529 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-redo.png | Bin 813 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-rename.png | Bin 245 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-select.png | Bin 368 -> 0 bytes Z/src/nglwidgets/icons/16x16/edit-undo.png | Bin 866 -> 0 bytes Z/src/nglwidgets/icons/16x16/fill-color.png | Bin 985 -> 0 bytes .../icons/16x16/format-justify-center.png | Bin 272 -> 0 bytes .../icons/16x16/format-justify-fill.png | Bin 230 -> 0 bytes .../icons/16x16/format-justify-left.png | Bin 264 -> 0 bytes .../icons/16x16/format-justify-right.png | Bin 266 -> 0 bytes .../icons/16x16/format-stroke-color.png | Bin 822 -> 0 bytes .../icons/16x16/format-text-bold.png | Bin 463 -> 0 bytes .../icons/16x16/format-text-italic.png | Bin 332 -> 0 bytes .../icons/16x16/format-text-underline.png | Bin 392 -> 0 bytes Z/src/nglwidgets/icons/16x16/freu.gif | Bin 375 -> 0 bytes Z/src/nglwidgets/icons/16x16/go-down.png | Bin 623 -> 0 bytes Z/src/nglwidgets/icons/16x16/go-home.png | Bin 935 -> 0 bytes Z/src/nglwidgets/icons/16x16/go-next.png | Bin 721 -> 0 bytes Z/src/nglwidgets/icons/16x16/go-previous.png | Bin 634 -> 0 bytes Z/src/nglwidgets/icons/16x16/go-up.png | Bin 672 -> 0 bytes Z/src/nglwidgets/icons/16x16/help-about.png | Bin 734 -> 0 bytes .../nglwidgets/icons/16x16/help-contents.png | Bin 684 -> 0 bytes .../icons/16x16/image-x-generic.png | Bin 680 -> 0 bytes .../nglwidgets/icons/16x16/internet-mail.png | Bin 755 -> 0 bytes Z/src/nglwidgets/icons/16x16/led-blue-on.png | Bin 763 -> 0 bytes Z/src/nglwidgets/icons/16x16/led-gray-on.png | Bin 695 -> 0 bytes Z/src/nglwidgets/icons/16x16/led-green-on.png | Bin 815 -> 0 bytes Z/src/nglwidgets/icons/16x16/led-red-on.png | Bin 737 -> 0 bytes .../nglwidgets/icons/16x16/led-yellow-on.png | Bin 934 -> 0 bytes .../nglwidgets/icons/16x16/list-add-user.png | Bin 690 -> 0 bytes Z/src/nglwidgets/icons/16x16/list-add.png | Bin 564 -> 0 bytes .../icons/16x16/list-remove-user.png | Bin 707 -> 0 bytes Z/src/nglwidgets/icons/16x16/list-remove.png | Bin 332 -> 0 bytes .../nglwidgets/icons/16x16/mail-mark-read.png | Bin 680 -> 0 bytes .../icons/16x16/mail-mark-unread.png | Bin 472 -> 0 bytes .../icons/16x16/mail-message-new.png | Bin 967 -> 0 bytes Z/src/nglwidgets/icons/16x16/mail-send.png | Bin 1179 -> 0 bytes Z/src/nglwidgets/icons/16x16/media-eject.png | Bin 502 -> 0 bytes Z/src/nglwidgets/icons/16x16/media-flash.png | Bin 740 -> 0 bytes .../icons/16x16/media-play-faster.png | Bin 909 -> 0 bytes .../icons/16x16/media-play-normal.png | Bin 918 -> 0 bytes .../icons/16x16/media-play-slower.png | Bin 962 -> 0 bytes .../icons/16x16/media-playback-pause.png | Bin 484 -> 0 bytes .../icons/16x16/media-playback-start.png | Bin 501 -> 0 bytes .../icons/16x16/media-playback-stop.png | Bin 499 -> 0 bytes Z/src/nglwidgets/icons/16x16/media-record.png | Bin 735 -> 0 bytes .../icons/16x16/media-seek-backward.png | Bin 505 -> 0 bytes .../icons/16x16/media-seek-forward.png | Bin 506 -> 0 bytes .../icons/16x16/media-skip-backward.png | Bin 511 -> 0 bytes .../icons/16x16/media-skip-forward.png | Bin 506 -> 0 bytes Z/src/nglwidgets/icons/16x16/oO.gif | Bin 1623 -> 0 bytes Z/src/nglwidgets/icons/16x16/object-group.png | Bin 320 -> 0 bytes .../nglwidgets/icons/16x16/object-ungroup.png | Bin 348 -> 0 bytes Z/src/nglwidgets/icons/16x16/ooo-calc.png | Bin 986 -> 0 bytes Z/src/nglwidgets/icons/16x16/ooo-impress.png | Bin 651 -> 0 bytes Z/src/nglwidgets/icons/16x16/ooo-writer.png | Bin 796 -> 0 bytes .../nglwidgets/icons/16x16/player_status.png | Bin 196 -> 0 bytes .../icons/16x16/preferences-desktop-font.png | Bin 708 -> 0 bytes .../16x16/preferences-web-browser-cache.png | Bin 835 -> 0 bytes Z/src/nglwidgets/icons/16x16/printer.png | Bin 630 -> 0 bytes Z/src/nglwidgets/icons/16x16/process-stop.png | Bin 722 -> 0 bytes Z/src/nglwidgets/icons/16x16/rde.ico | Bin 30166 -> 0 bytes Z/src/nglwidgets/icons/16x16/rde_16x16.png | Bin 430 -> 0 bytes Z/src/nglwidgets/icons/16x16/system-help.png | Bin 897 -> 0 bytes Z/src/nglwidgets/icons/16x16/text-plain.png | Bin 609 -> 0 bytes .../nglwidgets/icons/16x16/transform-move.png | Bin 875 -> 0 bytes .../icons/16x16/transform-rotate.png | Bin 558 -> 0 bytes .../icons/16x16/transform-scale.png | Bin 509 -> 0 bytes Z/src/nglwidgets/icons/16x16/traurig.gif | Bin 1591 -> 0 bytes .../icons/16x16/video-x-generic.png | Bin 721 -> 0 bytes .../icons/16x16/view-fullscreen.png | Bin 624 -> 0 bytes .../icons/16x16/view-statistics.png | Bin 495 -> 0 bytes Z/src/nglwidgets/icons/16x16/weinend.gif | Bin 202 -> 0 bytes .../icons/16x16/x-office-address-book.png | Bin 561 -> 0 bytes .../nglwidgets/icons/16x16/yellow-off-16.png | Bin 779 -> 0 bytes Z/src/nglwidgets/icons/16x16/yellow-on-16.png | Bin 937 -> 0 bytes Z/src/nglwidgets/icons/16x16/zoom-in.png | Bin 791 -> 0 bytes Z/src/nglwidgets/icons/16x16/zoom-out.png | Bin 786 -> 0 bytes Z/src/nglwidgets/icons/16x16/zunge.gif | Bin 377 -> 0 bytes Z/src/nglwidgets/icons/16x16/zwinker.gif | Bin 170 -> 0 bytes .../icons/22x22/dialogbuttonbox.png | Bin 1003 -> 0 bytes Z/src/nglwidgets/icons/22x22/edit-redo.png | Bin 1245 -> 0 bytes .../icons/22x22/format-justify-center.png | Bin 313 -> 0 bytes .../icons/22x22/format-justify-fill.png | Bin 261 -> 0 bytes .../icons/22x22/format-justify-left.png | Bin 314 -> 0 bytes .../icons/22x22/format-justify-right.png | Bin 313 -> 0 bytes .../icons/22x22/format-stroke-color.png | Bin 1230 -> 0 bytes .../icons/22x22/format-text-bold.png | Bin 667 -> 0 bytes .../icons/22x22/format-text-italic.png | Bin 630 -> 0 bytes .../icons/22x22/format-text-underline.png | Bin 537 -> 0 bytes Z/src/nglwidgets/icons/22x22/kdialog.png | Bin 1045 -> 0 bytes Z/src/nglwidgets/icons/22x22/klineedit.png | Bin 1163 -> 0 bytes Z/src/nglwidgets/icons/22x22/layer.png | Bin 570 -> 0 bytes Z/src/nglwidgets/icons/22x22/lkm_einsatz.png | Bin 165 -> 0 bytes Z/src/nglwidgets/icons/22x22/pushbutton.png | Bin 408 -> 0 bytes Z/src/nglwidgets/icons/22x22/scricon.png | Bin 291 -> 0 bytes Z/src/nglwidgets/icons/32x32/database.png | Bin 1484 -> 0 bytes .../icons/32x32/hand_grenade_locations.png | Bin 949 -> 0 bytes Z/src/nglwidgets/icons/32x32/hideshow.png | Bin 3187 -> 0 bytes Z/src/nglwidgets/icons/32x32/marker-end.png | Bin 508 -> 0 bytes Z/src/nglwidgets/icons/32x32/marker-start.png | Bin 446 -> 0 bytes Z/src/nglwidgets/icons/32x32/media-eject.png | Bin 1197 -> 0 bytes .../icons/32x32/media-play-backward.png | Bin 1138 -> 0 bytes .../icons/32x32/media-play-faster.png | Bin 2335 -> 0 bytes .../icons/32x32/media-play-faster.svg | 0 .../icons/32x32/media-play-normal.png | Bin 2267 -> 0 bytes .../icons/32x32/media-play-normal.svg | 0 .../icons/32x32/media-play-slower.png | Bin 2298 -> 0 bytes .../icons/32x32/media-play-slower.svg | 0 .../icons/32x32/media-play-slower.svg.png | Bin 2414 -> 0 bytes .../icons/32x32/media-playback-pause.png | Bin 1145 -> 0 bytes .../32x32/media-playback-start-single.png | Bin 1205 -> 0 bytes .../icons/32x32/media-playback-start.png | Bin 1177 -> 0 bytes .../icons/32x32/media-playback-stop.png | Bin 1165 -> 0 bytes Z/src/nglwidgets/icons/32x32/media-record.png | Bin 1843 -> 0 bytes .../icons/32x32/media-seek-backward.png | Bin 1207 -> 0 bytes .../icons/32x32/media-seek-forward.png | Bin 1220 -> 0 bytes .../icons/32x32/media-skip-backward.png | Bin 1211 -> 0 bytes .../icons/32x32/media-skip-forward.png | Bin 1209 -> 0 bytes .../icons/32x32/media-vcr-nopause.png | Bin 251 -> 0 bytes .../icons/32x32/media-vcr-normal.png | Bin 503 -> 0 bytes .../icons/32x32/media-vcr-repeat.png | Bin 815 -> 0 bytes .../icons/32x32/media-vcr-withpause.png | Bin 388 -> 0 bytes .../icons/32x32/service_message.png | Bin 3209 -> 0 bytes Z/src/nglwidgets/icons/32x32/showhide.png | Bin 3079 -> 0 bytes Z/src/nglwidgets/icons/32x32/wireless.png | Bin 3651 -> 0 bytes Z/src/nglwidgets/icons/LICENSE | 24 - Z/src/nglwidgets/icons/png/add.png | Bin 1312 -> 0 bytes Z/src/nglwidgets/icons/png/add_disabled.png | Bin 1173 -> 0 bytes .../icons/png/add_tactical_sign_act.png | Bin 1906 -> 0 bytes .../png/add_tactical_sign_act_disabled.png | Bin 1514 -> 0 bytes Z/src/nglwidgets/icons/png/add_text_act.png | Bin 1385 -> 0 bytes .../icons/png/add_text_act_disabled.png | Bin 1270 -> 0 bytes Z/src/nglwidgets/icons/png/address_book.png | Bin 575 -> 0 bytes .../nglwidgets/icons/png/big_letters_act.png | Bin 1610 -> 0 bytes .../icons/png/big_letters_act_disabled.png | Bin 1363 -> 0 bytes .../icons/png/camera_arrow_down.png | Bin 771 -> 0 bytes .../icons/png/camera_arrow_left.png | Bin 791 -> 0 bytes .../icons/png/camera_arrow_right.png | Bin 786 -> 0 bytes .../nglwidgets/icons/png/camera_arrow_up.png | Bin 756 -> 0 bytes .../icons/png/center_object_in_map_act.png | Bin 1588 -> 0 bytes .../png/center_object_in_map_act_disabled.png | Bin 1412 -> 0 bytes Z/src/nglwidgets/icons/png/clear_plan_act.png | Bin 2189 -> 0 bytes .../icons/png/clear_plan_act_disabled.png | Bin 1573 -> 0 bytes Z/src/nglwidgets/icons/png/copy_exercise.png | Bin 1589 -> 0 bytes .../icons/png/copy_exercise_disabled.png | Bin 1224 -> 0 bytes Z/src/nglwidgets/icons/png/delete.png | Bin 2507 -> 0 bytes .../nglwidgets/icons/png/delete_disabled.png | Bin 1539 -> 0 bytes .../nglwidgets/icons/png/delete_exercise.png | Bin 2115 -> 0 bytes .../icons/png/delete_exercise_disabled.png | Bin 1568 -> 0 bytes .../icons/png/delete_graphic_eass.png | Bin 1594 -> 0 bytes .../png/delete_graphic_eass_disabled.png | Bin 1359 -> 0 bytes Z/src/nglwidgets/icons/png/deleteall.png | Bin 935 -> 0 bytes Z/src/nglwidgets/icons/png/download.png | Bin 637 -> 0 bytes .../nglwidgets/icons/png/draw_ellipse_act.png | Bin 2040 -> 0 bytes .../icons/png/draw_ellipse_act_disabled.png | Bin 1766 -> 0 bytes Z/src/nglwidgets/icons/png/draw_line.png | Bin 560 -> 0 bytes .../icons/png/draw_line_disabled.png | Bin 591 -> 0 bytes .../nglwidgets/icons/png/draw_polygon_act.png | Bin 1805 -> 0 bytes .../icons/png/draw_polygon_act_disabled.png | Bin 1589 -> 0 bytes .../icons/png/draw_rectangle_act.png | Bin 1723 -> 0 bytes .../icons/png/draw_rectangle_act_disabled.png | Bin 1362 -> 0 bytes Z/src/nglwidgets/icons/png/draw_spline.png | Bin 1749 -> 0 bytes .../icons/png/draw_spline_disabled.png | Bin 1378 -> 0 bytes Z/src/nglwidgets/icons/png/eass_line.png | Bin 1553 -> 0 bytes .../icons/png/eass_line_disabled.png | Bin 1616 -> 0 bytes Z/src/nglwidgets/icons/png/eass_polygon.png | Bin 1707 -> 0 bytes .../icons/png/eass_polygon_disabled.png | Bin 1681 -> 0 bytes .../nglwidgets/icons/png/edit_element_act.png | Bin 1676 -> 0 bytes .../icons/png/edit_element_act_disabled.png | Bin 1499 -> 0 bytes .../icons/png/edith_graphic_eass.png | Bin 1549 -> 0 bytes .../icons/png/edith_graphic_eass_disabled.png | Bin 1237 -> 0 bytes Z/src/nglwidgets/icons/png/fast_playback.png | Bin 1826 -> 0 bytes .../icons/png/fast_playback_backward.png | Bin 1102 -> 0 bytes .../png/fast_playback_backward_disabled.png | Bin 602 -> 0 bytes .../icons/png/fast_playback_disabled.png | Bin 1340 -> 0 bytes .../nglwidgets/icons/png/fill_colour_act.png | Bin 1303 -> 0 bytes .../icons/png/fill_colour_act_disabled.png | Bin 1139 -> 0 bytes Z/src/nglwidgets/icons/png/folder_open.png | Bin 681 -> 0 bytes .../icons/png/kosy_audio_control_act.png | Bin 1958 -> 0 bytes .../png/kosy_audio_control_act_disabled.png | Bin 1756 -> 0 bytes .../icons/png/kosy_radio_error_act.png | Bin 2473 -> 0 bytes .../png/kosy_radio_error_act_disabled.png | Bin 2160 -> 0 bytes .../icons/png/kosy_remote_control_act.png | Bin 2730 -> 0 bytes .../png/kosy_remote_control_act_disabled.png | Bin 1805 -> 0 bytes Z/src/nglwidgets/icons/png/line_color.png | Bin 822 -> 0 bytes Z/src/nglwidgets/icons/png/marker_left.png | Bin 1911 -> 0 bytes .../icons/png/marker_left_disabled.png | Bin 1317 -> 0 bytes Z/src/nglwidgets/icons/png/marker_right.png | Bin 1926 -> 0 bytes .../icons/png/marker_right_disabled.png | Bin 1329 -> 0 bytes Z/src/nglwidgets/icons/png/messages_new.png | Bin 1061 -> 0 bytes Z/src/nglwidgets/icons/png/missionmarch.png | Bin 1431 -> 0 bytes .../icons/png/missionmarch_disabled.png | Bin 1206 -> 0 bytes .../icons/png/normal_letters_act.png | Bin 1485 -> 0 bytes .../icons/png/normal_letters_act_disabled.png | Bin 1274 -> 0 bytes Z/src/nglwidgets/icons/png/ok.png | Bin 1825 -> 0 bytes Z/src/nglwidgets/icons/png/ok_disabled.png | Bin 1448 -> 0 bytes Z/src/nglwidgets/icons/png/paste_exercise.png | Bin 1793 -> 0 bytes .../icons/png/paste_exercise_disabled.png | Bin 1305 -> 0 bytes Z/src/nglwidgets/icons/png/pause_playback.png | Bin 1187 -> 0 bytes .../icons/png/pause_playback_disabled.png | Bin 1090 -> 0 bytes Z/src/nglwidgets/icons/png/polygonmap.png | Bin 1527 -> 0 bytes .../icons/png/polygonmap_disabled.png | Bin 1532 -> 0 bytes Z/src/nglwidgets/icons/png/position_map.png | Bin 2361 -> 0 bytes .../icons/png/position_map_disabled.png | Bin 1681 -> 0 bytes Z/src/nglwidgets/icons/png/preview.png | Bin 1201 -> 0 bytes Z/src/nglwidgets/icons/png/printer.png | Bin 667 -> 0 bytes Z/src/nglwidgets/icons/png/redo.png | Bin 2040 -> 0 bytes Z/src/nglwidgets/icons/png/redo_disabled.png | Bin 1537 -> 0 bytes Z/src/nglwidgets/icons/png/remove.png | Bin 1134 -> 0 bytes .../nglwidgets/icons/png/remove_disabled.png | Bin 1080 -> 0 bytes .../nglwidgets/icons/png/rename_exercise.png | Bin 1921 -> 0 bytes .../icons/png/rename_exercise_disabled.png | Bin 1539 -> 0 bytes Z/src/nglwidgets/icons/png/save.png | Bin 595 -> 0 bytes Z/src/nglwidgets/icons/png/save_as.png | Bin 938 -> 0 bytes Z/src/nglwidgets/icons/png/settings.png | Bin 2200 -> 0 bytes .../icons/png/settings_disabled.png | Bin 1597 -> 0 bytes Z/src/nglwidgets/icons/png/start_playback.png | Bin 1558 -> 0 bytes .../icons/png/start_playback_disabled.png | Bin 1287 -> 0 bytes Z/src/nglwidgets/icons/png/stop.png | Bin 1720 -> 0 bytes Z/src/nglwidgets/icons/png/stop_disabled.png | Bin 1702 -> 0 bytes Z/src/nglwidgets/icons/png/stop_playback.png | Bin 1135 -> 0 bytes .../icons/png/stop_playback_disabled.png | Bin 1066 -> 0 bytes Z/src/nglwidgets/icons/png/thick_line_act.png | Bin 1079 -> 0 bytes .../icons/png/thick_line_act_disabled.png | Bin 1059 -> 0 bytes Z/src/nglwidgets/icons/png/turn_left.png | Bin 2057 -> 0 bytes .../icons/png/turn_left_disabled.png | Bin 1585 -> 0 bytes Z/src/nglwidgets/icons/png/undo_act.png | Bin 2034 -> 0 bytes .../icons/png/undo_act_disabled.png | Bin 1536 -> 0 bytes Z/src/nglwidgets/icons/png/zoom_in.png | Bin 1330 -> 0 bytes .../nglwidgets/icons/png/zoom_in_disabled.png | Bin 1268 -> 0 bytes Z/src/nglwidgets/icons/png/zoom_out.png | Bin 1495 -> 0 bytes .../icons/png/zoom_out_disabled.png | Bin 1275 -> 0 bytes Z/src/nglwidgets/icons/svg/3d_viewer.svg | 407 --- .../nglwidgets/icons/svg/3d_viewer_config.svg | 497 ---- .../icons/svg/3d_viewer_config_disabled.svg | 169 -- .../icons/svg/3d_viewer_disabled.svg | 78 - Z/src/nglwidgets/icons/svg/about.svg | 428 --- Z/src/nglwidgets/icons/svg/about_disabled.svg | 100 - .../icons/svg/ac_adapter_plugged_in.svg | 398 --- Z/src/nglwidgets/icons/svg/access_rights.svg | 423 --- .../icons/svg/access_rights_disabled.svg | 95 - Z/src/nglwidgets/icons/svg/aircraft_sim.svg | 399 --- Z/src/nglwidgets/icons/svg/announcement.svg | 428 --- .../icons/svg/announcement_disabled.svg | 101 - .../icons/svg/announcement_menu.svg | 419 --- .../icons/svg/announcement_menu_disabled.svg | 91 - .../nglwidgets/icons/svg/application-exit.svg | 376 --- Z/src/nglwidgets/icons/svg/arrow_back.svg | 392 --- .../icons/svg/arrow_back_disabled.svg | 62 - .../icons/svg/artillery_firings.svg | 459 --- .../icons/svg/artillery_firings_disabled.svg | 134 - .../icons/svg/audio-volume-high.svg | 397 --- .../nglwidgets/icons/svg/audio-volume-low.svg | 381 --- .../icons/svg/audio-volume-medium.svg | 390 --- .../icons/svg/audio-volume-muted.svg | 384 --- Z/src/nglwidgets/icons/svg/battery_mode.svg | 368 --- Z/src/nglwidgets/icons/svg/camera_control.svg | 428 --- .../icons/svg/camera_control_disabled.svg | 102 - .../nglwidgets/icons/svg/change_password.svg | 466 ---- .../icons/svg/change_password_disabled.svg | 140 - Z/src/nglwidgets/icons/svg/chat.svg | 408 --- Z/src/nglwidgets/icons/svg/chat_disabled.svg | 82 - .../nglwidgets/icons/svg/chemical_agents.svg | 458 --- .../icons/svg/chemical_agents_disabled.svg | 132 - .../icons/svg/config_gobal_alarms.svg | 508 ---- .../svg/config_gobal_alarms_disabled.svg | 180 -- .../nglwidgets/icons/svg/configure_arrow.svg | 451 --- .../icons/svg/configure_buildings.svg | 472 ---- .../svg/configure_buildings_disabled.svg | 143 - .../icons/svg/configure_line_of_sight.svg | 509 ---- .../svg/configure_line_of_sight_disabled.svg | 184 -- .../icons/svg/configure_symbol_attributes.svg | 464 --- .../configure_symbol_attributes_disabled.svg | 144 - .../icons/svg/configure_tunnels.svg | 498 ---- .../icons/svg/configure_tunnels_disabled.svg | 173 -- .../icons/svg/create_distance_measure.svg | 470 ---- .../svg/create_distance_measure_disabled.svg | 142 - .../icons/svg/create_evaluation_topic.svg | 416 --- .../icons/svg/create_image_topic.svg | 403 --- .../icons/svg/create_lecture_topic.svg | 470 ---- .../nglwidgets/icons/svg/create_pdf_topic.svg | 438 --- .../nglwidgets/icons/svg/create_snapshot.svg | 467 ---- .../icons/svg/create_snapshot_disabled.svg | 147 - .../icons/svg/create_terrain_profile.svg | 433 --- .../svg/create_terrain_profile_disabled.svg | 106 - Z/src/nglwidgets/icons/svg/create_thp.svg | 430 --- .../icons/svg/create_thp_disabled.svg | 105 - Z/src/nglwidgets/icons/svg/danger_areas.svg | 434 --- .../icons/svg/debug-execute-from-cursor.svg | 416 --- .../icons/svg/debug-execute-to-cursor.svg | 417 --- .../icons/svg/default_operator_rights.svg | 461 --- .../svg/default_operator_rights_disabled.svg | 132 - .../icons/svg/define_border_lines.svg | 499 ---- .../svg/define_border_lines_disabled.svg | 172 -- .../icons/svg/define_force_templates.svg | 467 ---- .../svg/define_force_templates_disabled.svg | 140 - .../icons/svg/define_movement_simulation.svg | 500 ---- .../define_movement_simulation_disabled.svg | 181 -- .../icons/svg/define_player_types.svg | 536 ---- .../svg/define_player_types_disabled.svg | 213 -- .../icons/svg/define_position_system.svg | 427 --- .../svg/define_position_system_disabled.svg | 99 - .../icons/svg/define_presentation.svg | 400 --- .../svg/define_presentation_disabled.svg | 72 - .../icons/svg/define_tactical_symbols.svg | 434 --- .../svg/define_tactical_symbols_disabled.svg | 103 - Z/src/nglwidgets/icons/svg/define_teams.svg | 450 --- .../icons/svg/define_teams_disabled.svg | 120 - .../icons/svg/define_training_cycles.svg | 466 ---- .../svg/define_training_cycles_disabled.svg | 130 - Z/src/nglwidgets/icons/svg/delete_thp.svg | 424 --- .../icons/svg/delete_thp_disabled.svg | 95 - Z/src/nglwidgets/icons/svg/dialog-close.svg | 386 --- .../nglwidgets/icons/svg/dialog-ok-apply.svg | 377 --- Z/src/nglwidgets/icons/svg/dialog-ok.svg | 377 --- .../icons/svg/display_oc_evaluations.svg | 403 --- .../svg/display_oc_evaluations_disabled.svg | 74 - .../nglwidgets/icons/svg/display_reports.svg | 423 --- .../icons/svg/display_reports_disabled.svg | 96 - Z/src/nglwidgets/icons/svg/document-edit.svg | 386 --- Z/src/nglwidgets/icons/svg/document-new.svg | 383 --- .../icons/svg/document-open-data.svg | 409 --- Z/src/nglwidgets/icons/svg/document-open.svg | 376 --- .../icons/svg/document-properties.svg | 407 --- Z/src/nglwidgets/icons/svg/documents.svg | 417 --- .../icons/svg/documents_disabled.svg | 88 - Z/src/nglwidgets/icons/svg/download.svg | 380 --- .../icons/svg/draw-bezier-curves.svg | 397 --- Z/src/nglwidgets/icons/svg/edit-cut.svg | 400 --- .../icons/svg/edit_evaluation_marker.svg | 468 ---- .../svg/edit_evaluation_marker_disabled.svg | 144 - .../svg/electronical_warfare_measures.svg | 488 ---- ...electronical_warfare_measures_disabled.svg | 154 - Z/src/nglwidgets/icons/svg/evaluate.svg | 454 --- .../icons/svg/evaluate_disabled.svg | 129 - .../icons/svg/evaluation_elements.svg | 441 --- .../svg/evaluation_elements_disabled.svg | 115 - .../icons/svg/execute_presentation.svg | 406 --- .../svg/execute_presentation_disabled.svg | 78 - Z/src/nglwidgets/icons/svg/exercise_force.svg | 429 --- .../icons/svg/exercise_force_disabled.svg | 103 - .../nglwidgets/icons/svg/exercise_purpose.svg | 449 --- .../icons/svg/exercise_purpose_disabled.svg | 126 - .../nglwidgets/icons/svg/export_documents.svg | 409 --- .../icons/svg/export_documents_disabled.svg | 80 - Z/src/nglwidgets/icons/svg/fill_pattern.svg | 403 --- .../nglwidgets/icons/svg/filter_barriers.svg | 498 ---- .../icons/svg/filter_barriers_disabled.svg | 178 -- .../icons/svg/filter_border_lines.svg | 475 ---- .../svg/filter_border_lines_disabled.svg | 151 - .../icons/svg/filter_collapse_expand.svg | 434 --- .../svg/filter_collapse_expand_disabled.svg | 112 - .../icons/svg/filter_effect_simulation.svg | 494 ---- .../svg/filter_effect_simulation_disabled.svg | 170 -- Z/src/nglwidgets/icons/svg/filter_firings.svg | 478 ---- .../icons/svg/filter_firings_disabled.svg | 154 - .../icons/svg/filter_line_of_sight.svg | 444 --- .../svg/filter_line_of_sight_disabled.svg | 115 - .../icons/svg/filter_map_settings.svg | 407 --- .../svg/filter_map_settings_disabled.svg | 81 - .../icons/svg/filter_map_settings_load.svg | 419 --- .../svg/filter_map_settings_load_disabled.svg | 93 - .../icons/svg/filter_maps_settings_save.svg | 420 --- .../filter_maps_settings_save_disabled.svg | 97 - .../icons/svg/filter_nbc_agents.svg | 528 ---- .../icons/svg/filter_nbc_agents_disabled.svg | 204 -- .../nglwidgets/icons/svg/filter_nc_agents.svg | 484 ---- .../icons/svg/filter_nc_agents_disabled.svg | 159 -- Z/src/nglwidgets/icons/svg/filter_player.svg | 452 --- .../icons/svg/filter_player_disabled.svg | 129 - Z/src/nglwidgets/icons/svg/filter_teams.svg | 471 ---- .../icons/svg/filter_teams_disabled.svg | 148 - .../icons/svg/fixed_video_announcement.svg | 450 --- .../svg/fixed_video_announcement_disabled.svg | 125 - .../icons/svg/force_composition.svg | 468 ---- .../icons/svg/force_composition_disabled.svg | 140 - .../icons/svg/force_organization.svg | 504 ---- .../icons/svg/force_organization_disabled.svg | 179 -- .../icons/svg/force_player_configuration.svg | 486 ---- .../force_player_configuration_disabled.svg | 160 -- Z/src/nglwidgets/icons/svg/freq_plan.svg | 450 --- .../icons/svg/function_key_toolbar.svg | 463 --- .../svg/function_key_toolbar_disabled.svg | 140 - Z/src/nglwidgets/icons/svg/getpos.svg | 394 --- .../icons/svg/global_alarms_history.svg | 450 --- .../svg/global_alarms_history_disabled.svg | 122 - .../icons/svg/global_exercise_settings.svg | 480 ---- .../svg/global_exercise_settings_disabled.svg | 158 -- Z/src/nglwidgets/icons/svg/go-home.svg | 386 --- .../icons/svg/hand_grenade_locations.svg | 465 ---- Z/src/nglwidgets/icons/svg/help_index.svg | 451 --- .../icons/svg/help_index_disabled.svg | 122 - .../nglwidgets/icons/svg/import_buildings.svg | 423 --- .../icons/svg/import_buildings_disabled.svg | 94 - .../nglwidgets/icons/svg/import_documents.svg | 422 --- .../icons/svg/import_documents_disabled.svg | 94 - Z/src/nglwidgets/icons/svg/import_images.svg | 417 --- .../icons/svg/import_images_disabled.svg | 90 - Z/src/nglwidgets/icons/svg/import_msdl.svg | 400 --- .../icons/svg/import_pdf_document.svg | 457 --- .../svg/import_pdf_document_disabled.svg | 132 - Z/src/nglwidgets/icons/svg/import_video.svg | 478 ---- .../icons/svg/import_video_disabled.svg | 153 - Z/src/nglwidgets/icons/svg/insert_script.svg | 414 --- Z/src/nglwidgets/icons/svg/karte.svg | 410 --- Z/src/nglwidgets/icons/svg/layer.svg | 410 --- Z/src/nglwidgets/icons/svg/line_styles.svg | 383 --- Z/src/nglwidgets/icons/svg/list-add-user.svg | 400 --- .../nglwidgets/icons/svg/list-remove-user.svg | 393 --- Z/src/nglwidgets/icons/svg/load.svg | 392 --- Z/src/nglwidgets/icons/svg/load_disabled.svg | 67 - .../icons/svg/load_global_filter.svg | 456 --- .../icons/svg/load_global_filter_disabled.svg | 136 - Z/src/nglwidgets/icons/svg/logout.svg | 413 --- .../nglwidgets/icons/svg/logout_disabled.svg | 85 - .../nglwidgets/icons/svg/map_context_menu.svg | 487 ---- .../icons/svg/map_context_menu_disabled.svg | 143 - Z/src/nglwidgets/icons/svg/map_settings.svg | 484 ---- .../icons/svg/map_settings_disabled.svg | 161 -- Z/src/nglwidgets/icons/svg/marke.svg | 413 --- Z/src/nglwidgets/icons/svg/marker-end.svg | 373 --- Z/src/nglwidgets/icons/svg/marker-start.svg | 373 --- Z/src/nglwidgets/icons/svg/media-flash.svg | 375 --- .../icons/svg/media-play-backward.svg | 365 --- .../icons/svg/media-play-slower.svg | 372 --- Z/src/nglwidgets/icons/svg/media-record.svg | 373 --- .../icons/svg/media-seek-backward.svg | 376 --- .../icons/svg/media-seek-forward.svg | 375 --- .../icons/svg/media-skip-backward.svg | 379 --- .../icons/svg/media-skip-forward.svg | 379 --- .../icons/svg/media-vcr-nopause.svg | 370 --- .../nglwidgets/icons/svg/media-vcr-normal.svg | 369 --- .../icons/svg/media-vcr-withpause.svg | 390 --- Z/src/nglwidgets/icons/svg/messages.svg | 391 --- .../icons/svg/messages_disabled.svg | 65 - .../icons/svg/metereological_data.svg | 417 --- .../svg/metereological_data_disabled.svg | 91 - Z/src/nglwidgets/icons/svg/mgrs_grid.svg | 504 ---- .../icons/svg/mgrs_grid_disabled.svg | 178 -- .../icons/svg/mine_barrier_corridor.svg | 485 ---- .../svg/mine_barrier_corridor_disabled.svg | 161 -- Z/src/nglwidgets/icons/svg/mine_barriers.svg | 490 ---- .../icons/svg/mine_barriers_disabled.svg | 169 -- Z/src/nglwidgets/icons/svg/mini_map.svg | 514 ---- .../icons/svg/mini_map_disabled.svg | 191 -- Z/src/nglwidgets/icons/svg/mlvs_control.svg | 436 --- Z/src/nglwidgets/icons/svg/mobile_video.svg | 409 --- .../icons/svg/mobile_video_disabled.svg | 82 - .../svg/mortar_ammunition_administration.svg | 440 --- ...tar_ammunition_administration_disabled.svg | 111 - .../icons/svg/mortar_ammunition_firings.svg | 474 ---- .../mortar_ammunition_firings_disabled.svg | 145 - .../nglwidgets/icons/svg/network-wireless.svg | 392 --- Z/src/nglwidgets/icons/svg/new_documents.svg | 885 ------ .../icons/svg/new_mail_indicator.svg | 405 --- Z/src/nglwidgets/icons/svg/nuclear_agents.svg | 422 --- Z/src/nglwidgets/icons/svg/object-group.svg | 386 --- Z/src/nglwidgets/icons/svg/object-ungroup.svg | 405 --- .../icons/svg/objects_define_areas.svg | 464 --- .../svg/objects_define_areas_disabled.svg | 139 - .../icons/svg/oc_aar_preparation.svg | 421 --- .../icons/svg/oc_aar_preparation_disabled.svg | 94 - Z/src/nglwidgets/icons/svg/oc_composition.svg | 479 ---- .../icons/svg/oc_composition_disabled.svg | 154 - .../icons/svg/oc_evaluation_schemes.svg | 457 --- .../svg/oc_evaluation_schemes_disabled.svg | 135 - .../icons/svg/oc_player_configuration.svg | 488 ---- .../svg/oc_player_configuration_disabled.svg | 163 -- .../icons/svg/operator_administration.svg | 458 --- .../svg/operator_administration_disabled.svg | 129 - .../icons/svg/organize_documents.svg | 420 --- .../icons/svg/organize_documents_disabled.svg | 95 - .../nglwidgets/icons/svg/organize_library.svg | 467 ---- .../icons/svg/organize_library_disabled.svg | 151 - Z/src/nglwidgets/icons/svg/overlay_plans.svg | 442 --- .../icons/svg/overlay_plans_disabled.svg | 115 - Z/src/nglwidgets/icons/svg/overlays.svg | 402 --- Z/src/nglwidgets/icons/svg/phase_def.svg | 501 ---- .../icons/svg/pi_force_collapse.svg | 394 --- .../nglwidgets/icons/svg/pi_force_expand.svg | 394 --- Z/src/nglwidgets/icons/svg/pi_force_left.svg | 386 --- Z/src/nglwidgets/icons/svg/pi_force_right.svg | 387 --- Z/src/nglwidgets/icons/svg/pi_markup_3d.svg | 407 --- .../nglwidgets/icons/svg/pi_movementtrack.svg | 389 --- Z/src/nglwidgets/icons/svg/pi_nbc.svg | 406 --- .../icons/svg/pi_set_player_mode.svg | 401 --- Z/src/nglwidgets/icons/svg/pi_show_bite.svg | 393 --- .../nglwidgets/icons/svg/pi_show_building.svg | 472 ---- .../icons/svg/pi_tact_status_history.svg | 396 --- .../icons/svg/pi_type_weaponrange.svg | 752 ----- .../icons/svg/planned_training_cycles.svg | 404 --- Z/src/nglwidgets/icons/svg/plans.svg | 410 --- Z/src/nglwidgets/icons/svg/plans_disabled.svg | 81 - Z/src/nglwidgets/icons/svg/playback_audio.svg | 426 --- .../icons/svg/playback_audio_disabled.svg | 100 - .../icons/svg/playback_situation.svg | 413 --- .../icons/svg/playback_situation_disabled.svg | 87 - Z/src/nglwidgets/icons/svg/playback_video.svg | 427 --- .../icons/svg/playback_video_disabled.svg | 101 - .../nglwidgets/icons/svg/player_equipment.svg | 675 ----- .../icons/svg/player_equipment_check.svg | 425 --- .../svg/player_equipment_check_disabled.svg | 96 - Z/src/nglwidgets/icons/svg/player_mode.svg | 647 ----- .../svg/preferences-web-browser-cache.svg | 458 --- Z/src/nglwidgets/icons/svg/prepare_thp.svg | 452 --- .../icons/svg/prepare_thp_disabled.svg | 125 - Z/src/nglwidgets/icons/svg/presets.svg | 476 ---- .../nglwidgets/icons/svg/presets_disabled.svg | 153 - .../icons/svg/primary_map_position.svg | 407 --- .../svg/primary_map_position_disabled.svg | 81 - .../icons/svg/primary_map_toolbar.svg | 448 --- .../svg/primary_map_toolbar_disabled.svg | 125 - Z/src/nglwidgets/icons/svg/proj_audio.svg | 403 --- Z/src/nglwidgets/icons/svg/projection.svg | 379 --- Z/src/nglwidgets/icons/svg/raster.svg | 390 --- .../nglwidgets/icons/svg/raster_disabled.svg | 68 - .../icons/svg/report_ammunition_left.svg | 431 --- .../svg/report_ammunition_left_disabled.svg | 107 - .../icons/svg/report_ammunition_resupply.svg | 502 ---- .../report_ammunition_resupply_disabled.svg | 179 -- Z/src/nglwidgets/icons/svg/report_awes.svg | 493 ---- .../icons/svg/report_awes_disabled.svg | 168 -- .../icons/svg/report_combat_effectiveness.svg | 480 ---- .../report_combat_effectiveness_disabled.svg | 155 -- .../icons/svg/report_combat_power.svg | 494 ---- .../svg/report_combat_power_disabled.svg | 168 -- .../icons/svg/report_explosive_devices.svg | 454 --- .../svg/report_explosive_devices_disabled.svg | 130 - .../icons/svg/report_maintenance.svg | 432 --- .../icons/svg/report_maintenance_disabled.svg | 108 - .../icons/svg/report_mine_barrier.svg | 464 --- .../svg/report_mine_barrier_disabled.svg | 138 - .../icons/svg/report_mortar_firings.svg | 461 --- .../svg/report_mortar_firings_disabled.svg | 135 - .../icons/svg/report_nbc_events.svg | 493 ---- .../icons/svg/report_nbc_events_disabled.svg | 170 -- .../nglwidgets/icons/svg/report_nc_events.svg | 464 --- .../icons/svg/report_nc_events_disabled.svg | 138 - .../nglwidgets/icons/svg/report_treatment.svg | 449 --- .../icons/svg/report_treatment_disabled.svg | 125 - .../icons/svg/report_treatmentmaintanance.svg | 460 --- .../report_treatmentmaintanance_disabled.svg | 136 - Z/src/nglwidgets/icons/svg/restart.svg | 369 --- .../nglwidgets/icons/svg/save_preferences.svg | 468 ---- .../icons/svg/save_preferences_disabled.svg | 130 - Z/src/nglwidgets/icons/svg/screenshot.svg | 398 --- .../icons/svg/screenshot_disabled.svg | 71 - .../icons/svg/secondary_map_position.svg | 420 --- .../svg/secondary_map_position_disabled.svg | 96 - .../icons/svg/secondary_map_toolbar.svg | 461 --- .../svg/secondary_map_toolbar_disabled.svg | 138 - Z/src/nglwidgets/icons/svg/select_player.svg | 474 ---- .../icons/svg/select_player_disabled.svg | 159 -- .../icons/svg/select_training_areas.svg | 404 --- .../svg/select_training_areas_disabled.svg | 77 - .../icons/svg/select_training_cylces.svg | 442 --- .../svg/select_training_cylces_disabled.svg | 114 - .../icons/svg/send_player_message.svg | 419 --- .../svg/send_player_message_disabled.svg | 92 - .../icons/svg/set_exercise_process.svg | 485 ---- .../svg/set_exercise_process_disabled.svg | 161 -- .../icons/svg/set_force_visibility.svg | 477 ---- .../svg/set_force_visibility_disabled.svg | 149 - .../icons/svg/set_home_transparency.svg | 444 --- .../svg/set_home_transparency_disabled.svg | 119 - .../icons/svg/set_marker_situation.svg | 445 --- .../svg/set_marker_situation_disabled.svg | 126 - .../nglwidgets/icons/svg/set_marker_video.svg | 474 ---- .../icons/svg/set_marker_video_disabled.svg | 155 -- .../icons/svg/set_markers_audio.svg | 461 --- .../icons/svg/set_markers_audio_disabled.svg | 139 - .../nglwidgets/icons/svg/set_player_mode.svg | 401 --- .../icons/svg/set_player_mode_disabled.svg | 73 - .../icons/svg/set_player_status.svg | 391 --- .../icons/svg/set_player_status_disabled.svg | 67 - .../icons/svg/set_view_position.svg | 454 --- .../icons/svg/set_view_position_disabled.svg | 125 - .../icons/svg/show_building_configuration.svg | 466 ---- .../show_building_configuration_disabled.svg | 138 - .../icons/svg/show_distance_measure.svg | 455 --- .../svg/show_distance_measure_disabled.svg | 130 - .../icons/svg/show_line_of_sight.svg | 454 --- .../icons/svg/show_line_of_sight_disabled.svg | 126 - .../icons/svg/show_terrain_profile.svg | 439 --- .../svg/show_terrain_profile_disabled.svg | 113 - .../icons/svg/simulated_players.svg | 409 --- .../icons/svg/simulated_players_disabled.svg | 85 - Z/src/nglwidgets/icons/svg/situation.svg | 431 --- .../icons/svg/situation_disabled.svg | 106 - Z/src/nglwidgets/icons/svg/status_bar.svg | 486 ---- .../icons/svg/status_bar_disabled.svg | 159 -- .../icons/svg/support_documents.svg | 449 --- .../icons/svg/support_documents_disabled.svg | 120 - Z/src/nglwidgets/icons/svg/switch_display.svg | 435 --- .../icons/svg/switch_display_disabled.svg | 111 - .../icons/svg/switch_to_primary_map.svg | 439 --- .../svg/switch_to_primary_map_disabled.svg | 110 - .../icons/svg/switch_to_sec_map.svg | 447 --- .../icons/svg/switch_to_sec_map_disabled.svg | 120 - Z/src/nglwidgets/icons/svg/symbol_display.svg | 411 --- .../icons/svg/symbol_display_disabled.svg | 86 - Z/src/nglwidgets/icons/svg/system_status.svg | 456 --- .../icons/svg/system_status_disabled.svg | 127 - .../icons/svg/tactical_organization.svg | 455 --- .../svg/tactical_organization_disabled.svg | 129 - .../nglwidgets/icons/svg/tactical_symbol.svg | 404 --- .../icons/svg/tactical_symbol_disabled.svg | 76 - Z/src/nglwidgets/icons/svg/target.svg | 402 --- .../nglwidgets/icons/svg/terminal_window.svg | 399 --- .../icons/svg/terminal_window_disabled.svg | 70 - Z/src/nglwidgets/icons/svg/toolbar_editor.svg | 480 ---- .../icons/svg/toolbar_editor_disabled.svg | 157 -- .../icons/svg/translation_editor.svg | 391 --- .../icons/svg/update_player_position.svg | 423 --- .../svg/update_player_position_disabled.svg | 97 - Z/src/nglwidgets/icons/svg/video_menu.svg | 404 --- .../icons/svg/video_menu_disabled.svg | 79 - .../nglwidgets/icons/svg/view-statistics.svg | 378 --- Z/src/nglwidgets/icons/svg/visible_teams.svg | 487 ---- .../icons/svg/voice_communication.svg | 470 ---- .../icons/svg/voice_communication_access.svg | 455 --- .../voice_communication_access_disabled.svg | 125 - .../svg/voice_communication_disabled.svg | 140 - Z/src/nglwidgets/icons/svg/weapon_firing.svg | 438 --- .../icons/svg/weapon_firing_disabled.svg | 112 - Z/src/nglwidgets/icons/svg/wireless.svg | 448 --- Z/src/nglwidgets/ngldialog.cpp | 148 - Z/src/nglwidgets/ngldialog.h | 62 - Z/src/nglwidgets/ngldialogbuttonbox.cpp | 445 --- Z/src/nglwidgets/ngldialogbuttonbox.h | 189 -- Z/src/nglwidgets/nglmainwindow.cpp | 106 - Z/src/nglwidgets/nglmainwindow.h | 46 - Z/src/nglwidgets/nglpdfdocument.cpp | 335 --- Z/src/nglwidgets/nglpdfdocument.h | 118 - Z/src/nglwidgets/nglpdfpage.cpp | 212 -- Z/src/nglwidgets/nglpdfpage.h | 55 - Z/src/nglwidgets/nglpdfwidget.cpp | 151 - Z/src/nglwidgets/nglpdfwidget.h | 49 - Z/src/nglwidgets/nglpdfwidget.ui | 253 -- Z/src/nglwidgets/nglresources.qrc | 669 ----- Z/src/nglwidgets/nglstandardbutton.cpp | 336 --- Z/src/nglwidgets/nglstandardbutton.h | 105 - Z/src/nglwidgets/ngltreeitem.cpp | 131 - Z/src/nglwidgets/ngltreeitem.h | 72 - Z/src/nglwidgets/ngltreemodel.cpp | 460 --- Z/src/nglwidgets/ngltreemodel.h | 98 - Z/src/nglwidgets/nglwidgets_export.h | 17 - Z/src/nglwidgets/rme_light_style.qrc | 7 - Z/src/nglwidgets/test_nglpdfwidget.cpp | 20 - Z/src/qsql_informix/CMakeLists.txt | 49 - Z/src/qsql_informix/FindCrypt.cmake | 38 - Z/src/qsql_informix/FindIClientSDK.cmake | 209 -- Z/src/qsql_informix/qsql_informix.cpp | 1334 --------- Z/src/qsql_informix/qsql_informix.h | 88 - Z/src/qsql_informix/qsql_informix.json | 3 - Z/src/qsql_informix/qsql_informix_plugin.cpp | 28 - Z/src/qsql_informix/qsql_informix_test.cpp | 277 -- Z/src/qsql_informix/qsql_informix_utils.cpp | 98 - Z/src/qsql_informix/qsql_informix_utils.h | 41 - Z/src/tcpserver/CMakeLists.txt | 56 - Z/src/tcpserver/cbi_help.cpp | 29 - Z/src/tcpserver/cbi_help.h | 19 - Z/src/tcpserver/cbi_pingpong.cpp | 23 - Z/src/tcpserver/cbi_pingpong.h | 19 - Z/src/tcpserver/cbi_quit.cpp | 19 - Z/src/tcpserver/cbi_quit.h | 19 - Z/src/tcpserver/cbi_recvfile.cpp | 86 - Z/src/tcpserver/cbi_recvfile.h | 23 - Z/src/tcpserver/cbi_sendfile.cpp | 45 - Z/src/tcpserver/cbi_sendfile.h | 17 - Z/src/tcpserver/cbi_shutdown.cpp | 25 - Z/src/tcpserver/cbi_shutdown.h | 25 - Z/src/tcpserver/commandinterface.cpp | 329 --- Z/src/tcpserver/commandinterface.h | 158 -- Z/src/tcpserver/tcpserver.cpp | 209 -- Z/src/tcpserver/tcpserver.h | 96 - Z/src/tcpserver/tcpserver_export.h | 27 - Z/src/tcpserver/tcpserver_logger.h | 27 - Z/src/tcpserver/tcpserver_test.cpp | 43 - Z/src/tcpserver/workerbase.cpp | 304 -- Z/src/tcpserver/workerbase.h | 128 - app/README.md | 496 ---- app/migration_pipeline/__init__.py | 1 - app/migration_pipeline/api_client.py | 150 - app/migration_pipeline/build_runner.py | 185 -- app/migration_pipeline/chunker.py | 183 -- app/migration_pipeline/config.py | 96 - app/migration_pipeline/git_manager.py | 135 - app/migration_pipeline/migrator.py | 408 --- app/migration_pipeline/models.py | 151 - app/migration_pipeline/orchestrator.py | 204 -- app/migration_pipeline/quality_gate.py | 56 - app/migration_pipeline/quarantine.py | 190 -- app/migration_pipeline/reporter.py | 221 -- app/migration_pipeline/test_generator.py | 156 -- app/tests/test_api_client.py | 32 - app/tests/test_config.py | 58 - app/tests/test_run_migration.py | 34 - cmake/DoxygenMacros.cmake | 22 - cmake/FindArcGIS.cmake | 126 - cmake/FindBgViewer.cmake | 49 - cmake/FindBotan2.cmake | 48 - cmake/FindGrantlee5.cmake | 37 - cmake/FindKDChart.cmake | 40 - cmake/FindKDReports.cmake | 49 - cmake/FindLibraryWithDebug.cmake | 113 - cmake/FindLog4Qt.cmake | 95 - cmake/FindNGS.cmake | 134 - cmake/FindPopplerQt5.cmake | 116 - cmake/FindRTI.cmake | 223 -- cmake/FindSchusstafel.cmake | 40 - cmake/FindTMSIF.cmake | 104 - cmake/FindVLC.cmake | 49 - cmake/FindXSD.cmake | 81 - cmake/MacroCodeGenIdl.cmake | 511 ---- cmake/MacroEnsureOutOfSourceBuild.cmake | 21 - cmake/MacroRTIDDSGen.cmake | 77 - cmake/NGLMacros.cmake | 634 ----- cmake/make_qt_help.py | 186 -- cmake/ngl_dep_test_main.cpp | 5 - doxygen/Doxyfile.in | 2 - doxygen/Doxyfile2025.in | 2 - lts-cc.conf | 544 ---- qt-migration-legatus | 1 + specs/Guez_Ausbilderterminal.iss | 146 - specs/external/GeographicLib-1.49.spec | 87 - specs/external/OpenSceneGraph-3.2.3.spec | 78 - specs/external/avs_conf/avs_conf-if.spec.in | 76 - .../avs_conf/avs_conf-legacy-ws.spec.in | 56 - specs/external/avs_conf/avs_conf.spec.in | 131 - specs/external/avs_conf/build_rpm.sh | 118 - specs/external/avs_conf/build_rpm_git.sh | 92 - specs/external/avs_conf/guez/AVS_Config.conf | 22 - specs/external/avs_conf/guez/AVS_DB.conf | 9 - specs/external/avs_conf/guez/AVS_Export.conf | 10 - .../external/avs_conf/guez/AVS_Interface.conf | 86 - specs/external/avs_conf/guez/AVS_Mixer.conf | 22 - .../avs_conf/guez/AVS_Mobilegate.conf | 54 - .../avs_conf/guez/AVS_MotionRecorder.conf | 14 - specs/external/avs_conf/guez/AVS_Root.conf | 23 - .../avs_conf/guez/AVS_Server.aar.conf | 14 - specs/external/avs_conf/guez/AVS_Server.conf | 12 - .../avs_conf/guez/AVS_TimelineConverter.conf | 6 - .../avs_conf/guez/AVS_Workstation.conf | 325 --- .../external/avs_conf/guez/if/AVS_Config.conf | 23 - .../avs_conf/guez/if/AVS_Interface.conf.in | 26 - specs/external/avs_conf/guez/if/AVS_Root.conf | 22 - specs/external/avs_conf/ltu/AVS_Config.conf | 22 - specs/external/avs_conf/ltu/AVS_DB.conf | 9 - specs/external/avs_conf/ltu/AVS_Export.conf | 10 - .../external/avs_conf/ltu/AVS_Interface.conf | 86 - specs/external/avs_conf/ltu/AVS_Mixer.conf | 22 - .../external/avs_conf/ltu/AVS_Mobilegate.conf | 54 - .../avs_conf/ltu/AVS_MotionRecorder.conf | 14 - specs/external/avs_conf/ltu/AVS_Root.conf | 23 - .../external/avs_conf/ltu/AVS_Server.aar.conf | 14 - specs/external/avs_conf/ltu/AVS_Server.conf | 12 - .../avs_conf/ltu/AVS_TimelineConverter.conf | 6 - .../avs_conf/ltu/AVS_Workstation.conf | 320 --- .../external/avs_conf/ltu/if/AVS_Config.conf | 23 - .../avs_conf/ltu/if/AVS_Interface.conf.in | 26 - specs/external/avs_conf/ltu/if/AVS_Root.conf | 22 - .../bremen_buildings_buildingviewer.spec | 55 - specs/external/clazy/clazy-1.3.spec | 45 - specs/external/clazy/clazy-1.5.spec | 40 - specs/external/clazy/make_rpm.sh | 51 - specs/external/ctc_Log4Qt-1.4.2.patch | 23 - specs/external/ctc_devel_tds_qt5-1.0.spec | 188 -- specs/external/ctc_drc-1.1.spec | 51 - specs/external/ctc_gdal_2.4.3.spec | 92 - specs/external/ctc_mak_rti_lm.spec | 62 - specs/external/ctc_mak_rti_srv.spec | 63 - specs/external/ctc_online_help_de.spec | 74 - specs/external/ctc_online_help_en.spec | 67 - specs/external/ctc_rti-6.0.1-1.spec | 117 - specs/external/ctc_schusstafel.spec | 48 - specs/external/gdb/load-qt5printers.py | 45 - specs/external/gdb/load_lts_printers.py | 9 - specs/external/gdb/lts_printers/__init__.py | 3 - specs/external/gdb/lts_printers/gdb_helper.py | 242 -- .../gdb/lts_printers/qtcreator_helper.py | 167 -- specs/external/gdb/qt5printers/__init__.py | 34 - specs/external/gdb/qt5printers/core.py | 901 ------ specs/external/gdb/qt5printers/typeinfo.py | 355 --- specs/external/git_with_committer | 91 - .../postProcessingExcon/README.txt | 81 - .../checkDuplicateNames.sh | 64 - .../postProcessingExcon/checkProtective.sh | 35 - .../convert_coordinates.py | 172 -- .../guez_buildings_ba0_buildingviewer.spec | 74 - .../guez_buildings_ba1_buildingviewer.spec | 74 - .../guez_buildings_ba2_buildingviewer.spec | 74 - .../guez_buildings_ba3_buildingviewer.spec | 62 - .../guez_buildings_ba4_buildingviewer.spec | 62 - ...guez_buildings_salchau_buildingviewer.spec | 75 - .../postProcessingExcon/osgconv.sh | 86 - .../patches/patch_bldg_1251.flt.xml.patch | 57 - ...patch_bldg_2101.flt.VWM_Building.xml.patch | 46 - ...patch_bldg_3100.flt.VWM_Building.xml.patch | 19 - .../patches/patch_bldg_3101.flt.xml.patch | 38 - .../postProcessingExcon.sh | 408 --- .../vwm_adjustment/RECURSIV/BSM_pos.js | 233 -- .../vwm_adjustment/RECURSIV/egcu_conf.js | 160 -- .../vwm_adjustment/RECURSIV/pos_dir_guez.js | 101 - .../RECURSIV/replace_transistion_factors.js | 288 -- .../RECURSIV/simplify_polygons.js | 355 --- .../vertical_Transition_Factor_neu.js | 108 - .../RECURSIV/vwmXmlToVrmlWrl.js | 122 - .../vwm_adjustment/analyse_xdr_VWM_File.js | 86 - specs/external/kdchart-2.6.0.spec | 77 - specs/external/log4qt-1.4.2.spec | 80 - .../external/lts_astyle_helper-1.0/README.txt | 16 - .../external/lts_astyle_helper-1.0/astyle_lts | 25 - specs/external/lts_astyle_helper-1.0/main.cpp | 197 -- .../lts_astyle_helper-1.0/normalize.pro | 6 - specs/external/lts_astyle_helper.spec | 49 - .../lts_config_entw_192_168_133_44.conf | 14 - .../lts_config_entw_192_168_133_44.spec | 72 - specs/external/maia-1.2.spec | 91 - specs/external/mak_rti.spec | 75 - specs/external/ngs.spec | 111 - specs/external/ngs_addons.spec | 88 - specs/external/ngs_guez_fom.spec | 95 - specs/external/ngs_rtifacade_mak450.spec | 95 - .../external/ngs_rtifacade_prti431_1516e.spec | 94 - specs/external/pitch_rti_431_1516e.spec | 88 - .../ahtc_dependencies.spec | 47 - .../guez_dependencies.spec | 57 - .../l400_dependencies.spec | 47 - .../l400p2_dependencies.spec | 47 - .../lynxhu_dependencies.spec | 54 - .../qatar_dependencies.spec | 51 - .../qatarmout_dependencies.spec | 51 - specs/external/score_configuration-3.9.9.spec | 78 - specs/external/score_geodesy-3.3.8.spec | 85 - specs/external/score_geometry-2.4.3.spec | 82 - .../arcgis/qt-V100/arcgis-qt-V100.spec | 110 - .../qt-V100/arcgis_map_topographic.spec | 56 - .../qt-V100/arcgis_qt-V100-debug_symbols.spec | 66 - .../qt-V100/arcgis_qt-V100.2.1_testFix.spec | 58 - .../arcgis_local_server-V100.2.spec | 55 - .../arcgis_viewshed_gpk.spec | 55 - .../arcgis/symbols/arcgis_app6b.spec | 57 - .../guez/guez_arcgis_3Dmodels.spec | 62 - .../situation_map/guez/guez_arcgis_bldg.spec | 76 - .../guez_arcgis_elevation_map_letzlingen.spec | 64 - .../guez/guez_basemap_aerial2017_32632.spec | 61 - .../guez/guez_basemap_aerial2017_WebMerc.spec | 61 - ...z_basemap_schnoeggersburg_drone_32632.spec | 63 - ...basemap_schnoeggersburg_drone_WebMerc.spec | 63 - .../guez/guez_basemap_topo2015_32632.spec | 61 - .../guez/guez_basemap_topo2015_WebMerc.spec | 61 - .../guez/guez_situation_map.spec | 91 - specs/external/subversion_config | 239 -- specs/external/tmsif.spec | 70 - specs/external/valgrind_supressions.txt | 45 - specs/lts.spec | 387 --- specs/lts15.1.spec | 389 --- specs/lts15.2.spec | 321 --- specs/lts15.4.spec | 379 --- specs/lts15.6.spec | 389 --- specs/lts42.3.spec | 198 -- specs/ltsHLAOnly.spec | 262 -- specs/ltsSVOnly.spec | 198 -- specs/lts_libre_office_integration.spec | 60 - specs/lts_ngs_tes_uae_apache.spec | 58 - specs/lts_prti1516e.spec | 81 - specs/lts_thp.spec | 50 - tests/gtest/CMakeLists.txt | 20 - tests/gtest/README.md | 11 - tests/gtest/generate_gtests.py | 57 - tests/strix/Z/NGLLogger__MurmurHash2.cpp.md | 14 - tests/strix/Z/NGLLogger__NGLLogger.cpp.md | 14 - .../Z/NGLLogger__NGLLoggerBootstrap.cpp.md | 14 - .../strix/Z/NGLLogger__test_ngllogger.cpp.md | 14 - .../Z/QtSingleApplication__qtlocalpeer.cpp.md | 14 - .../QtSingleApplication__qtlockedfile.cpp.md | 14 - ...ingleApplication__qtlockedfile_unix.cpp.md | 14 - ...SingleApplication__qtlockedfile_win.cpp.md | 14 - ...gleApplication__qtsingleapplication.cpp.md | 14 - ...pplication__qtsinglecoreapplication.cpp.md | 14 - ...nterface__wms_ecc_equipRequirements.cpp.md | 14 - ...S_Interface__wms_ecc_equipmenttypes.cpp.md | 14 - .../Z/WMS_Interface__wms_ecc_exercises.cpp.md | 14 - .../WMS_Interface__wms_ecc_playerInfo.cpp.md | 14 - ...WMS_Interface__wms_ecc_registration.cpp.md | 14 - .../WMS_Interface__wms_ecc_unregister.cpp.md | 14 - .../Z/nglapplication__nglapplication.cpp.md | 14 - .../nglapplication__nglcoreapplication.cpp.md | 14 - .../nglapplication__nglguiapplication.cpp.md | 14 - .../Z/nglapplication__nglloggerhandler.cpp.md | 14 - ...glapplication__nglsingleapplication.cpp.md | 14 - ...plication__nglsinglecoreapplication.cpp.md | 14 - ...lcompression__nglcomp_bzip2iodevice.cpp.md | 14 - .../Z/nglcompression__nglcomp_compress.cpp.md | 14 - .../nglcompression__nglcomp_tarfilter.cpp.md | 14 - .../Z/nglcompression__nglcomp_test.cpp.md | 14 - tests/strix/Z/ngldb__dbconnection.cpp.md | 14 - tests/strix/Z/ngldb__dbtransaction.cpp.md | 14 - tests/strix/Z/ngldb__ngldb_logger.cpp.md | 14 - tests/strix/Z/ngldb__nsqlquery.cpp.md | 14 - tests/strix/Z/ngldb__sql_query_janitor.cpp.md | 14 - ...glsettings__nglsettings_connections.cpp.md | 14 - .../nglsettings__nglsettings_settings.cpp.md | 14 - .../strix/Z/ngltools__ngltools_convert.cpp.md | 14 - tests/strix/Z/ngltools__ngltools_dtg.cpp.md | 14 - .../Z/ngltools__ngltools_filesystem.cpp.md | 14 - .../Z/ngltools__ngltools_helpevent.cpp.md | 14 - .../Z/ngltranslator__ngltranslator.cpp.md | 14 - tests/strix/Z/nglwidgets__ngldialog.cpp.md | 14 - .../Z/nglwidgets__ngldialogbuttonbox.cpp.md | 14 - .../strix/Z/nglwidgets__nglmainwindow.cpp.md | 14 - .../strix/Z/nglwidgets__nglpdfdocument.cpp.md | 14 - tests/strix/Z/nglwidgets__nglpdfpage.cpp.md | 14 - tests/strix/Z/nglwidgets__nglpdfwidget.cpp.md | 14 - .../Z/nglwidgets__nglstandardbutton.cpp.md | 14 - tests/strix/Z/nglwidgets__ngltreeitem.cpp.md | 14 - tests/strix/Z/nglwidgets__ngltreemodel.cpp.md | 14 - .../Z/nglwidgets__test_nglpdfwidget.cpp.md | 14 - .../Z/qsql_informix__qsql_informix.cpp.md | 14 - ...qsql_informix__qsql_informix_plugin.cpp.md | 14 - .../qsql_informix__qsql_informix_test.cpp.md | 14 - .../qsql_informix__qsql_informix_utils.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_help.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_pingpong.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_quit.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_recvfile.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_sendfile.cpp.md | 14 - tests/strix/Z/tcpserver__cbi_shutdown.cpp.md | 14 - .../Z/tcpserver__commandinterface.cpp.md | 14 - tests/strix/Z/tcpserver__tcpserver.cpp.md | 14 - .../strix/Z/tcpserver__tcpserver_test.cpp.md | 14 - tests/strix/Z/tcpserver__workerbase.cpp.md | 14 - tests/strix/generate_strix_tests.py | 37 - tests/strix/run_all_strix.sh | 31 - translations/CMakeLists.txt | 4 - 1916 files changed, 4 insertions(+), 309991 deletions(-) create mode 100644 .gitmodules delete mode 100755 CMakeLists.txt delete mode 100644 CTestCustom.ctest delete mode 100644 Lts/CMakeLists.txt delete mode 100755 Lts/Common/CMakeLists.txt delete mode 100755 Lts/Common/CodeGen/CMakeLists.txt delete mode 100755 Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp delete mode 100755 Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h delete mode 100755 Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp delete mode 100755 Lts/Common/CodeGen/DaoGeneratorGrantlee.h delete mode 100755 Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp delete mode 100755 Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h delete mode 100755 Lts/Common/CodeGen/GeneratorGrantleeBase.cpp delete mode 100755 Lts/Common/CodeGen/GeneratorGrantleeBase.h delete mode 100755 Lts/Common/CodeGen/GrantleeWrappers.cpp delete mode 100755 Lts/Common/CodeGen/GrantleeWrappers.h delete mode 100755 Lts/Common/CodeGen/README.txt delete mode 100755 Lts/Common/CodeGen/SqlGenerator.cpp delete mode 100755 Lts/Common/CodeGen/SqlGenerator.h delete mode 100755 Lts/Common/CodeGen/StructBuilder.cpp delete mode 100755 Lts/Common/CodeGen/StructBuilder.h delete mode 100755 Lts/Common/CodeGen/XmlReader.cpp delete mode 100755 Lts/Common/CodeGen/XmlReader.h delete mode 100755 Lts/Common/CodeGen/codegen_export.h delete mode 100755 Lts/Common/CodeGen/codegen_log4cxx.props delete mode 100755 Lts/Common/CodeGen/codegen_simple.py delete mode 100755 Lts/Common/CodeGen/ea_export_script.txt delete mode 100755 Lts/Common/CodeGen/main_grantlee.cpp delete mode 100755 Lts/Common/CodeGen/main_grantlee_gui.cpp delete mode 100755 Lts/Common/CodeGen/main_grantlee_gui.h delete mode 100755 Lts/Common/CodeGen/main_grantlee_gui.ui delete mode 100755 Lts/Common/CodeGen/svc.idl.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dao.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dao.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_db.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_db_impl.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_db_impl.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_db_structs.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_db_structs.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dtc.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dtc_impl.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dts.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_dts_impl.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_enums.idl.grantlee delete mode 100755 Lts/Common/CodeGen/svc_ftc.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_ftc_impl.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_fts.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_fts_impl.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_json.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_json.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions delete mode 100755 Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee delete mode 100755 Lts/Common/CodeGen/svc_translation.cpp.grantlee delete mode 100755 Lts/Common/CodeGen/svc_translation.h.grantlee delete mode 100644 Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp delete mode 100644 Lts/Common/CommonUnitTestsPlaceholder/CMakeLists.txt delete mode 100644 Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_gtest.cpp delete mode 100644 Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_smoke.cpp delete mode 100644 Lts/Common/CommonUnitTestsPlaceholder/mocks/CMakeLists.txt delete mode 100644 Lts/Common/CommonUnitTestsPlaceholder/mocks/MockCoordTrans.h delete mode 100755 Lts/Common/LtsAppDescriptor/CMakeLists.txt delete mode 100755 Lts/Common/LtsAppDescriptor/checker/main.cpp delete mode 100755 Lts/Common/LtsAppDescriptor/lts_app_descriptor.cpp delete mode 100755 Lts/Common/LtsAppDescriptor/lts_app_descriptor.h delete mode 100755 Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.cpp delete mode 100755 Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.h delete mode 100755 Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.ui delete mode 100755 Lts/Common/LtsAppDescriptor/main.cpp delete mode 100755 Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp delete mode 100755 Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h delete mode 100755 Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui delete mode 100755 Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp delete mode 100755 Lts/Common/LtsAppManager/AppChooser/app_chooser.h delete mode 100755 Lts/Common/LtsAppManager/AppChooser/app_chooser.ui delete mode 100755 Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp delete mode 100755 Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h delete mode 100755 Lts/Common/LtsAppManager/CMakeLists.txt delete mode 100755 Lts/Common/LtsAppManager/lts_app_manager.cpp delete mode 100755 Lts/Common/LtsAppManager/lts_app_manager.h delete mode 100755 Lts/Common/LtsBotan/CMakeLists.txt delete mode 100755 Lts/Common/LtsCommandLineParser/CMakeLists.txt delete mode 100755 Lts/Common/LtsCommandLineParser/LtsCommandLineParser.cpp delete mode 100755 Lts/Common/LtsCommandLineParser/LtsCommandLineParser.h delete mode 100755 Lts/Common/LtsCoordTrans/CMakeLists.txt delete mode 100755 Lts/Common/LtsCoordTrans/lonlat2mgrs.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_trans.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_trans.h delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.h delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget.h delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget_mgrs.ui delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget_p.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget_p.h delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget_test.cpp delete mode 100755 Lts/Common/LtsCoordTrans/lts_coord_widget_wgs84.ui delete mode 100755 Lts/Common/LtsCoordTrans/mgrs2lonlat.cpp delete mode 100755 Lts/Common/LtsCoordinate/CMakeLists.txt delete mode 100755 Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt delete mode 100755 Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp delete mode 100755 Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h delete mode 100755 Lts/Common/LtsCoordinate/lts_coord_vector.cpp delete mode 100755 Lts/Common/LtsCoordinate/lts_coord_vector.h delete mode 100755 Lts/Common/LtsCoordinate/lts_coordinate.cpp delete mode 100755 Lts/Common/LtsCoordinate/lts_coordinate.h delete mode 100755 Lts/Common/LtsElevation/CMakeLists.txt delete mode 100755 Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt delete mode 100755 Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp delete mode 100755 Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h delete mode 100755 Lts/Common/LtsElevation/logger.cpp delete mode 100755 Lts/Common/LtsElevation/logger.h delete mode 100755 Lts/Common/LtsElevation/lts_elevation_manager.cpp delete mode 100755 Lts/Common/LtsElevation/lts_elevation_manager.h delete mode 100755 Lts/Common/LtsLicence/CMakeLists.txt delete mode 100755 Lts/Common/LtsLicence/aes_crypt.cpp delete mode 100755 Lts/Common/LtsLicence/aes_crypt.h delete mode 100755 Lts/Common/LtsLicence/aes_crypt_test.cpp delete mode 100755 Lts/Common/LtsLicence/aes_crypt_test.h delete mode 100755 Lts/Common/LtsLicence/lts_licence.cpp delete mode 100755 Lts/Common/LtsLicence/lts_licence.h delete mode 100755 Lts/Common/LtsLogin/CMakeLists.txt delete mode 100755 Lts/Common/LtsLogin/logout_info_dialog.cpp delete mode 100755 Lts/Common/LtsLogin/logout_info_dialog.h delete mode 100755 Lts/Common/LtsLogin/logout_info_dialog.ui delete mode 100755 Lts/Common/LtsLogin/lts_login.cpp delete mode 100755 Lts/Common/LtsLogin/lts_login.h delete mode 100755 Lts/Common/LtsMultiMonitor/CMakeLists.txt delete mode 100755 Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_dummy_widget.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_magic_corner.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_base.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_base.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_container.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_container.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_normal.h delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp delete mode 100755 Lts/Common/LtsMultiMonitor/lts_screen_wide.h delete mode 100755 Lts/Common/LtsNotification/CMakeLists.txt delete mode 100755 Lts/Common/LtsNotification/lts_notification.cpp delete mode 100755 Lts/Common/LtsNotification/lts_notification.h delete mode 100755 Lts/Common/LtsNotification/lts_notification_ctl.cpp delete mode 100755 Lts/Common/LtsNotification/lts_notification_ctl.h delete mode 100755 Lts/Common/LtsNotification/lts_notification_export.h delete mode 100755 Lts/Common/LtsPluginManager/CMakeLists.txt delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h delete mode 100755 Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui delete mode 100755 Lts/Common/LtsPluginManager/lts_plugin_loader.cpp delete mode 100755 Lts/Common/LtsPluginManager/lts_plugin_loader.h delete mode 100755 Lts/Common/LtsPluginManager/lts_plugin_manager.cpp delete mode 100755 Lts/Common/LtsPluginManager/lts_plugin_manager.h delete mode 100755 Lts/Common/LtsPluginManager/lts_plugin_manager_export.h delete mode 100755 Lts/Common/LtsPrinting/CMakeLists.txt delete mode 100755 Lts/Common/LtsPrinting/printing.cpp delete mode 100755 Lts/Common/LtsPrinting/printing.h delete mode 100755 Lts/Common/LtsPrinting/printing_base.cpp delete mode 100755 Lts/Common/LtsPrinting/printing_base.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/CMakeLists.txt delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h delete mode 100644 Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/logger.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/logger.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h delete mode 100755 Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp delete mode 100755 Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h delete mode 100755 Lts/Common/LtsScriptTimeline/CMakeLists.txt delete mode 100755 Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt delete mode 100755 Lts/Common/LtsScriptTimeline/Test/main.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/Test/mainwindow.h delete mode 100755 Lts/Common/LtsScriptTimeline/Test/mainwindow.ui delete mode 100755 Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui delete mode 100755 Lts/Common/LtsScriptTimeline/Ui/indicator.ui delete mode 100755 Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui delete mode 100755 Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui delete mode 100755 Lts/Common/LtsScriptTimeline/Ui/track_widget.ui delete mode 100755 Lts/Common/LtsScriptTimeline/filter_background_widget.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/filter_background_widget.h delete mode 100755 Lts/Common/LtsScriptTimeline/filter_track_widget.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/filter_track_widget.h delete mode 100755 Lts/Common/LtsScriptTimeline/indicator.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/indicator.h delete mode 100755 Lts/Common/LtsScriptTimeline/newsrc.tar.xz delete mode 100755 Lts/Common/LtsScriptTimeline/script_timeline.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/script_timeline.h delete mode 100755 Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h delete mode 100755 Lts/Common/LtsScriptTimeline/tools.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/tools.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_content_widget.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_content_widget.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_drag_handler.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_drag_handler.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_painters.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_painters.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_entry_previewer.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_header.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_header.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_resize_handler.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_resize_handler.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_select_handler.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_select_handler.h delete mode 100755 Lts/Common/LtsScriptTimeline/track_widget.cpp delete mode 100755 Lts/Common/LtsScriptTimeline/track_widget.h delete mode 100755 Lts/Common/LtsSerialPortQuerry/CMakeLists.txt delete mode 100755 Lts/Common/LtsSerialPortQuerry/logger.cpp delete mode 100755 Lts/Common/LtsSerialPortQuerry/logger.h delete mode 100755 Lts/Common/LtsSerialPortQuerry/lts_comport_querry.h delete mode 100755 Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.cpp delete mode 100755 Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.h delete mode 100755 Lts/Common/LtsSession/CMakeLists.txt delete mode 100755 Lts/Common/LtsSession/lts_session.cpp delete mode 100755 Lts/Common/LtsSession/lts_session.h delete mode 100755 Lts/Common/LtsSession/lts_session_data.cpp delete mode 100755 Lts/Common/LtsSession/lts_session_data.h delete mode 100755 Lts/Common/LtsSession/lts_session_data_dds.cpp delete mode 100755 Lts/Common/LtsSession/lts_session_data_dds.h delete mode 100755 Lts/Common/LtsSession/lts_session_lock_handler.cpp delete mode 100755 Lts/Common/LtsSession/lts_session_lock_handler.h delete mode 100755 Lts/Common/LtsSession/lts_session_manager.cpp delete mode 100755 Lts/Common/LtsSession/lts_session_manager.h delete mode 100755 Lts/Common/LtsTimeline/CMakeLists.txt delete mode 100755 Lts/Common/LtsTimeline/item.cpp delete mode 100755 Lts/Common/LtsTimeline/item.h delete mode 100755 Lts/Common/LtsTimeline/item_delegate.cpp delete mode 100755 Lts/Common/LtsTimeline/item_delegate.h delete mode 100755 Lts/Common/LtsTimeline/log4cxx_timeline.props delete mode 100755 Lts/Common/LtsTimeline/main.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_header_view.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_header_view.h delete mode 100755 Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h delete mode 100755 Lts/Common/LtsTimeline/timeline_model.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_model.h delete mode 100755 Lts/Common/LtsTimeline/timeline_utils.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_utils.h delete mode 100755 Lts/Common/LtsTimeline/timeline_view.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_view.h delete mode 100755 Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_view_drag_handler.h delete mode 100755 Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_view_resize_handler.h delete mode 100755 Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_view_selection_handler.h delete mode 100755 Lts/Common/LtsTimeline/timeline_view_track_handler.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_view_track_handler.h delete mode 100755 Lts/Common/LtsTimeline/timeline_widget.cpp delete mode 100755 Lts/Common/LtsTimeline/timeline_widget.h delete mode 100755 Lts/Common/LtsTimeline/timeline_widget.ui delete mode 100755 Lts/Common/LtsTimeline/track.cpp delete mode 100755 Lts/Common/LtsTimeline/track.h delete mode 100755 Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h delete mode 100755 Lts/Common/LtsTimelineClassic/CMakeLists.txt delete mode 100755 Lts/Common/LtsTimelineClassic/Event.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/Event.h delete mode 100755 Lts/Common/LtsTimelineClassic/EventDelegate.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/EventDelegate.h delete mode 100755 Lts/Common/LtsTimelineClassic/EventGraphic.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/EventGraphic.h delete mode 100755 Lts/Common/LtsTimelineClassic/EventList.h delete mode 100755 Lts/Common/LtsTimelineClassic/EventTools.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/EventTools.h delete mode 100755 Lts/Common/LtsTimelineClassic/Item.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/Item.h delete mode 100755 Lts/Common/LtsTimelineClassic/LineMap.h delete mode 100755 Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/PassiveEventContainer.h delete mode 100755 Lts/Common/LtsTimelineClassic/Range.h delete mode 100755 Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/TestTimelineModel.h delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineHeaderView.h delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineIndicator.h delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineView.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineView.h delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineWidget.cpp delete mode 100755 Lts/Common/LtsTimelineClassic/TimelineWidget.h delete mode 100755 Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui delete mode 100755 Lts/Common/LtsTimelineClassic/log4cxx_timeline.props delete mode 100755 Lts/Common/LtsTimelineClassic/main.cpp delete mode 100755 Lts/Common/LtsUtils/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/await.h delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/connection.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/data_modification_enum.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/dds_bitarray.h delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/dds_vector.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/dds_vector_to_pixmap.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/json.h delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/json_object.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/outdated_position.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/pgarray.cpp delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/pgarray.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/promise.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/promise.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.cpp delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.h delete mode 100644 Lts/Common/LtsUtils/LtsUtilsCore/qbytearray_byteaccess.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsCore/variant_operators.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.ui delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.h delete mode 100755 Lts/Common/LtsUtils/LtsUtilsSettings/CMakeLists.txt delete mode 100755 Lts/Common/LtsUtils/LtsUtilsSettings/settings.cpp delete mode 100755 Lts/Common/LtsUtils/LtsUtilsSettings/settings.h delete mode 100755 Lts/Common/LtsVirtualKeyboard/CMakeLists.txt delete mode 100755 Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp delete mode 100755 Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h delete mode 100755 Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp delete mode 100755 Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h delete mode 100755 Lts/Common/LtsWebSocket/CMakeLists.txt delete mode 100755 Lts/Common/LtsWebSocket/client_connection.cpp delete mode 100755 Lts/Common/LtsWebSocket/client_connection.h delete mode 100755 Lts/Common/LtsWebSocket/client_message.cpp delete mode 100755 Lts/Common/LtsWebSocket/client_message.h delete mode 100755 Lts/Common/LtsWebSocket/client_message_factory.cpp delete mode 100755 Lts/Common/LtsWebSocket/client_message_factory.h delete mode 100755 Lts/Common/LtsWebSocket/connection_handler.cpp delete mode 100755 Lts/Common/LtsWebSocket/connection_handler.h delete mode 100755 Lts/Common/LtsWebSocket/data_manager_base_dl.h delete mode 100755 Lts/Common/LtsWebSocket/json_keys.h delete mode 100755 Lts/Common/LtsWebSocket/logger.cpp delete mode 100755 Lts/Common/LtsWebSocket/logger.h delete mode 100755 Lts/Common/LtsWebSocket/lts_web_socket_client.cpp delete mode 100755 Lts/Common/LtsWebSocket/lts_web_socket_client.h delete mode 100755 Lts/Common/LtsWebSocket/lts_web_socket_server.cpp delete mode 100755 Lts/Common/LtsWebSocket/lts_web_socket_server.h delete mode 100755 Lts/Common/LtsWebSocket/message_transmitter.h delete mode 100755 Lts/Common/LtsWebSocket/reply_message.cpp delete mode 100755 Lts/Common/LtsWebSocket/reply_message.h delete mode 100755 Lts/Common/LtsWidgets/CMakeLists.txt delete mode 100755 Lts/Common/LtsWidgets/CMakeLists_rc.txt delete mode 100755 Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp delete mode 100755 Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h delete mode 100755 Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_abstract_table_model.h delete mode 100755 Lts/Common/LtsWidgets/lts_angle_edit.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_angle_edit.h delete mode 100755 Lts/Common/LtsWidgets/lts_angle_edit.ui delete mode 100755 Lts/Common/LtsWidgets/lts_background_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_background_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_base.h delete mode 100755 Lts/Common/LtsWidgets/lts_base_layout.ui delete mode 100755 Lts/Common/LtsWidgets/lts_base_layout_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_base_layout_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_bitfield_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_bitfield_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_checkable_table_model.h delete mode 100755 Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h delete mode 100755 Lts/Common/LtsWidgets/lts_column_organiser_widget.ui delete mode 100755 Lts/Common/LtsWidgets/lts_combo_box.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_combo_box.h delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit.h delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_p.h delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_rc.h delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_touch.h delete mode 100755 Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui delete mode 100755 Lts/Common/LtsWidgets/lts_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_button_box.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_button_box.h delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_button_box.ui delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_helper.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_helper.h delete mode 100755 Lts/Common/LtsWidgets/lts_dialog_helper.ui delete mode 100755 Lts/Common/LtsWidgets/lts_drag_icon.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_drag_icon.h delete mode 100755 Lts/Common/LtsWidgets/lts_drop_handler.h delete mode 100755 Lts/Common/LtsWidgets/lts_elided_label.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_elided_label.h delete mode 100755 Lts/Common/LtsWidgets/lts_events.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_events.h delete mode 100755 Lts/Common/LtsWidgets/lts_frameless_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_frameless_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_global_wait_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_grid_layout.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_grid_layout.h delete mode 100755 Lts/Common/LtsWidgets/lts_height_plot_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_height_plot_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_image_editor.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_image_editor.h delete mode 100755 Lts/Common/LtsWidgets/lts_info_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_info_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_info_dialog.ui delete mode 100755 Lts/Common/LtsWidgets/lts_led_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_led_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_message_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_message_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_message_dialog_helper.h delete mode 100755 Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_msec_date_time_edit.h delete mode 100755 Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_multi_range_spinbox.h delete mode 100755 Lts/Common/LtsWidgets/lts_notification_box.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_notification_box.h delete mode 100755 Lts/Common/LtsWidgets/lts_notification_box.ui delete mode 100755 Lts/Common/LtsWidgets/lts_operator_menu.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_operator_menu.h delete mode 100755 Lts/Common/LtsWidgets/lts_operator_menu.ui delete mode 100755 Lts/Common/LtsWidgets/lts_overlay_loading.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_overlay_loading.h delete mode 100755 Lts/Common/LtsWidgets/lts_overlay_loading.ui delete mode 100755 Lts/Common/LtsWidgets/lts_picture_view.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_picture_view.h delete mode 100755 Lts/Common/LtsWidgets/lts_profile_button.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_profile_button.h delete mode 100755 Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_progress_dialog_helper.h delete mode 100755 Lts/Common/LtsWidgets/lts_range_slider.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_range_slider.h delete mode 100755 Lts/Common/LtsWidgets/lts_selection_popup.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_selection_popup.h delete mode 100755 Lts/Common/LtsWidgets/lts_selection_popup.ui delete mode 100755 Lts/Common/LtsWidgets/lts_spinbox.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_spinbox.h delete mode 100755 Lts/Common/LtsWidgets/lts_splash.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_splash.h delete mode 100755 Lts/Common/LtsWidgets/lts_splitter.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_splitter.h delete mode 100755 Lts/Common/LtsWidgets/lts_splitter_handle.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_splitter_handle.h delete mode 100755 Lts/Common/LtsWidgets/lts_tab_bar.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tab_bar.h delete mode 100755 Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tab_sequence_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_tab_widget.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tab_widget.h delete mode 100755 Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_text_edit_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_text_editor.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_text_editor.h delete mode 100755 Lts/Common/LtsWidgets/lts_text_editor.ui delete mode 100755 Lts/Common/LtsWidgets/lts_text_editor2.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_text_editor2.h delete mode 100755 Lts/Common/LtsWidgets/lts_tool_bar.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tool_bar.h delete mode 100755 Lts/Common/LtsWidgets/lts_tool_bar_area.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tool_bar_area.h delete mode 100755 Lts/Common/LtsWidgets/lts_tool_bar_area.ui delete mode 100755 Lts/Common/LtsWidgets/lts_tool_box.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_tool_box.h delete mode 100755 Lts/Common/LtsWidgets/lts_wait_dialog.cpp delete mode 100755 Lts/Common/LtsWidgets/lts_wait_dialog.h delete mode 100755 Lts/Common/LtsWidgets/lts_widgets_export.h delete mode 100755 Lts/Common/LtsWidgets/ltswidgetsplugin.cpp delete mode 100755 Lts/Common/LtsWidgets/ltswidgetsplugin.json delete mode 100755 Lts/Common/LtsWidgets/resources/DMF.wav delete mode 100755 Lts/Common/LtsWidgets/resources/European-Union-Flag.png delete mode 100755 Lts/Common/LtsWidgets/resources/Germany-Flag.png delete mode 100755 Lts/Common/LtsWidgets/resources/Great-Britain-Flag.png delete mode 100755 Lts/Common/LtsWidgets/resources/ScriptEditor-Changelog-1.png delete mode 100755 Lts/Common/LtsWidgets/resources/US-Flag.png delete mode 100755 Lts/Common/LtsWidgets/resources/ack.svg delete mode 100755 Lts/Common/LtsWidgets/resources/ack_dark.svg delete mode 100755 Lts/Common/LtsWidgets/resources/beep.wav delete mode 100755 Lts/Common/LtsWidgets/resources/close.svg delete mode 100755 Lts/Common/LtsWidgets/resources/close_dark.svg delete mode 100755 Lts/Common/LtsWidgets/resources/effectarea-enter.wav delete mode 100755 Lts/Common/LtsWidgets/resources/effectarea-leave.wav delete mode 100755 Lts/Common/LtsWidgets/resources/entryNew.wav delete mode 100755 Lts/Common/LtsWidgets/resources/error.svg delete mode 100755 Lts/Common/LtsWidgets/resources/info.svg delete mode 100755 Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js delete mode 100755 Lts/Common/LtsWidgets/resources/jquery.orgchart.js delete mode 100755 Lts/Common/LtsWidgets/resources/leaveNew.wav delete mode 100755 Lts/Common/LtsWidgets/resources/lts_resources.qrc delete mode 100755 Lts/Common/LtsWidgets/resources/qwebchannel.js delete mode 100755 Lts/Common/LtsWidgets/resources/rde_icon_256.ico delete mode 100755 Lts/Common/LtsWidgets/resources/rde_icon_256.png delete mode 100755 Lts/Common/LtsWidgets/resources/rde_internal_dds_logo_256x256.ico delete mode 100755 Lts/Common/LtsWidgets/resources/rde_internal_dds_logo_256x256.png delete mode 100755 Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.ico delete mode 100755 Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.png delete mode 100755 Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.svg delete mode 100755 Lts/Common/LtsWidgets/resources/rde_komplett.png delete mode 100755 Lts/Common/LtsWidgets/resources/rde_logo.ico delete mode 100755 Lts/Common/LtsWidgets/resources/rde_logo.png delete mode 100755 Lts/Common/LtsWidgets/resources/rde_splitter_left.svg delete mode 100755 Lts/Common/LtsWidgets/resources/scripts.js delete mode 100755 Lts/Common/LtsWidgets/resources/start.html delete mode 100755 Lts/Common/LtsWidgets/resources/stylesheet-branch-closed.png delete mode 100755 Lts/Common/LtsWidgets/resources/stylesheet-branch-open.png delete mode 100755 Lts/Common/LtsWidgets/resources/success.svg delete mode 100755 Lts/Common/LtsWidgets/resources/warning.svg delete mode 100755 Lts/Common/LtsWidgets/resources/warningrange-enter.wav delete mode 100755 Lts/Common/LtsWidgets/resources/warningrange-leave.wav delete mode 100755 Lts/Common/LtsWidgets/tests/lts_picture_editing.cpp delete mode 100755 Lts/Common/LtsWidgets/tests/lts_picture_editing.h delete mode 100755 Lts/Common/LtsWidgets/tests/lts_picture_editing.ui delete mode 100755 Lts/Common/LtsWidgets/tests/lts_picture_editing_main.cpp delete mode 100755 Lts/Common/LtsWidgets/tests/main.cpp delete mode 100755 Lts/Common/LtsWidgets/tests/window.cpp delete mode 100755 Lts/Common/LtsWidgets/tests/window.h delete mode 100755 Lts/Common/LtsWidgets/ui_requirement.cpp delete mode 100755 Lts/Common/LtsWidgets/ui_requirement.h delete mode 100755 Lts/Common/PluginInterfaces/CMakeLists.txt delete mode 100755 Lts/Common/PluginInterfaces/data_plugin_interface.h delete mode 100755 Lts/Common/PluginInterfaces/gui_plugin_interface.h delete mode 100755 Lts/Common/PluginInterfaces/project_plugin_interface.h delete mode 100755 Lts/Common/PluginInterfaces/record_replay_plugin.h delete mode 100755 Lts/Common/PluginInterfaces/style_plugin_interface.h delete mode 100755 Lts/Common/PluginInterfaces/topic_replay_interface.h delete mode 100755 Lts/Common/ProjectConfig/CMakeLists.txt delete mode 100755 Lts/Common/ProjectConfig/project_defines.h delete mode 100755 Lts/Common/TopicEditorBase/CMakeLists.txt delete mode 100755 Lts/Common/TopicEditorBase/topic_editor_base.cpp delete mode 100755 Lts/Common/TopicEditorBase/topic_editor_base.h delete mode 100755 Lts/Common/TopicEditorBase/topic_editor_interface.h delete mode 100755 Lts/Common/TopicEditorBase/topic_editor_utils.cpp delete mode 100755 Lts/Common/TopicEditorBase/topic_editor_utils.h delete mode 100755 Lts/Common/TopicEditorBase/topic_preview_widget.cpp delete mode 100755 Lts/Common/TopicEditorBase/topic_preview_widget.h delete mode 100755 Lts/Common/TopicEditorBase/topic_preview_widget.ui delete mode 100755 Lts/Common/TopicEditorBase/topic_previewer.cpp delete mode 100755 Lts/Common/TopicEditorBase/topic_previewer.h delete mode 100755 Lts/Common/httq/AbstractServer.cpp delete mode 100755 Lts/Common/httq/CMakeLists.txt delete mode 100755 Lts/Common/httq/DataStream.cpp delete mode 100755 Lts/Common/httq/HttpRequest.cpp delete mode 100755 Lts/Common/httq/LICENSE_http-parser delete mode 100755 Lts/Common/httq/LICENSE_httq delete mode 100755 Lts/Common/httq/Logger.cpp delete mode 100755 Lts/Common/httq/README.md delete mode 100755 Lts/Common/httq/include/github-tag delete mode 100755 Lts/Common/httq/include/http_parser.c delete mode 100644 Lts/Common/httq/include/http_parser.h delete mode 100755 Lts/Common/httq/include/httq/AbstractServer.h delete mode 100755 Lts/Common/httq/include/httq/DataStream.h delete mode 100755 Lts/Common/httq/include/httq/HttpRequest.h delete mode 100755 Lts/Common/httq/include/httq/Logger.h delete mode 100755 Lts/DDS/CMakeLists.txt delete mode 100755 Lts/DDS/Core/CMakeLists.txt delete mode 100755 Lts/DDS/Core/core.xml delete mode 100755 Lts/DDS/Core/core_export.h delete mode 100755 Lts/DDS/Core/function_reply_object.cpp delete mode 100755 Lts/DDS/Core/function_reply_object.h delete mode 100755 Lts/DDS/Core/logger.cpp delete mode 100755 Lts/DDS/Core/logger.h delete mode 100755 Lts/DDS/Core/pacemaker.cpp delete mode 100755 Lts/DDS/Core/pacemaker.h delete mode 100755 Lts/DDS/Core/topic_lock_master.cpp delete mode 100755 Lts/DDS/Core/topic_lock_master.h delete mode 100755 Lts/DDS/DDSConnector/CMakeLists.txt delete mode 100755 Lts/DDS/DDSConnector/dds_connector.cpp delete mode 100755 Lts/DDS/DDSConnector/dds_connector.h delete mode 100755 Lts/DDS/DDSConnector/pub_sub_base.cpp delete mode 100755 Lts/DDS/DDSConnector/pub_sub_base.h delete mode 100755 Lts/DDS/DDSConnector/pub_sub_base_p.h delete mode 100755 Lts/DDS/DDSConnector/publisher.cpp delete mode 100755 Lts/DDS/DDSConnector/publisher.h delete mode 100755 Lts/DDS/DDSConnector/subscriber.cpp delete mode 100755 Lts/DDS/DDSConnector/subscriber.h delete mode 100755 Lts/DDS/DTC/CMakeLists.txt delete mode 100755 Lts/DDS/DTC/generic_dtc.h delete mode 100755 Lts/DDS/DTC/generic_dtc_impl.h delete mode 100755 Lts/DDS/DTS/CMakeLists.txt delete mode 100755 Lts/DDS/DTS/generic_dts.h delete mode 100755 Lts/DDS/DTS/generic_dts_impl.h delete mode 100755 Lts/DDS/DdsUnitTests/CMakeLists.txt delete mode 100755 Lts/DDS/DdsUnitTests/data_topic_tests.cpp delete mode 100755 Lts/DDS/DdsUnitTests/data_topic_tests.h delete mode 100755 Lts/DDS/DdsUnitTests/data_topic_tests_main.cpp delete mode 100755 Lts/DDS/DdsUnitTests/data_topic_tests_main.h delete mode 100755 Lts/DDS/DdsUnitTests/ddsut.xml delete mode 100755 Lts/DDS/DdsUnitTests/ddsut_export.h delete mode 100755 Lts/DDS/DdsUnitTests/function_topic_tests.cpp delete mode 100755 Lts/DDS/DdsUnitTests/function_topic_tests.h delete mode 100755 Lts/DDS/DdsUnitTests/generic_data_tests.cpp delete mode 100755 Lts/DDS/DdsUnitTests/generic_data_tests.h delete mode 100755 Lts/DDS/DdsUnitTests/initialisation.cpp delete mode 100755 Lts/DDS/DdsUnitTests/initialisation.h delete mode 100755 Lts/DDS/DdsUnitTests/log4cxx_ddsUnitTests.props delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/CMakeLists.txt delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsDataTopicTests_gtest.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsDataTopicTests_smoke.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsFunctionTopicTests_gtest.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsFunctionTopicTests_smoke.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsGenericDataTests_gtest.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/ddsGenericDataTests_smoke.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/mocks/MockDDS.cpp delete mode 100644 Lts/DDS/DdsUnitTestsPlaceholder/mocks/MockDDS.h delete mode 100755 Lts/DDS/FTC/CMakeLists.txt delete mode 100755 Lts/DDS/FTC/abstract_requester.cpp delete mode 100755 Lts/DDS/FTC/abstract_requester.h delete mode 100755 Lts/DDS/FTC/generic_ftc.cpp delete mode 100755 Lts/DDS/FTC/generic_ftc.h delete mode 100755 Lts/DDS/FTC/generic_ftc_impl.h delete mode 100755 Lts/DDS/FTC/generic_ftc_threading.cpp delete mode 100755 Lts/DDS/FTC/generic_ftc_threading.h delete mode 100755 Lts/DDS/FTS/CMakeLists.txt delete mode 100755 Lts/DDS/FTS/connext.h delete mode 100755 Lts/DDS/FTS/generic_fts.h delete mode 100755 Lts/DDS/FTS/generic_fts_impl.h delete mode 100755 Lts/DDS/FTS/generic_fts_internal.cpp delete mode 100755 Lts/DDS/FTS/generic_fts_internal.h delete mode 100755 Lts/DDS/Generic/CMakeLists.txt delete mode 100755 Lts/DDS/Generic/dynamic_data_reader.h delete mode 100755 Lts/DDS/Generic/generic_base.cpp delete mode 100755 Lts/DDS/Generic/generic_base.h delete mode 100755 Lts/DDS/Generic/generic_data_reader.h delete mode 100755 Lts/DDS/Generic/generic_data_writer.h delete mode 100755 Lts/DDS/Generic/generic_logger.cpp delete mode 100755 Lts/DDS/Generic/generic_logger.h delete mode 100755 Lts/DDS/Generic/subscriber_listener.cpp delete mode 100755 Lts/DDS/Generic/subscriber_listener.h delete mode 100755 Lts/DDS/Generic/topic_description.h delete mode 100755 Lts/DDS/Generic/topic_type_plugin.h delete mode 100755 Lts/DDS/Generic/wait_set_handler.cpp delete mode 100755 Lts/DDS/Generic/wait_set_handler.h delete mode 100755 Lts/DDS/QosProfile/CMakeLists.txt delete mode 100755 Lts/DDS/QosProfile/DDS_DefaultQos.cpp delete mode 100755 Lts/DDS/QosProfile/DDS_DefaultQos.h delete mode 100755 Lts/DDS/QosProfile/default_profile.xml delete mode 100755 Lts/DDS/QosProfile/default_profile_tablet.xml delete mode 100755 Lts/DDS/README delete mode 100755 Lts/DDS/Routing/CMakeLists.txt delete mode 100755 Lts/DDS/Routing/routing_plugin.h delete mode 100755 Lts/Proxies/TrainingAreaBase/CMakeLists.txt delete mode 100644 TetraIF/CMakeLists.txt delete mode 100644 TetraIF/CoPrtCnv/CMakeLists.txt delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvCentralTelegram.cpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvCentralTelegram.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvHexDumpHelper.cpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvHexDumpHelper.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvInjectionHeader.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvLogHeader.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvRadioTelegram.cpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvRadioTelegram.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvTelegramConverter.hpp delete mode 100644 TetraIF/CoPrtCnv/CoPrtCnvTelegramConverterNewIcd.cpp delete mode 100644 TetraIF/CoPrtCnv/tetra_telegram_ids.h delete mode 100644 TetraIF/LogView/CMakeLists.txt delete mode 100644 TetraIF/LogView/Console/CMakeLists.txt delete mode 100644 TetraIF/LogView/Console/main.cpp delete mode 100644 TetraIF/LogView/IntegerCalculator.cpp delete mode 100644 TetraIF/LogView/IntegerCalculator.hpp delete mode 100644 TetraIF/LogView/LogViewBooleanEdit.cpp delete mode 100644 TetraIF/LogView/LogViewBooleanEdit.hpp delete mode 100644 TetraIF/LogView/LogViewDateTimeEdit.cpp delete mode 100644 TetraIF/LogView/LogViewDateTimeEdit.hpp delete mode 100644 TetraIF/LogView/LogViewEnumEdit.cpp delete mode 100644 TetraIF/LogView/LogViewEnumEdit.hpp delete mode 100644 TetraIF/LogView/LogViewField.cpp delete mode 100644 TetraIF/LogView/LogViewField.hpp delete mode 100644 TetraIF/LogView/LogViewGui.cpp delete mode 100644 TetraIF/LogView/LogViewGui.hpp delete mode 100644 TetraIF/LogView/LogViewInjectionThread.cpp delete mode 100644 TetraIF/LogView/LogViewInjectionThread.hpp delete mode 100644 TetraIF/LogView/LogViewMain.cpp delete mode 100644 TetraIF/LogView/LogViewModels.cpp delete mode 100644 TetraIF/LogView/LogViewModels.hpp delete mode 100644 TetraIF/LogView/LogViewOptionDialog.cpp delete mode 100644 TetraIF/LogView/LogViewOptionDialog.hpp delete mode 100644 TetraIF/LogView/LogViewResources.qrc delete mode 100644 TetraIF/LogView/LogViewSpinBox.cpp delete mode 100644 TetraIF/LogView/LogViewSpinBox.hpp delete mode 100644 TetraIF/LogView/LogViewTabWidget.cpp delete mode 100644 TetraIF/LogView/LogViewTabWidget.hpp delete mode 100644 TetraIF/LogView/LogViewTableWidget.cpp delete mode 100644 TetraIF/LogView/LogViewTableWidget.hpp delete mode 100644 TetraIF/LogView/LogViewTelegram.cpp delete mode 100644 TetraIF/LogView/LogViewTelegram.hpp delete mode 100644 TetraIF/LogView/LogViewTelegramDialog.cpp delete mode 100644 TetraIF/LogView/LogViewTelegramDialog.hpp delete mode 100644 TetraIF/LogView/LogViewTelegramEdit.cpp delete mode 100644 TetraIF/LogView/LogViewTelegramEdit.hpp delete mode 100644 TetraIF/LogView/LogViewTelegramEditModel.cpp delete mode 100644 TetraIF/LogView/LogViewTelegramEditModel.hpp delete mode 100644 TetraIF/LogView/LogViewTelegramSelectionDialog.cpp delete mode 100644 TetraIF/LogView/LogViewTelegramSelectionDialog.hpp delete mode 100644 TetraIF/LogView/LogViewTetraTelegramsParser.cpp delete mode 100644 TetraIF/LogView/LogViewTetraTelegramsParser.hpp delete mode 100644 TetraIF/LogView/LogViewTextEdit.cpp delete mode 100644 TetraIF/LogView/LogViewTextEdit.hpp delete mode 100644 TetraIF/LogView/LogViewValueEdit.cpp delete mode 100644 TetraIF/LogView/LogViewValueEdit.hpp delete mode 100644 TetraIF/LogView/LogViewWidget.cpp delete mode 100644 TetraIF/LogView/LogViewWidget.hpp delete mode 100644 TetraIF/LogView/LogViewXdrElement.cpp delete mode 100644 TetraIF/LogView/LogViewXdrElement.hpp delete mode 100644 TetraIF/LogView/LogViewXdrParser.cpp delete mode 100644 TetraIF/LogView/LogViewXdrParser.hpp delete mode 100644 TetraIF/LogView/LogViewXdrStandardType.cpp delete mode 100644 TetraIF/LogView/LogViewXdrStandardType.hpp delete mode 100644 TetraIF/LogView/LogViewXdrStruct.cpp delete mode 100644 TetraIF/LogView/LogViewXdrStruct.hpp delete mode 100644 TetraIF/LogView/LogViewXdrType.cpp delete mode 100644 TetraIF/LogView/LogViewXdrType.hpp delete mode 100644 TetraIF/LogView/LogViewXdrValue.cpp delete mode 100644 TetraIF/LogView/LogViewXdrValue.hpp delete mode 100644 TetraIF/LogView/LogView_log4cxx.props delete mode 100644 TetraIF/LogView/image/C2P.png delete mode 100644 TetraIF/LogView/image/Central.png delete mode 100644 TetraIF/LogView/image/GreenDarkLamp.png delete mode 100644 TetraIF/LogView/image/GreenLamp.png delete mode 100644 TetraIF/LogView/image/P2C.png delete mode 100644 TetraIF/LogView/image/P2T.png delete mode 100644 TetraIF/LogView/image/P2W.png delete mode 100644 TetraIF/LogView/image/RedLamp.png delete mode 100644 TetraIF/LogView/image/ShowSend.png delete mode 100644 TetraIF/LogView/image/T2P.png delete mode 100644 TetraIF/LogView/image/TNE.png delete mode 100644 TetraIF/LogView/image/Tetra.png delete mode 100644 TetraIF/LogView/image/W2P.png delete mode 100644 TetraIF/LogView/image/Wls.png delete mode 100644 TetraIF/LogView/image/YellowDarkLamp.png delete mode 100644 TetraIF/LogView/image/YellowLamp.png delete mode 100644 TetraIF/LogView/logger.cpp delete mode 100644 TetraIF/LogView/logger.h delete mode 120000 TetraIF/LogView/zsksxd_tetraForLogView.cpp delete mode 100644 TetraIF/LogView/zsksxd_tetraForLogView.x delete mode 120000 TetraIF/LogView/zsksxd_tetraForLogView_guez.cpp delete mode 100644 TetraIF/LogView/zsksxd_tetraForLogView_guez.x delete mode 100644 TetraIF/PrtCnv/AppComponent.cpp delete mode 100644 TetraIF/PrtCnv/AppComponent.hpp delete mode 100644 TetraIF/PrtCnv/CMakeLists.txt delete mode 100644 TetraIF/PrtCnv/IAppComponent.hpp delete mode 100644 TetraIF/PrtCnv/LastTest/CMakeLists.txt delete mode 100644 TetraIF/PrtCnv/LastTest/main.cpp delete mode 100644 TetraIF/PrtCnv/LastTest/tetra_if.cpp delete mode 100644 TetraIF/PrtCnv/LastTest/tetra_if.h delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/CMakeLists.txt delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/LastTestAuswertung.pro delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/main.cpp delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/mainwindow.cpp delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/mainwindow.h delete mode 100644 TetraIF/PrtCnv/LastTestAuswertung/mainwindow.ui delete mode 100644 TetraIF/PrtCnv/PrtCnv.dat.tts delete mode 100644 TetraIF/PrtCnv/PrtCnvApplication.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvApplication.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvBroadcastMgr.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvBroadcastMgr.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvCentralThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvCentralThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvDbThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvDbThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvInjectionSocket.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvInjectionSocket.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvInjectionThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvInjectionThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvMain.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvMember.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvMember.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvRadioThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvRadioThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvRadioTrafficThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvRadioTrafficThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvSocketThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvSocketThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvStoredCentralTelegram.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvStoredRadioTelegram.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvStoredTelegram.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvTelegramLogThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvTelegramLogThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvThread.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvThread.hpp delete mode 100644 TetraIF/PrtCnv/PrtCnvThreadStarter.cpp delete mode 100644 TetraIF/PrtCnv/PrtCnvThreadStarter.hpp delete mode 100644 TetraIF/PrtCnv/TetraTestServer/CMakeLists.txt delete mode 100644 TetraIF/PrtCnv/TetraTestServer/log4cxx_tetratestserver.props delete mode 100644 TetraIF/PrtCnv/TetraTestServer/main.cpp delete mode 100644 TetraIF/PrtCnv/TetraTestServer/server.cpp delete mode 100644 TetraIF/PrtCnv/TetraTestServer/server.h delete mode 100644 TetraIF/PrtCnv/TetraTestServer/tts.txt delete mode 100644 TetraIF/PrtCnv/TetraTestServer/worker.cpp delete mode 100644 TetraIF/PrtCnv/TetraTestServer/worker.h delete mode 100644 TetraIF/PrtCnv/log4cxx_prtcnv.props delete mode 100644 TetraIF/PrtCnv/logger.cpp delete mode 100644 TetraIF/PrtCnv/logger.h delete mode 100755 Z/src/CMakeLists.txt delete mode 100755 Z/src/NGLLogger/CMakeLists.txt delete mode 100755 Z/src/NGLLogger/INGLLogger.h delete mode 100755 Z/src/NGLLogger/MurmurHash2.cpp delete mode 100755 Z/src/NGLLogger/MurmurHash2.h delete mode 100755 Z/src/NGLLogger/NGLLogger.cpp delete mode 100755 Z/src/NGLLogger/NGLLogger.h delete mode 100755 Z/src/NGLLogger/NGLLoggerBootstrap.cpp delete mode 100755 Z/src/NGLLogger/NGLLoggerBootstrap.h delete mode 100755 Z/src/NGLLogger/NGLLogger_export.h delete mode 100755 Z/src/NGLLogger/log4cxx.props delete mode 100755 Z/src/NGLLogger/test_ngllogger.cpp delete mode 100755 Z/src/QtSingleApplication/CMakeLists.txt delete mode 100755 Z/src/QtSingleApplication/QtLockedFile delete mode 100755 Z/src/QtSingleApplication/QtSingleApplication delete mode 100755 Z/src/QtSingleApplication/QtSingleCoreApplication delete mode 100755 Z/src/QtSingleApplication/qtlocalpeer.cpp delete mode 100755 Z/src/QtSingleApplication/qtlocalpeer.h delete mode 100755 Z/src/QtSingleApplication/qtlockedfile.cpp delete mode 100755 Z/src/QtSingleApplication/qtlockedfile.h delete mode 100755 Z/src/QtSingleApplication/qtlockedfile_unix.cpp delete mode 100755 Z/src/QtSingleApplication/qtlockedfile_win.cpp delete mode 100755 Z/src/QtSingleApplication/qtsingleapplication.cpp delete mode 100755 Z/src/QtSingleApplication/qtsingleapplication.h delete mode 100755 Z/src/QtSingleApplication/qtsinglecoreapplication.cpp delete mode 100755 Z/src/QtSingleApplication/qtsinglecoreapplication.h delete mode 100755 Z/src/WMS_Interface/CMakeLists.txt delete mode 100755 Z/src/WMS_Interface/IECC2WMS.h delete mode 100755 Z/src/WMS_Interface/IWMS2ECC.h delete mode 100755 Z/src/WMS_Interface/json_keys.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_equipRequirements.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_equipRequirements.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_equipmenttypes.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_equipmenttypes.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_exercises.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_exercises.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_ibase.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_playerInfo.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_playerInfo.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_registration.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_registration.h delete mode 100755 Z/src/WMS_Interface/wms_ecc_unregister.cpp delete mode 100755 Z/src/WMS_Interface/wms_ecc_unregister.h delete mode 100755 Z/src/nglapplication/CMakeLists.txt delete mode 100755 Z/src/nglapplication/ngl_qt_msg_handler.inc delete mode 100755 Z/src/nglapplication/ngl_signal_handler.inc delete mode 100755 Z/src/nglapplication/nglapplication.cpp delete mode 100755 Z/src/nglapplication/nglapplication.h delete mode 100755 Z/src/nglapplication/nglcoreapplication.cpp delete mode 100755 Z/src/nglapplication/nglcoreapplication.h delete mode 100755 Z/src/nglapplication/nglguiapplication.cpp delete mode 100755 Z/src/nglapplication/nglguiapplication.h delete mode 100755 Z/src/nglapplication/nglloggerhandler.cpp delete mode 100755 Z/src/nglapplication/nglloggerhandler.h delete mode 100755 Z/src/nglapplication/nglsingleapplication.cpp delete mode 100755 Z/src/nglapplication/nglsingleapplication.h delete mode 100755 Z/src/nglapplication/nglsinglecoreapplication.cpp delete mode 100755 Z/src/nglapplication/nglsinglecoreapplication.h delete mode 100755 Z/src/nglcompression/CMakeLists.txt delete mode 100755 Z/src/nglcompression/nglcomp_bzip2iodevice.cpp delete mode 100755 Z/src/nglcompression/nglcomp_bzip2iodevice.h delete mode 100755 Z/src/nglcompression/nglcomp_compress.cpp delete mode 100755 Z/src/nglcompression/nglcomp_compress.h delete mode 100755 Z/src/nglcompression/nglcomp_export.h delete mode 100755 Z/src/nglcompression/nglcomp_tarfilter.cpp delete mode 100755 Z/src/nglcompression/nglcomp_tarfilter.h delete mode 100755 Z/src/nglcompression/nglcomp_test.cpp delete mode 100755 Z/src/nglcompression/nglcomp_test.h delete mode 100755 Z/src/ngldb/CMakeLists.txt delete mode 100755 Z/src/ngldb/db_service_handler.h delete mode 100755 Z/src/ngldb/dbcommon.h delete mode 100755 Z/src/ngldb/dbconnection.cpp delete mode 100755 Z/src/ngldb/dbconnection.h delete mode 100755 Z/src/ngldb/dbtransaction.cpp delete mode 100755 Z/src/ngldb/dbtransaction.h delete mode 100755 Z/src/ngldb/ngldb_export.h delete mode 100755 Z/src/ngldb/ngldb_logger.cpp delete mode 100755 Z/src/ngldb/ngldb_logger.h delete mode 100755 Z/src/ngldb/nsqlquery.cpp delete mode 100755 Z/src/ngldb/nsqlquery.h delete mode 100755 Z/src/ngldb/sql_query_janitor.cpp delete mode 100755 Z/src/ngldb/sql_query_janitor.h delete mode 100755 Z/src/nglsettings/CMakeLists.txt delete mode 100755 Z/src/nglsettings/nglsettings_connections.cpp delete mode 100755 Z/src/nglsettings/nglsettings_connections.h delete mode 100755 Z/src/nglsettings/nglsettings_export.h delete mode 100755 Z/src/nglsettings/nglsettings_settings.cpp delete mode 100755 Z/src/nglsettings/nglsettings_settings.h delete mode 100755 Z/src/ngltools/CMakeLists.txt delete mode 100755 Z/src/ngltools/ngltools_bitset.h delete mode 100755 Z/src/ngltools/ngltools_convert.cpp delete mode 100755 Z/src/ngltools/ngltools_convert.h delete mode 100755 Z/src/ngltools/ngltools_dtg.cpp delete mode 100755 Z/src/ngltools/ngltools_dtg.h delete mode 100755 Z/src/ngltools/ngltools_export.h delete mode 100755 Z/src/ngltools/ngltools_filesystem.cpp delete mode 100755 Z/src/ngltools/ngltools_filesystem.h delete mode 100755 Z/src/ngltools/ngltools_helpevent.cpp delete mode 100755 Z/src/ngltools/ngltools_helpevent.h delete mode 100644 Z/src/ngltools/tests/NGLToolsQtSmokeTest.cpp delete mode 100755 Z/src/ngltranslator/CMakeLists.txt delete mode 100755 Z/src/ngltranslator/ngltranslator.cpp delete mode 100755 Z/src/ngltranslator/ngltranslator.h delete mode 100755 Z/src/ngltranslator/ngltranslator_export.h delete mode 100755 Z/src/ngltranslator/ngltranslator_p.h delete mode 100755 Z/src/nglwidgets/CMakeLists.txt delete mode 100755 Z/src/nglwidgets/icons/16x16/LICENSE delete mode 100755 Z/src/nglwidgets/icons/16x16/application-pdf.png delete mode 100755 Z/src/nglwidgets/icons/16x16/arrow-left.png delete mode 100755 Z/src/nglwidgets/icons/16x16/arrow-right.png delete mode 100755 Z/src/nglwidgets/icons/16x16/audio-x-generic.png delete mode 100755 Z/src/nglwidgets/icons/16x16/bg.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/camera-photo.png delete mode 100755 Z/src/nglwidgets/icons/16x16/configure.png delete mode 100755 Z/src/nglwidgets/icons/16x16/dialog-close.png delete mode 100755 Z/src/nglwidgets/icons/16x16/dialog-ok-apply.png delete mode 100755 Z/src/nglwidgets/icons/16x16/dialog-ok.png delete mode 100755 Z/src/nglwidgets/icons/16x16/dirs.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-edit.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-import.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-new.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-open-data.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-open.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-preview.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-print.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-properties.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-save-all.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-save-as.png delete mode 100755 Z/src/nglwidgets/icons/16x16/document-save.png delete mode 100755 Z/src/nglwidgets/icons/16x16/download.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-bezier-curves.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-ellipse.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-line.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-polygon.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-polyline.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-rectangle.png delete mode 100755 Z/src/nglwidgets/icons/16x16/draw-text.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-clear-locationbar-rtl.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-copy.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-cut.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-delete.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-paste.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-redo.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-rename.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-select.png delete mode 100755 Z/src/nglwidgets/icons/16x16/edit-undo.png delete mode 100755 Z/src/nglwidgets/icons/16x16/fill-color.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-justify-center.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-justify-fill.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-justify-left.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-justify-right.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-stroke-color.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-text-bold.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-text-italic.png delete mode 100755 Z/src/nglwidgets/icons/16x16/format-text-underline.png delete mode 100755 Z/src/nglwidgets/icons/16x16/freu.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/go-down.png delete mode 100755 Z/src/nglwidgets/icons/16x16/go-home.png delete mode 100755 Z/src/nglwidgets/icons/16x16/go-next.png delete mode 100755 Z/src/nglwidgets/icons/16x16/go-previous.png delete mode 100755 Z/src/nglwidgets/icons/16x16/go-up.png delete mode 100755 Z/src/nglwidgets/icons/16x16/help-about.png delete mode 100755 Z/src/nglwidgets/icons/16x16/help-contents.png delete mode 100755 Z/src/nglwidgets/icons/16x16/image-x-generic.png delete mode 100755 Z/src/nglwidgets/icons/16x16/internet-mail.png delete mode 100755 Z/src/nglwidgets/icons/16x16/led-blue-on.png delete mode 100755 Z/src/nglwidgets/icons/16x16/led-gray-on.png delete mode 100755 Z/src/nglwidgets/icons/16x16/led-green-on.png delete mode 100755 Z/src/nglwidgets/icons/16x16/led-red-on.png delete mode 100755 Z/src/nglwidgets/icons/16x16/led-yellow-on.png delete mode 100755 Z/src/nglwidgets/icons/16x16/list-add-user.png delete mode 100755 Z/src/nglwidgets/icons/16x16/list-add.png delete mode 100755 Z/src/nglwidgets/icons/16x16/list-remove-user.png delete mode 100755 Z/src/nglwidgets/icons/16x16/list-remove.png delete mode 100755 Z/src/nglwidgets/icons/16x16/mail-mark-read.png delete mode 100755 Z/src/nglwidgets/icons/16x16/mail-mark-unread.png delete mode 100755 Z/src/nglwidgets/icons/16x16/mail-message-new.png delete mode 100755 Z/src/nglwidgets/icons/16x16/mail-send.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-eject.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-flash.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-play-faster.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-play-normal.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-play-slower.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-playback-pause.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-playback-start.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-playback-stop.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-record.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-seek-backward.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-seek-forward.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-skip-backward.png delete mode 100755 Z/src/nglwidgets/icons/16x16/media-skip-forward.png delete mode 100755 Z/src/nglwidgets/icons/16x16/oO.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/object-group.png delete mode 100755 Z/src/nglwidgets/icons/16x16/object-ungroup.png delete mode 100755 Z/src/nglwidgets/icons/16x16/ooo-calc.png delete mode 100755 Z/src/nglwidgets/icons/16x16/ooo-impress.png delete mode 100755 Z/src/nglwidgets/icons/16x16/ooo-writer.png delete mode 100755 Z/src/nglwidgets/icons/16x16/player_status.png delete mode 100755 Z/src/nglwidgets/icons/16x16/preferences-desktop-font.png delete mode 100755 Z/src/nglwidgets/icons/16x16/preferences-web-browser-cache.png delete mode 100755 Z/src/nglwidgets/icons/16x16/printer.png delete mode 100755 Z/src/nglwidgets/icons/16x16/process-stop.png delete mode 100755 Z/src/nglwidgets/icons/16x16/rde.ico delete mode 100755 Z/src/nglwidgets/icons/16x16/rde_16x16.png delete mode 100755 Z/src/nglwidgets/icons/16x16/system-help.png delete mode 100755 Z/src/nglwidgets/icons/16x16/text-plain.png delete mode 100755 Z/src/nglwidgets/icons/16x16/transform-move.png delete mode 100755 Z/src/nglwidgets/icons/16x16/transform-rotate.png delete mode 100755 Z/src/nglwidgets/icons/16x16/transform-scale.png delete mode 100755 Z/src/nglwidgets/icons/16x16/traurig.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/video-x-generic.png delete mode 100755 Z/src/nglwidgets/icons/16x16/view-fullscreen.png delete mode 100755 Z/src/nglwidgets/icons/16x16/view-statistics.png delete mode 100755 Z/src/nglwidgets/icons/16x16/weinend.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/x-office-address-book.png delete mode 100755 Z/src/nglwidgets/icons/16x16/yellow-off-16.png delete mode 100755 Z/src/nglwidgets/icons/16x16/yellow-on-16.png delete mode 100755 Z/src/nglwidgets/icons/16x16/zoom-in.png delete mode 100755 Z/src/nglwidgets/icons/16x16/zoom-out.png delete mode 100755 Z/src/nglwidgets/icons/16x16/zunge.gif delete mode 100755 Z/src/nglwidgets/icons/16x16/zwinker.gif delete mode 100755 Z/src/nglwidgets/icons/22x22/dialogbuttonbox.png delete mode 100755 Z/src/nglwidgets/icons/22x22/edit-redo.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-justify-center.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-justify-fill.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-justify-left.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-justify-right.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-stroke-color.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-text-bold.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-text-italic.png delete mode 100755 Z/src/nglwidgets/icons/22x22/format-text-underline.png delete mode 100755 Z/src/nglwidgets/icons/22x22/kdialog.png delete mode 100755 Z/src/nglwidgets/icons/22x22/klineedit.png delete mode 100755 Z/src/nglwidgets/icons/22x22/layer.png delete mode 100755 Z/src/nglwidgets/icons/22x22/lkm_einsatz.png delete mode 100755 Z/src/nglwidgets/icons/22x22/pushbutton.png delete mode 100755 Z/src/nglwidgets/icons/22x22/scricon.png delete mode 100755 Z/src/nglwidgets/icons/32x32/database.png delete mode 100755 Z/src/nglwidgets/icons/32x32/hand_grenade_locations.png delete mode 100755 Z/src/nglwidgets/icons/32x32/hideshow.png delete mode 100755 Z/src/nglwidgets/icons/32x32/marker-end.png delete mode 100755 Z/src/nglwidgets/icons/32x32/marker-start.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-eject.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-backward.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-faster.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-faster.svg delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-normal.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-normal.svg delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-slower.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-slower.svg delete mode 100755 Z/src/nglwidgets/icons/32x32/media-play-slower.svg.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-playback-pause.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-playback-start-single.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-playback-start.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-playback-stop.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-record.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-seek-backward.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-seek-forward.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-skip-backward.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-skip-forward.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-vcr-nopause.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-vcr-normal.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-vcr-repeat.png delete mode 100755 Z/src/nglwidgets/icons/32x32/media-vcr-withpause.png delete mode 100755 Z/src/nglwidgets/icons/32x32/service_message.png delete mode 100755 Z/src/nglwidgets/icons/32x32/showhide.png delete mode 100755 Z/src/nglwidgets/icons/32x32/wireless.png delete mode 100755 Z/src/nglwidgets/icons/LICENSE delete mode 100755 Z/src/nglwidgets/icons/png/add.png delete mode 100755 Z/src/nglwidgets/icons/png/add_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/add_tactical_sign_act.png delete mode 100755 Z/src/nglwidgets/icons/png/add_tactical_sign_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/add_text_act.png delete mode 100755 Z/src/nglwidgets/icons/png/add_text_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/address_book.png delete mode 100755 Z/src/nglwidgets/icons/png/big_letters_act.png delete mode 100755 Z/src/nglwidgets/icons/png/big_letters_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/camera_arrow_down.png delete mode 100755 Z/src/nglwidgets/icons/png/camera_arrow_left.png delete mode 100755 Z/src/nglwidgets/icons/png/camera_arrow_right.png delete mode 100755 Z/src/nglwidgets/icons/png/camera_arrow_up.png delete mode 100755 Z/src/nglwidgets/icons/png/center_object_in_map_act.png delete mode 100755 Z/src/nglwidgets/icons/png/center_object_in_map_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/clear_plan_act.png delete mode 100755 Z/src/nglwidgets/icons/png/clear_plan_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/copy_exercise.png delete mode 100755 Z/src/nglwidgets/icons/png/copy_exercise_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/delete.png delete mode 100755 Z/src/nglwidgets/icons/png/delete_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/delete_exercise.png delete mode 100755 Z/src/nglwidgets/icons/png/delete_exercise_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/delete_graphic_eass.png delete mode 100755 Z/src/nglwidgets/icons/png/delete_graphic_eass_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/deleteall.png delete mode 100755 Z/src/nglwidgets/icons/png/download.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_ellipse_act.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_ellipse_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_line.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_line_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_polygon_act.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_polygon_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_rectangle_act.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_rectangle_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_spline.png delete mode 100755 Z/src/nglwidgets/icons/png/draw_spline_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/eass_line.png delete mode 100755 Z/src/nglwidgets/icons/png/eass_line_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/eass_polygon.png delete mode 100755 Z/src/nglwidgets/icons/png/eass_polygon_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/edit_element_act.png delete mode 100755 Z/src/nglwidgets/icons/png/edit_element_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/edith_graphic_eass.png delete mode 100755 Z/src/nglwidgets/icons/png/edith_graphic_eass_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/fast_playback.png delete mode 100755 Z/src/nglwidgets/icons/png/fast_playback_backward.png delete mode 100755 Z/src/nglwidgets/icons/png/fast_playback_backward_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/fast_playback_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/fill_colour_act.png delete mode 100755 Z/src/nglwidgets/icons/png/fill_colour_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/folder_open.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_audio_control_act.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_audio_control_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_radio_error_act.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_radio_error_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_remote_control_act.png delete mode 100755 Z/src/nglwidgets/icons/png/kosy_remote_control_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/line_color.png delete mode 100755 Z/src/nglwidgets/icons/png/marker_left.png delete mode 100755 Z/src/nglwidgets/icons/png/marker_left_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/marker_right.png delete mode 100755 Z/src/nglwidgets/icons/png/marker_right_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/messages_new.png delete mode 100755 Z/src/nglwidgets/icons/png/missionmarch.png delete mode 100755 Z/src/nglwidgets/icons/png/missionmarch_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/normal_letters_act.png delete mode 100755 Z/src/nglwidgets/icons/png/normal_letters_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/ok.png delete mode 100755 Z/src/nglwidgets/icons/png/ok_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/paste_exercise.png delete mode 100755 Z/src/nglwidgets/icons/png/paste_exercise_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/pause_playback.png delete mode 100755 Z/src/nglwidgets/icons/png/pause_playback_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/polygonmap.png delete mode 100755 Z/src/nglwidgets/icons/png/polygonmap_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/position_map.png delete mode 100755 Z/src/nglwidgets/icons/png/position_map_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/preview.png delete mode 100755 Z/src/nglwidgets/icons/png/printer.png delete mode 100755 Z/src/nglwidgets/icons/png/redo.png delete mode 100755 Z/src/nglwidgets/icons/png/redo_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/remove.png delete mode 100755 Z/src/nglwidgets/icons/png/remove_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/rename_exercise.png delete mode 100755 Z/src/nglwidgets/icons/png/rename_exercise_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/save.png delete mode 100755 Z/src/nglwidgets/icons/png/save_as.png delete mode 100755 Z/src/nglwidgets/icons/png/settings.png delete mode 100755 Z/src/nglwidgets/icons/png/settings_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/start_playback.png delete mode 100755 Z/src/nglwidgets/icons/png/start_playback_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/stop.png delete mode 100755 Z/src/nglwidgets/icons/png/stop_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/stop_playback.png delete mode 100755 Z/src/nglwidgets/icons/png/stop_playback_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/thick_line_act.png delete mode 100755 Z/src/nglwidgets/icons/png/thick_line_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/turn_left.png delete mode 100755 Z/src/nglwidgets/icons/png/turn_left_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/undo_act.png delete mode 100755 Z/src/nglwidgets/icons/png/undo_act_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/zoom_in.png delete mode 100755 Z/src/nglwidgets/icons/png/zoom_in_disabled.png delete mode 100755 Z/src/nglwidgets/icons/png/zoom_out.png delete mode 100755 Z/src/nglwidgets/icons/png/zoom_out_disabled.png delete mode 100755 Z/src/nglwidgets/icons/svg/3d_viewer.svg delete mode 100755 Z/src/nglwidgets/icons/svg/3d_viewer_config.svg delete mode 100755 Z/src/nglwidgets/icons/svg/3d_viewer_config_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/3d_viewer_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/about.svg delete mode 100755 Z/src/nglwidgets/icons/svg/about_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/ac_adapter_plugged_in.svg delete mode 100755 Z/src/nglwidgets/icons/svg/access_rights.svg delete mode 100755 Z/src/nglwidgets/icons/svg/access_rights_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/aircraft_sim.svg delete mode 100755 Z/src/nglwidgets/icons/svg/announcement.svg delete mode 100755 Z/src/nglwidgets/icons/svg/announcement_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/announcement_menu.svg delete mode 100755 Z/src/nglwidgets/icons/svg/announcement_menu_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/application-exit.svg delete mode 100755 Z/src/nglwidgets/icons/svg/arrow_back.svg delete mode 100755 Z/src/nglwidgets/icons/svg/arrow_back_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/artillery_firings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/artillery_firings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/audio-volume-high.svg delete mode 100755 Z/src/nglwidgets/icons/svg/audio-volume-low.svg delete mode 100755 Z/src/nglwidgets/icons/svg/audio-volume-medium.svg delete mode 100755 Z/src/nglwidgets/icons/svg/audio-volume-muted.svg delete mode 100755 Z/src/nglwidgets/icons/svg/battery_mode.svg delete mode 100755 Z/src/nglwidgets/icons/svg/camera_control.svg delete mode 100755 Z/src/nglwidgets/icons/svg/camera_control_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/change_password.svg delete mode 100755 Z/src/nglwidgets/icons/svg/change_password_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/chat.svg delete mode 100755 Z/src/nglwidgets/icons/svg/chat_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/chemical_agents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/chemical_agents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/config_gobal_alarms.svg delete mode 100755 Z/src/nglwidgets/icons/svg/config_gobal_alarms_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_arrow.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_buildings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_buildings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_line_of_sight.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_line_of_sight_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_symbol_attributes.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_symbol_attributes_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_tunnels.svg delete mode 100755 Z/src/nglwidgets/icons/svg/configure_tunnels_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_distance_measure.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_distance_measure_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_evaluation_topic.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_image_topic.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_lecture_topic.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_pdf_topic.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_snapshot.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_snapshot_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_terrain_profile.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_terrain_profile_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_thp.svg delete mode 100755 Z/src/nglwidgets/icons/svg/create_thp_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/danger_areas.svg delete mode 100755 Z/src/nglwidgets/icons/svg/debug-execute-from-cursor.svg delete mode 100755 Z/src/nglwidgets/icons/svg/debug-execute-to-cursor.svg delete mode 100755 Z/src/nglwidgets/icons/svg/default_operator_rights.svg delete mode 100755 Z/src/nglwidgets/icons/svg/default_operator_rights_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_border_lines.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_border_lines_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_force_templates.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_force_templates_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_movement_simulation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_movement_simulation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_player_types.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_player_types_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_position_system.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_position_system_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_presentation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_presentation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_tactical_symbols.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_tactical_symbols_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_teams.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_teams_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_training_cycles.svg delete mode 100755 Z/src/nglwidgets/icons/svg/define_training_cycles_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/delete_thp.svg delete mode 100755 Z/src/nglwidgets/icons/svg/delete_thp_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/dialog-close.svg delete mode 100755 Z/src/nglwidgets/icons/svg/dialog-ok-apply.svg delete mode 100755 Z/src/nglwidgets/icons/svg/dialog-ok.svg delete mode 100755 Z/src/nglwidgets/icons/svg/display_oc_evaluations.svg delete mode 100755 Z/src/nglwidgets/icons/svg/display_oc_evaluations_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/display_reports.svg delete mode 100755 Z/src/nglwidgets/icons/svg/display_reports_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/document-edit.svg delete mode 100755 Z/src/nglwidgets/icons/svg/document-new.svg delete mode 100755 Z/src/nglwidgets/icons/svg/document-open-data.svg delete mode 100755 Z/src/nglwidgets/icons/svg/document-open.svg delete mode 100755 Z/src/nglwidgets/icons/svg/document-properties.svg delete mode 100755 Z/src/nglwidgets/icons/svg/documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/documents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/download.svg delete mode 100755 Z/src/nglwidgets/icons/svg/draw-bezier-curves.svg delete mode 100755 Z/src/nglwidgets/icons/svg/edit-cut.svg delete mode 100755 Z/src/nglwidgets/icons/svg/edit_evaluation_marker.svg delete mode 100755 Z/src/nglwidgets/icons/svg/edit_evaluation_marker_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/electronical_warfare_measures.svg delete mode 100755 Z/src/nglwidgets/icons/svg/electronical_warfare_measures_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/evaluate.svg delete mode 100755 Z/src/nglwidgets/icons/svg/evaluate_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/evaluation_elements.svg delete mode 100755 Z/src/nglwidgets/icons/svg/evaluation_elements_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/execute_presentation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/execute_presentation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/exercise_force.svg delete mode 100755 Z/src/nglwidgets/icons/svg/exercise_force_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/exercise_purpose.svg delete mode 100755 Z/src/nglwidgets/icons/svg/exercise_purpose_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/export_documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/export_documents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/fill_pattern.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_barriers.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_barriers_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_border_lines.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_border_lines_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_collapse_expand.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_collapse_expand_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_effect_simulation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_effect_simulation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_firings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_firings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_line_of_sight.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_line_of_sight_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_map_settings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_map_settings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_map_settings_load.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_map_settings_load_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_maps_settings_save.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_maps_settings_save_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_nbc_agents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_nbc_agents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_nc_agents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_nc_agents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_player.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_player_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_teams.svg delete mode 100755 Z/src/nglwidgets/icons/svg/filter_teams_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/fixed_video_announcement.svg delete mode 100755 Z/src/nglwidgets/icons/svg/fixed_video_announcement_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_composition.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_composition_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_organization.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_organization_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_player_configuration.svg delete mode 100755 Z/src/nglwidgets/icons/svg/force_player_configuration_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/freq_plan.svg delete mode 100755 Z/src/nglwidgets/icons/svg/function_key_toolbar.svg delete mode 100755 Z/src/nglwidgets/icons/svg/function_key_toolbar_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/getpos.svg delete mode 100755 Z/src/nglwidgets/icons/svg/global_alarms_history.svg delete mode 100755 Z/src/nglwidgets/icons/svg/global_alarms_history_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/global_exercise_settings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/global_exercise_settings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/go-home.svg delete mode 100755 Z/src/nglwidgets/icons/svg/hand_grenade_locations.svg delete mode 100755 Z/src/nglwidgets/icons/svg/help_index.svg delete mode 100755 Z/src/nglwidgets/icons/svg/help_index_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_buildings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_buildings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_documents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_images.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_images_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_msdl.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_pdf_document.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_pdf_document_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_video.svg delete mode 100755 Z/src/nglwidgets/icons/svg/import_video_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/insert_script.svg delete mode 100755 Z/src/nglwidgets/icons/svg/karte.svg delete mode 100755 Z/src/nglwidgets/icons/svg/layer.svg delete mode 100755 Z/src/nglwidgets/icons/svg/line_styles.svg delete mode 100755 Z/src/nglwidgets/icons/svg/list-add-user.svg delete mode 100755 Z/src/nglwidgets/icons/svg/list-remove-user.svg delete mode 100755 Z/src/nglwidgets/icons/svg/load.svg delete mode 100755 Z/src/nglwidgets/icons/svg/load_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/load_global_filter.svg delete mode 100755 Z/src/nglwidgets/icons/svg/load_global_filter_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/logout.svg delete mode 100755 Z/src/nglwidgets/icons/svg/logout_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/map_context_menu.svg delete mode 100755 Z/src/nglwidgets/icons/svg/map_context_menu_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/map_settings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/map_settings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/marke.svg delete mode 100755 Z/src/nglwidgets/icons/svg/marker-end.svg delete mode 100755 Z/src/nglwidgets/icons/svg/marker-start.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-flash.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-play-backward.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-play-slower.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-record.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-seek-backward.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-seek-forward.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-skip-backward.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-skip-forward.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-vcr-nopause.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-vcr-normal.svg delete mode 100755 Z/src/nglwidgets/icons/svg/media-vcr-withpause.svg delete mode 100755 Z/src/nglwidgets/icons/svg/messages.svg delete mode 100755 Z/src/nglwidgets/icons/svg/messages_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/metereological_data.svg delete mode 100755 Z/src/nglwidgets/icons/svg/metereological_data_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mgrs_grid.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mgrs_grid_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mine_barrier_corridor.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mine_barrier_corridor_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mine_barriers.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mine_barriers_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mini_map.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mini_map_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mlvs_control.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mobile_video.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mobile_video_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mortar_ammunition_administration.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mortar_ammunition_administration_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mortar_ammunition_firings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/mortar_ammunition_firings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/network-wireless.svg delete mode 100755 Z/src/nglwidgets/icons/svg/new_documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/new_mail_indicator.svg delete mode 100755 Z/src/nglwidgets/icons/svg/nuclear_agents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/object-group.svg delete mode 100755 Z/src/nglwidgets/icons/svg/object-ungroup.svg delete mode 100755 Z/src/nglwidgets/icons/svg/objects_define_areas.svg delete mode 100755 Z/src/nglwidgets/icons/svg/objects_define_areas_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_aar_preparation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_aar_preparation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_composition.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_composition_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_evaluation_schemes.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_evaluation_schemes_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_player_configuration.svg delete mode 100755 Z/src/nglwidgets/icons/svg/oc_player_configuration_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/operator_administration.svg delete mode 100755 Z/src/nglwidgets/icons/svg/operator_administration_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/organize_documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/organize_documents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/organize_library.svg delete mode 100755 Z/src/nglwidgets/icons/svg/organize_library_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/overlay_plans.svg delete mode 100755 Z/src/nglwidgets/icons/svg/overlay_plans_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/overlays.svg delete mode 100755 Z/src/nglwidgets/icons/svg/phase_def.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_force_collapse.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_force_expand.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_force_left.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_force_right.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_markup_3d.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_movementtrack.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_nbc.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_set_player_mode.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_show_bite.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_show_building.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_tact_status_history.svg delete mode 100755 Z/src/nglwidgets/icons/svg/pi_type_weaponrange.svg delete mode 100755 Z/src/nglwidgets/icons/svg/planned_training_cycles.svg delete mode 100755 Z/src/nglwidgets/icons/svg/plans.svg delete mode 100755 Z/src/nglwidgets/icons/svg/plans_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_audio.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_audio_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_situation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_situation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_video.svg delete mode 100755 Z/src/nglwidgets/icons/svg/playback_video_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/player_equipment.svg delete mode 100755 Z/src/nglwidgets/icons/svg/player_equipment_check.svg delete mode 100755 Z/src/nglwidgets/icons/svg/player_equipment_check_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/player_mode.svg delete mode 100755 Z/src/nglwidgets/icons/svg/preferences-web-browser-cache.svg delete mode 100755 Z/src/nglwidgets/icons/svg/prepare_thp.svg delete mode 100755 Z/src/nglwidgets/icons/svg/prepare_thp_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/presets.svg delete mode 100755 Z/src/nglwidgets/icons/svg/presets_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/primary_map_position.svg delete mode 100755 Z/src/nglwidgets/icons/svg/primary_map_position_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/primary_map_toolbar.svg delete mode 100755 Z/src/nglwidgets/icons/svg/primary_map_toolbar_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/proj_audio.svg delete mode 100755 Z/src/nglwidgets/icons/svg/projection.svg delete mode 100755 Z/src/nglwidgets/icons/svg/raster.svg delete mode 100755 Z/src/nglwidgets/icons/svg/raster_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_ammunition_left.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_ammunition_left_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_ammunition_resupply.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_ammunition_resupply_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_awes.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_awes_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_combat_effectiveness.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_combat_effectiveness_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_combat_power.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_combat_power_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_explosive_devices.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_explosive_devices_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_maintenance.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_maintenance_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_mine_barrier.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_mine_barrier_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_mortar_firings.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_mortar_firings_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_nbc_events.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_nbc_events_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_nc_events.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_nc_events_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_treatment.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_treatment_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_treatmentmaintanance.svg delete mode 100755 Z/src/nglwidgets/icons/svg/report_treatmentmaintanance_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/restart.svg delete mode 100755 Z/src/nglwidgets/icons/svg/save_preferences.svg delete mode 100755 Z/src/nglwidgets/icons/svg/save_preferences_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/screenshot.svg delete mode 100755 Z/src/nglwidgets/icons/svg/screenshot_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/secondary_map_position.svg delete mode 100755 Z/src/nglwidgets/icons/svg/secondary_map_position_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/secondary_map_toolbar.svg delete mode 100755 Z/src/nglwidgets/icons/svg/secondary_map_toolbar_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_player.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_player_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_training_areas.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_training_areas_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_training_cylces.svg delete mode 100755 Z/src/nglwidgets/icons/svg/select_training_cylces_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/send_player_message.svg delete mode 100755 Z/src/nglwidgets/icons/svg/send_player_message_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_exercise_process.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_exercise_process_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_force_visibility.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_force_visibility_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_home_transparency.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_home_transparency_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_marker_situation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_marker_situation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_marker_video.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_marker_video_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_markers_audio.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_markers_audio_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_player_mode.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_player_mode_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_player_status.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_player_status_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_view_position.svg delete mode 100755 Z/src/nglwidgets/icons/svg/set_view_position_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_building_configuration.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_building_configuration_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_distance_measure.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_distance_measure_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_line_of_sight.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_line_of_sight_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_terrain_profile.svg delete mode 100755 Z/src/nglwidgets/icons/svg/show_terrain_profile_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/simulated_players.svg delete mode 100755 Z/src/nglwidgets/icons/svg/simulated_players_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/situation.svg delete mode 100755 Z/src/nglwidgets/icons/svg/situation_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/status_bar.svg delete mode 100755 Z/src/nglwidgets/icons/svg/status_bar_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/support_documents.svg delete mode 100755 Z/src/nglwidgets/icons/svg/support_documents_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_display.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_display_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_to_primary_map.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_to_primary_map_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_to_sec_map.svg delete mode 100755 Z/src/nglwidgets/icons/svg/switch_to_sec_map_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/symbol_display.svg delete mode 100755 Z/src/nglwidgets/icons/svg/symbol_display_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/system_status.svg delete mode 100755 Z/src/nglwidgets/icons/svg/system_status_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/tactical_organization.svg delete mode 100755 Z/src/nglwidgets/icons/svg/tactical_organization_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/tactical_symbol.svg delete mode 100755 Z/src/nglwidgets/icons/svg/tactical_symbol_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/target.svg delete mode 100755 Z/src/nglwidgets/icons/svg/terminal_window.svg delete mode 100755 Z/src/nglwidgets/icons/svg/terminal_window_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/toolbar_editor.svg delete mode 100755 Z/src/nglwidgets/icons/svg/toolbar_editor_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/translation_editor.svg delete mode 100755 Z/src/nglwidgets/icons/svg/update_player_position.svg delete mode 100755 Z/src/nglwidgets/icons/svg/update_player_position_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/video_menu.svg delete mode 100755 Z/src/nglwidgets/icons/svg/video_menu_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/view-statistics.svg delete mode 100755 Z/src/nglwidgets/icons/svg/visible_teams.svg delete mode 100755 Z/src/nglwidgets/icons/svg/voice_communication.svg delete mode 100755 Z/src/nglwidgets/icons/svg/voice_communication_access.svg delete mode 100755 Z/src/nglwidgets/icons/svg/voice_communication_access_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/voice_communication_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/weapon_firing.svg delete mode 100755 Z/src/nglwidgets/icons/svg/weapon_firing_disabled.svg delete mode 100755 Z/src/nglwidgets/icons/svg/wireless.svg delete mode 100755 Z/src/nglwidgets/ngldialog.cpp delete mode 100755 Z/src/nglwidgets/ngldialog.h delete mode 100755 Z/src/nglwidgets/ngldialogbuttonbox.cpp delete mode 100755 Z/src/nglwidgets/ngldialogbuttonbox.h delete mode 100755 Z/src/nglwidgets/nglmainwindow.cpp delete mode 100755 Z/src/nglwidgets/nglmainwindow.h delete mode 100755 Z/src/nglwidgets/nglpdfdocument.cpp delete mode 100755 Z/src/nglwidgets/nglpdfdocument.h delete mode 100755 Z/src/nglwidgets/nglpdfpage.cpp delete mode 100755 Z/src/nglwidgets/nglpdfpage.h delete mode 100755 Z/src/nglwidgets/nglpdfwidget.cpp delete mode 100755 Z/src/nglwidgets/nglpdfwidget.h delete mode 100755 Z/src/nglwidgets/nglpdfwidget.ui delete mode 100755 Z/src/nglwidgets/nglresources.qrc delete mode 100755 Z/src/nglwidgets/nglstandardbutton.cpp delete mode 100755 Z/src/nglwidgets/nglstandardbutton.h delete mode 100755 Z/src/nglwidgets/ngltreeitem.cpp delete mode 100755 Z/src/nglwidgets/ngltreeitem.h delete mode 100755 Z/src/nglwidgets/ngltreemodel.cpp delete mode 100755 Z/src/nglwidgets/ngltreemodel.h delete mode 100755 Z/src/nglwidgets/nglwidgets_export.h delete mode 100644 Z/src/nglwidgets/rme_light_style.qrc delete mode 100644 Z/src/nglwidgets/test_nglpdfwidget.cpp delete mode 100755 Z/src/qsql_informix/CMakeLists.txt delete mode 100755 Z/src/qsql_informix/FindCrypt.cmake delete mode 100755 Z/src/qsql_informix/FindIClientSDK.cmake delete mode 100755 Z/src/qsql_informix/qsql_informix.cpp delete mode 100755 Z/src/qsql_informix/qsql_informix.h delete mode 100755 Z/src/qsql_informix/qsql_informix.json delete mode 100755 Z/src/qsql_informix/qsql_informix_plugin.cpp delete mode 100755 Z/src/qsql_informix/qsql_informix_test.cpp delete mode 100755 Z/src/qsql_informix/qsql_informix_utils.cpp delete mode 100755 Z/src/qsql_informix/qsql_informix_utils.h delete mode 100755 Z/src/tcpserver/CMakeLists.txt delete mode 100755 Z/src/tcpserver/cbi_help.cpp delete mode 100755 Z/src/tcpserver/cbi_help.h delete mode 100755 Z/src/tcpserver/cbi_pingpong.cpp delete mode 100755 Z/src/tcpserver/cbi_pingpong.h delete mode 100755 Z/src/tcpserver/cbi_quit.cpp delete mode 100755 Z/src/tcpserver/cbi_quit.h delete mode 100755 Z/src/tcpserver/cbi_recvfile.cpp delete mode 100755 Z/src/tcpserver/cbi_recvfile.h delete mode 100755 Z/src/tcpserver/cbi_sendfile.cpp delete mode 100755 Z/src/tcpserver/cbi_sendfile.h delete mode 100755 Z/src/tcpserver/cbi_shutdown.cpp delete mode 100755 Z/src/tcpserver/cbi_shutdown.h delete mode 100755 Z/src/tcpserver/commandinterface.cpp delete mode 100755 Z/src/tcpserver/commandinterface.h delete mode 100755 Z/src/tcpserver/tcpserver.cpp delete mode 100755 Z/src/tcpserver/tcpserver.h delete mode 100755 Z/src/tcpserver/tcpserver_export.h delete mode 100755 Z/src/tcpserver/tcpserver_logger.h delete mode 100755 Z/src/tcpserver/tcpserver_test.cpp delete mode 100755 Z/src/tcpserver/workerbase.cpp delete mode 100755 Z/src/tcpserver/workerbase.h delete mode 100644 app/README.md delete mode 100644 app/migration_pipeline/__init__.py delete mode 100644 app/migration_pipeline/api_client.py delete mode 100644 app/migration_pipeline/build_runner.py delete mode 100644 app/migration_pipeline/chunker.py delete mode 100644 app/migration_pipeline/config.py delete mode 100644 app/migration_pipeline/git_manager.py delete mode 100644 app/migration_pipeline/migrator.py delete mode 100644 app/migration_pipeline/models.py delete mode 100644 app/migration_pipeline/orchestrator.py delete mode 100644 app/migration_pipeline/quality_gate.py delete mode 100644 app/migration_pipeline/quarantine.py delete mode 100644 app/migration_pipeline/reporter.py delete mode 100644 app/migration_pipeline/test_generator.py delete mode 100644 app/tests/test_api_client.py delete mode 100644 app/tests/test_config.py delete mode 100644 app/tests/test_run_migration.py delete mode 100755 cmake/DoxygenMacros.cmake delete mode 100755 cmake/FindArcGIS.cmake delete mode 100755 cmake/FindBgViewer.cmake delete mode 100755 cmake/FindBotan2.cmake delete mode 100644 cmake/FindGrantlee5.cmake delete mode 100755 cmake/FindKDChart.cmake delete mode 100755 cmake/FindKDReports.cmake delete mode 100755 cmake/FindLibraryWithDebug.cmake delete mode 100644 cmake/FindLog4Qt.cmake delete mode 100755 cmake/FindNGS.cmake delete mode 100755 cmake/FindPopplerQt5.cmake delete mode 100755 cmake/FindRTI.cmake delete mode 100755 cmake/FindSchusstafel.cmake delete mode 100755 cmake/FindTMSIF.cmake delete mode 100755 cmake/FindVLC.cmake delete mode 100755 cmake/FindXSD.cmake delete mode 100755 cmake/MacroCodeGenIdl.cmake delete mode 100755 cmake/MacroEnsureOutOfSourceBuild.cmake delete mode 100755 cmake/MacroRTIDDSGen.cmake delete mode 100755 cmake/NGLMacros.cmake delete mode 100755 cmake/make_qt_help.py delete mode 100755 cmake/ngl_dep_test_main.cpp delete mode 100644 doxygen/Doxyfile.in delete mode 100644 doxygen/Doxyfile2025.in delete mode 100644 lts-cc.conf create mode 160000 qt-migration-legatus delete mode 100755 specs/Guez_Ausbilderterminal.iss delete mode 100755 specs/external/GeographicLib-1.49.spec delete mode 100755 specs/external/OpenSceneGraph-3.2.3.spec delete mode 100755 specs/external/avs_conf/avs_conf-if.spec.in delete mode 100755 specs/external/avs_conf/avs_conf-legacy-ws.spec.in delete mode 100755 specs/external/avs_conf/avs_conf.spec.in delete mode 100755 specs/external/avs_conf/build_rpm.sh delete mode 100755 specs/external/avs_conf/build_rpm_git.sh delete mode 100755 specs/external/avs_conf/guez/AVS_Config.conf delete mode 100755 specs/external/avs_conf/guez/AVS_DB.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Export.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Interface.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Mixer.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Mobilegate.conf delete mode 100755 specs/external/avs_conf/guez/AVS_MotionRecorder.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Root.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Server.aar.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Server.conf delete mode 100755 specs/external/avs_conf/guez/AVS_TimelineConverter.conf delete mode 100755 specs/external/avs_conf/guez/AVS_Workstation.conf delete mode 100755 specs/external/avs_conf/guez/if/AVS_Config.conf delete mode 100755 specs/external/avs_conf/guez/if/AVS_Interface.conf.in delete mode 100755 specs/external/avs_conf/guez/if/AVS_Root.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Config.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_DB.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Export.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Interface.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Mixer.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Mobilegate.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_MotionRecorder.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Root.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Server.aar.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Server.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_TimelineConverter.conf delete mode 100755 specs/external/avs_conf/ltu/AVS_Workstation.conf delete mode 100755 specs/external/avs_conf/ltu/if/AVS_Config.conf delete mode 100755 specs/external/avs_conf/ltu/if/AVS_Interface.conf.in delete mode 100755 specs/external/avs_conf/ltu/if/AVS_Root.conf delete mode 100755 specs/external/bremen_buildings_buildingviewer.spec delete mode 100755 specs/external/clazy/clazy-1.3.spec delete mode 100755 specs/external/clazy/clazy-1.5.spec delete mode 100755 specs/external/clazy/make_rpm.sh delete mode 100755 specs/external/ctc_Log4Qt-1.4.2.patch delete mode 100755 specs/external/ctc_devel_tds_qt5-1.0.spec delete mode 100755 specs/external/ctc_drc-1.1.spec delete mode 100755 specs/external/ctc_gdal_2.4.3.spec delete mode 100755 specs/external/ctc_mak_rti_lm.spec delete mode 100755 specs/external/ctc_mak_rti_srv.spec delete mode 100755 specs/external/ctc_online_help_de.spec delete mode 100755 specs/external/ctc_online_help_en.spec delete mode 100755 specs/external/ctc_rti-6.0.1-1.spec delete mode 100755 specs/external/ctc_schusstafel.spec delete mode 100755 specs/external/gdb/load-qt5printers.py delete mode 100755 specs/external/gdb/load_lts_printers.py delete mode 100755 specs/external/gdb/lts_printers/__init__.py delete mode 100755 specs/external/gdb/lts_printers/gdb_helper.py delete mode 100755 specs/external/gdb/lts_printers/qtcreator_helper.py delete mode 100755 specs/external/gdb/qt5printers/__init__.py delete mode 100755 specs/external/gdb/qt5printers/core.py delete mode 100755 specs/external/gdb/qt5printers/typeinfo.py delete mode 100755 specs/external/git_with_committer delete mode 100755 specs/external/guez_buildings/postProcessingExcon/README.txt delete mode 100755 specs/external/guez_buildings/postProcessingExcon/checkDuplicateNames.sh delete mode 100755 specs/external/guez_buildings/postProcessingExcon/checkProtective.sh delete mode 100755 specs/external/guez_buildings/postProcessingExcon/convert_coordinates.py delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_ba0_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_ba1_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_ba2_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_ba3_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_ba4_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/guez_buildings_salchau_buildingviewer.spec delete mode 100755 specs/external/guez_buildings/postProcessingExcon/osgconv.sh delete mode 100755 specs/external/guez_buildings/postProcessingExcon/patches/patch_bldg_1251.flt.xml.patch delete mode 100755 specs/external/guez_buildings/postProcessingExcon/patches/patch_bldg_2101.flt.VWM_Building.xml.patch delete mode 100755 specs/external/guez_buildings/postProcessingExcon/patches/patch_bldg_3100.flt.VWM_Building.xml.patch delete mode 100755 specs/external/guez_buildings/postProcessingExcon/patches/patch_bldg_3101.flt.xml.patch delete mode 100755 specs/external/guez_buildings/postProcessingExcon/postProcessingExcon.sh delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/BSM_pos.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/egcu_conf.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/pos_dir_guez.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/replace_transistion_factors.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/simplify_polygons.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/vertical_Transition_Factor_neu.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/RECURSIV/vwmXmlToVrmlWrl.js delete mode 100755 specs/external/guez_buildings/postProcessingExcon/vwm_adjustment/analyse_xdr_VWM_File.js delete mode 100755 specs/external/kdchart-2.6.0.spec delete mode 100755 specs/external/log4qt-1.4.2.spec delete mode 100755 specs/external/lts_astyle_helper-1.0/README.txt delete mode 100755 specs/external/lts_astyle_helper-1.0/astyle_lts delete mode 100755 specs/external/lts_astyle_helper-1.0/main.cpp delete mode 100755 specs/external/lts_astyle_helper-1.0/normalize.pro delete mode 100755 specs/external/lts_astyle_helper.spec delete mode 100755 specs/external/lts_config_entw_192_168_133_44.conf delete mode 100755 specs/external/lts_config_entw_192_168_133_44.spec delete mode 100755 specs/external/maia-1.2.spec delete mode 100755 specs/external/mak_rti.spec delete mode 100755 specs/external/ngs.spec delete mode 100755 specs/external/ngs_addons.spec delete mode 100755 specs/external/ngs_guez_fom.spec delete mode 100755 specs/external/ngs_rtifacade_mak450.spec delete mode 100755 specs/external/ngs_rtifacade_prti431_1516e.spec delete mode 100755 specs/external/pitch_rti_431_1516e.spec delete mode 100755 specs/external/project_dependencies/ahtc_dependencies.spec delete mode 100755 specs/external/project_dependencies/guez_dependencies.spec delete mode 100755 specs/external/project_dependencies/l400_dependencies.spec delete mode 100755 specs/external/project_dependencies/l400p2_dependencies.spec delete mode 100755 specs/external/project_dependencies/lynxhu_dependencies.spec delete mode 100755 specs/external/project_dependencies/qatar_dependencies.spec delete mode 100755 specs/external/project_dependencies/qatarmout_dependencies.spec delete mode 100755 specs/external/score_configuration-3.9.9.spec delete mode 100755 specs/external/score_geodesy-3.3.8.spec delete mode 100755 specs/external/score_geometry-2.4.3.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100/arcgis-qt-V100.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100/arcgis_map_topographic.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100/arcgis_qt-V100-debug_symbols.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100/arcgis_qt-V100.2.1_testFix.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100_local_server/arcgis_local_server-V100.2.spec delete mode 100755 specs/external/situation_map/arcgis/qt-V100_local_server/arcgis_viewshed_gpk.spec delete mode 100755 specs/external/situation_map/arcgis/symbols/arcgis_app6b.spec delete mode 100755 specs/external/situation_map/guez/guez_arcgis_3Dmodels.spec delete mode 100755 specs/external/situation_map/guez/guez_arcgis_bldg.spec delete mode 100755 specs/external/situation_map/guez/guez_arcgis_elevation_map_letzlingen.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_aerial2017_32632.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_aerial2017_WebMerc.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_schnoeggersburg_drone_32632.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_schnoeggersburg_drone_WebMerc.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_topo2015_32632.spec delete mode 100755 specs/external/situation_map/guez/guez_basemap_topo2015_WebMerc.spec delete mode 100755 specs/external/situation_map/guez/guez_situation_map.spec delete mode 100755 specs/external/subversion_config delete mode 100755 specs/external/tmsif.spec delete mode 100755 specs/external/valgrind_supressions.txt delete mode 100755 specs/lts.spec delete mode 100755 specs/lts15.1.spec delete mode 100755 specs/lts15.2.spec delete mode 100755 specs/lts15.4.spec delete mode 100755 specs/lts15.6.spec delete mode 100755 specs/lts42.3.spec delete mode 100755 specs/ltsHLAOnly.spec delete mode 100755 specs/ltsSVOnly.spec delete mode 100755 specs/lts_libre_office_integration.spec delete mode 100755 specs/lts_ngs_tes_uae_apache.spec delete mode 100755 specs/lts_prti1516e.spec delete mode 100755 specs/lts_thp.spec delete mode 100644 tests/gtest/CMakeLists.txt delete mode 100644 tests/gtest/README.md delete mode 100644 tests/gtest/generate_gtests.py delete mode 100644 tests/strix/Z/NGLLogger__MurmurHash2.cpp.md delete mode 100644 tests/strix/Z/NGLLogger__NGLLogger.cpp.md delete mode 100644 tests/strix/Z/NGLLogger__NGLLoggerBootstrap.cpp.md delete mode 100644 tests/strix/Z/NGLLogger__test_ngllogger.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtlocalpeer.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtlockedfile.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtlockedfile_unix.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtlockedfile_win.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtsingleapplication.cpp.md delete mode 100644 tests/strix/Z/QtSingleApplication__qtsinglecoreapplication.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_equipRequirements.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_equipmenttypes.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_exercises.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_playerInfo.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_registration.cpp.md delete mode 100644 tests/strix/Z/WMS_Interface__wms_ecc_unregister.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglapplication.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglcoreapplication.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglguiapplication.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglloggerhandler.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglsingleapplication.cpp.md delete mode 100644 tests/strix/Z/nglapplication__nglsinglecoreapplication.cpp.md delete mode 100644 tests/strix/Z/nglcompression__nglcomp_bzip2iodevice.cpp.md delete mode 100644 tests/strix/Z/nglcompression__nglcomp_compress.cpp.md delete mode 100644 tests/strix/Z/nglcompression__nglcomp_tarfilter.cpp.md delete mode 100644 tests/strix/Z/nglcompression__nglcomp_test.cpp.md delete mode 100644 tests/strix/Z/ngldb__dbconnection.cpp.md delete mode 100644 tests/strix/Z/ngldb__dbtransaction.cpp.md delete mode 100644 tests/strix/Z/ngldb__ngldb_logger.cpp.md delete mode 100644 tests/strix/Z/ngldb__nsqlquery.cpp.md delete mode 100644 tests/strix/Z/ngldb__sql_query_janitor.cpp.md delete mode 100644 tests/strix/Z/nglsettings__nglsettings_connections.cpp.md delete mode 100644 tests/strix/Z/nglsettings__nglsettings_settings.cpp.md delete mode 100644 tests/strix/Z/ngltools__ngltools_convert.cpp.md delete mode 100644 tests/strix/Z/ngltools__ngltools_dtg.cpp.md delete mode 100644 tests/strix/Z/ngltools__ngltools_filesystem.cpp.md delete mode 100644 tests/strix/Z/ngltools__ngltools_helpevent.cpp.md delete mode 100644 tests/strix/Z/ngltranslator__ngltranslator.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__ngldialog.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__ngldialogbuttonbox.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__nglmainwindow.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__nglpdfdocument.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__nglpdfpage.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__nglpdfwidget.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__nglstandardbutton.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__ngltreeitem.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__ngltreemodel.cpp.md delete mode 100644 tests/strix/Z/nglwidgets__test_nglpdfwidget.cpp.md delete mode 100644 tests/strix/Z/qsql_informix__qsql_informix.cpp.md delete mode 100644 tests/strix/Z/qsql_informix__qsql_informix_plugin.cpp.md delete mode 100644 tests/strix/Z/qsql_informix__qsql_informix_test.cpp.md delete mode 100644 tests/strix/Z/qsql_informix__qsql_informix_utils.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_help.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_pingpong.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_quit.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_recvfile.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_sendfile.cpp.md delete mode 100644 tests/strix/Z/tcpserver__cbi_shutdown.cpp.md delete mode 100644 tests/strix/Z/tcpserver__commandinterface.cpp.md delete mode 100644 tests/strix/Z/tcpserver__tcpserver.cpp.md delete mode 100644 tests/strix/Z/tcpserver__tcpserver_test.cpp.md delete mode 100644 tests/strix/Z/tcpserver__workerbase.cpp.md delete mode 100755 tests/strix/generate_strix_tests.py delete mode 100755 tests/strix/run_all_strix.sh delete mode 100644 translations/CMakeLists.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..586777254 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "qt-migration-legatus"] + path = qt-migration-legatus + url = git@devel:lifemaps/qt-migration-legatus.git diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100755 index 85d4a8bbf..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,378 +0,0 @@ -# -# Haupt CMakeLists.txt für das Projekt TDS -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Setzen von CMAKE_INSTALL_PREFIX auf "/shd/CTC" falls nicht über Kommandozeile angegeben -# - -CMAKE_MINIMUM_REQUIRED(VERSION 3.16) -IF(POLICY CMP0069) - CMAKE_POLICY(SET CMP0069 NEW) # cmake >= 3.9.0 - INTERPROCEDURAL_OPTIMIZATION behavior -ENDIF() -IF(POLICY CMP0071) - CMAKE_POLICY(SET CMP0071 NEW) # cmake >= 3.10.0 - automatically process generated sources with AUTOMOC -ENDIF() -IF(POLICY CMP0072) - CMAKE_POLICY(SET CMP0072 NEW) # cmake >= 3.11.0 - prefer libOpenGL.so over legacy libGL.so -ENDIF() - - -IF(NOT DEFINED CMAKE_INSTALL_PREFIX) - SET(CMAKE_INSTALL_PREFIX "/shd/CTC" CACHE STRING "Install path prefix, prepended onto install directories." FORCE) -ENDIF() -# -# # Setzen von CMAKE_BUILD_TYPE auf "Debug" falls nicht über Kommandozeile angegeben -# -IF(NOT DEFINED CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) -ENDIF() -ADD_DEFINITIONS(-DEXPORT_BDV_TOPIC_AS_JSON) -SET(_ver "1.2.0") -IF(DEFINED LTS_VERSION_MAJOR AND DEFINED LTS_VERSION_MINOR AND DEFINED LTS_VERSION_PATCH) - SET(_ver "${LTS_VERSION_MAJOR}.${LTS_VERSION_MINOR}.${LTS_VERSION_PATCH}") -ENDIF() -PROJECT(LTS VERSION ${_ver}) -MESSAGE(STATUS "LTS Version: ${LTS_VERSION_MAJOR}.${LTS_VERSION_MINOR}.${LTS_VERSION_PATCH}") -SET(LTS_VERSION_DATE "01.01.2018" CACHE STRING "The date when this software was compiled") -ADD_DEFINITIONS(-DLTS_VERSION_MAJOR=\"${LTS_VERSION_MAJOR}\") -ADD_DEFINITIONS(-DLTS_VERSION_MINOR=\"${LTS_VERSION_MINOR}\") -ADD_DEFINITIONS(-DLTS_VERSION_PATCH=\"${LTS_VERSION_PATCH}\") -ADD_DEFINITIONS(-DLTS_VERSION_DATE=\"${LTS_VERSION_DATE}\") -IF(JENKINS_BUILD_NUMBER) - ADD_DEFINITIONS(-DJENKINS_BUILD_NUMBER=\"${JENKINS_BUILD_NUMBER}\") -ELSE() - ADD_DEFINITIONS(-DJENKINS_BUILD_NUMBER=\"unknown\") -ENDIF() - -ADD_DEFINITIONS(-DAPP6C) -SET(CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories(${CMAKE_SOURCE_DIR}/include) -SET(CMAKE_CXX_STANDARD 20) -SET(CMAKE_CXX_STANDARD_REQUIRED 14) -SET(CMAKE_VISIBILITY_INLINES_HIDDEN ON) -SET(CMAKE_CXX_VISIBILITY_PRESET hidden) -SET(CMAKE_POSITION_INDEPENDENT_CODE ON) - -IF(CMAKE_VERSION VERSION_GREATER 3.15.9) - OPTION(USE_UNITY_BUILDS "Use Unity builds to speed up compilation (cmake 3.16 or newer required!)" OFF) - IF (USE_UNITY_BUILDS) - SET(CMAKE_UNITY_BUILD ON) - SET(CMAKE_UNITY_BUILD_BATCH_SIZE 40) - ENDIF() -ENDIF() - -IF(WIN32) - SET(EXTERNAL_LIBRARY_DIR "C:/Development/Projekte/SituationMap/ExtFw" CACHE STRING "Library dir with all external dependencies") - LIST(APPEND CMAKE_PREFIX_PATH "${EXTERNAL_LIBRARY_DIR}") - LIST(APPEND CMAKE_PROGRAM_PATH "${EXTERNAL_LIBRARY_DIR}/bin;${EXTERNAL_LIBRARY_DIR}/dev-utils/bin") -ENDIF() - -ENABLE_TESTING() -INCLUDE(CTest) -CONFIGURE_FILE(CTestCustom.ctest ${CMAKE_BINARY_DIR}/CTestCustom.ctest COPYONLY) -CONFIGURE_FILE(lts-cc.conf ${CMAKE_BINARY_DIR}/lts-cc.conf.distrib COPYONLY) - -SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) -IF(WIN32) - SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) -ELSE(WIN32) - SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) -ENDIF() - -# -# Disallow in-source build -# -INCLUDE(MacroEnsureOutOfSourceBuild) - -macro_ensure_out_of_source_build("In-Source build nicht erlaubt!") - -# -# Qt ist wichtig - zuerst suchen -# -SET(CMAKE_AUTOMOC TRUE) -SET(CMAKE_AUTOUIC TRUE) -SET(QT_USE_IMPORTED_TARGETS TRUE) -# Prefer Qt6, fallback to Qt5. Create Qt5::*-aliases when Qt6 is found so existing CMakeLists continue to work. -# Try to detect Qt6 with a minimal check and then create aliases/placeholders for components used across the project. -# Prefer Qt6, fallback to Qt5. Create Qt5::* aliases only when the corresponding -# Qt6:: component target exists. Avoid creating IMPORTED placeholder targets which -# have no LOCATION and later inject 'NOTFOUND' into autogen timestamp deps. -# Find minimal Qt6 Core first, then probe for other components (non-fatal). -find_package(Qt6 6.8 COMPONENTS Core QUIET) -if(Qt6_FOUND) - message(STATUS "Found Qt6: ${Qt6Core_VERSION}") - set(_qlist Concurrent Core Gui Location LinguistTools Network OpenGL PrintSupport - Sensors Multimedia Sql Svg SerialPort Test UiTools WebSockets WebEngineCore WebEngineWidgets - Widgets Xml XmlPatterns) - foreach(_comp ${_qlist}) - # Probe for each component (non-fatal). If found, create a Qt5:: alias. - find_package(Qt6 COMPONENTS ${_comp} QUIET) - if(TARGET "Qt6::${_comp}") - add_library("Qt5::${_comp}" ALIAS "Qt6::${_comp}") - endif() - endforeach() - # Map version variable for compatibility if available - if(Qt6Core_VERSION) - set(Qt5Core_VERSION ${Qt6Core_VERSION} CACHE INTERNAL "Compatibility Qt5Core_VERSION mapped from Qt6") - endif() - # Find rcc/lrelease programs as compatibility fallbacks - find_program(_rcc_exec rcc-qt6 rcc) - if(_rcc_exec) - set(Qt5Core_RCC_EXECUTABLE ${_rcc_exec} CACHE INTERNAL "Compatibility rcc executable") - endif() - find_program(_lrelease_exec lrelease-qt6 lrelease) - if(_lrelease_exec) - set(Qt5_LRELEASE_EXECUTABLE ${_lrelease_exec} CACHE INTERNAL "Compatibility lrelease executable") - if(TARGET Qt6::LinguistTools AND NOT TARGET Qt5::lrelease) - add_executable(Qt5::lrelease IMPORTED) - set_target_properties(Qt5::lrelease PROPERTIES IMPORTED_LOCATION ${Qt5_LRELEASE_EXECUTABLE}) - endif() - endif() -else() - FIND_PACKAGE(Qt5 5.9.0 COMPONENTS Concurrent Core Gui Location LinguistTools - Network OpenGL PrintSupport Sensors Multimedia - Sql Svg SerialPort Test UiTools WebSockets - WebEngineCore WebEngineWidgets Widgets - Xml XmlPatterns - REQUIRED) - MESSAGE(STATUS "Qt version is ${Qt5Core_VERSION}") -endif() - -MESSAGE(STATUS "Qt version is ${Qt5Core_VERSION}") -ADD_DEFINITIONS(-DQT_USE_QSTRINGBUILDER) -ADD_DEFINITIONS(-DQT_NO_CAST_TO_ASCII) -ADD_DEFINITIONS(-DQT_NO_CAST_FROM_ASCII) -ADD_DEFINITIONS(-DQT_NO_URL_CAST_FROM_STRING) -ADD_DEFINITIONS(-DQT_DEPRECATED_WARNINGS) -ADD_DEFINITIONS(-DQT_NO_CAST_FROM_BYTEARRAY) -#ADD_DEFINITIONS(-DQT_NO_DEPRECATED) -ADD_DEFINITIONS(-DQT_DISABLE_DEPRECATED_BEFORE=0x050900) -ADD_DEFINITIONS(-DQT_NO_FOREACH) -IF(NOT WIN32) - ADD_DEFINITIONS(-DQT_STRICT_ITERATORS) -ENDIF() - -# -# Ein paar NGL-spezifische Macros -# -INCLUDE(NGLMacros) - -# -# Projektspezifische Einstellungen? -# -NGL_INSTALL(SV AAR WS FILES lts-cc.conf DESTINATION . RENAME lts-cc.conf.distrib) - -OPTION(ETS_MODE "Build for ETS (No B-Contamination etc)" OFF) -IF(ETS_MODE) - ADD_DEFINITIONS(-DETS_MODE) -ENDIF() -OPTION(TABLET_MODE "Build for TABLET " OFF) -IF(TABLET_MODE) - ADD_DEFINITIONS(-DTABLET_MODE) -ENDIF() - -OPTION(GUEZ_MODE "Build for GUEZ " ON) -IF(GUEZ_MODE) - ADD_DEFINITIONS(-DGUEZ_MODE) -ENDIF() - -# -# Doxygen -# -INCLUDE(DoxygenMacros) -IF(NOT DOXYGEN_OUT_DIR) - SET(DOXYGEN_OUT_DIR ${CMAKE_BINARY_DIR}/doxygen) -ENDIF() -ADD_DOXYGEN_TARGET(TDS ${CMAKE_SOURCE_DIR}/doxygen/Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile - ${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg - ${DOXYGEN_OUT_DIR}) - -IF(NOT DOXYGEN_OUT_DIR_2025) - SET(DOXYGEN_OUT_DIR_2025 ${CMAKE_BINARY_DIR}/doxygen2025) -ENDIF() -ADD_DOXYGEN_TARGET(TDS2025 ${CMAKE_SOURCE_DIR}/doxygen/Doxyfile2025.in ${CMAKE_BINARY_DIR}/Doxyfile - ${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg - ${DOXYGEN_OUT_DIR_2025}) - -# -# Compilerflags -# -IF(WIN32) - ADD_DEFINITIONS(-DWIN32 -D_USE_MATH_DEFINES -D_WIN32_WINNT=0x0501 -DUNICODE) -ELSE() - ADD_DEFINITIONS(-D_LINUX) -ENDIF() - -IF(MSVC) - # C4250: 'class1' : inherits 'class2::member' via dominance - # C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' - # C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow) - # C4496: 'function': was declared deprecated - # C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data - ADD_DEFINITIONS(-wd4250 - -wd4251 - -wd4290 - -wd4496 - -wd4267 - -D_CRT_SECURE_NO_DEPRECATE - -D_CRT_NONSTDC_NO_DEPRECATE - -D_SCL_SECURE_NO_WARNINGS - -D_WINSOCK_DEPRECATED_NO_WARNINGS - /bigobj - ) - # komplette Programmoptimierung - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") - # .pdb - Datei nicht gefunden - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /IGNORE:4099") - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /IGNORE:4099") - # min() und max() nicht als Macros - ADD_DEFINITIONS(-DNOMINMAX) - SET(CMAKE_MSVCIDE_RUN_PATH "" CACHE PATH "Run path to use when starting programs from MSVC") -ENDIF() - -IF(UNIX) - # Alle Warnungen aktivieren - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wmissing-include-dirs") - # disable - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-overloaded-virtual") - IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0) - MESSAGE(FATAL_ERROR "gcc 4.8 or higher is required to build TDS: ${CMAKE_CXX_COMPILER_VERSION}") - ENDIF() - IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9.99) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-attribute=const") -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-final-types") -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-final-methods") -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") - ENDIF() - ENDIF() -ENDIF() - -OPTION(USE_IPO "Use interprocedural optimizations (cmake 3.9 or higher required)" OFF) -IF (USE_IPO AND CMAKE_VERSION VERSION_GREATER 3.8.9) - INCLUDE(CheckIPOSupported) - check_ipo_supported(RESULT result OUTPUT output) - IF(result) - SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) - ELSE() - MESSAGE(WARNING "IPO is not supported: ${output}") - ENDIF() -ENDIF() - -OPTION(USE_ASAN "Use Address Sanitizer (current Clang only)" OFF) -IF (USE_ASAN) - IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - MESSAGE(STATUS "enable Address Sanitizer with Clang (is: ${CMAKE_CXX_COMPILER_ID})") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1") - ELSE() - MESSAGE(STATUS "Address Sanitizer only supported with Clang (is: ${CMAKE_CXX_COMPILER_ID})") - ENDIF() -ENDIF(USE_ASAN) - -OPTION(USE_UBSAN "Use UndefinedBehavior Sanitizer (current Clang only)" OFF) -IF (USE_UBSAN) - IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") - ELSE() - MESSAGE(STATUS "UndefinedBehavior only supported with Clang (is: ${CMAKE_CXX_COMPILER_ID})") - ENDIF() -ENDIF(USE_UBSAN) - -OPTION(BUILD_RPM "Build a RPM - install script in scripts subdir" OFF) - -IF(BUILD_RPM) - ADD_SUBDIRECTORY(scripts) -ELSE() - ADD_DEFINITIONS(-DLTS_DEBUG) -ENDIF() - -IF (NOT DEFINED _pname) - IF(GUEZ_MODE) - SET (_pname "guez") - ADD_DEFINITIONS(-DPROJECTBUILD="guez") - MESSAGE(STATUS "NO ProjectName givin! BUT GUEZ_MODE set PROJECTBUILD to guez") - ELSE() - MESSAGE(WARNING "NO ProjectName givin! Can't install coresponding lts-cc.conf! and PROJECTBUILD is empty") - ADD_DEFINITIONS(-DPROJECTBUILD="") - ENDIF() -ELSE() - MESSAGE(STATUS "ProjectName givin! PROJECTBUILD is ${_pname} ") - ADD_DEFINITIONS(-DPROJECTBUILD=\"${_pname}\") -ENDIF() - - -# -# DDS benoetigt zwingend RTI -# -FIND_PACKAGE(RTI QUIET) -if(NOT RTI_FOUND) - message(WARNING "RTI not found; creating placeholder RTI::* imported targets to allow configure without RTI installation") - set(_rti_libs nddscore nddsc nddscpp2 rtiroutingservice) - foreach(_lib ${_rti_libs}) - if(NOT TARGET RTI::${_lib}) - add_library(RTI::${_lib} UNKNOWN IMPORTED) - set_property(TARGET RTI::${_lib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "") - endif() - endforeach() -endif() - -# Respect MIGRATE_MODULE: when set to a relative path (e.g. "Lts/Common"), only add -# the top-level subdirectories required to configure/build that module. This allows -# per-module migration builds via -DMIGRATE_MODULE=. -if(NOT DEFINED MIGRATE_MODULE) - set(MIGRATE_MODULE "" CACHE STRING "If set, only add subdirectories covering this module (path relative to repo root)") -endif() - -function(NGL_ADD_SUBDIR_GUARDED subdir) - if(MIGRATE_MODULE STREQUAL "") - add_subdirectory(${subdir}) - else() - # normalize to forward slashes - string(REPLACE "\\" "/" _mig "${MIGRATE_MODULE}") - string(REPLACE "\\" "/" _sub "${subdir}") - # also compare basenames so MIGRATE_MODULE=Lts/DDS matches subdir 'DDS' when called from Lts - get_filename_component(_mig_base "${_mig}" NAME) - get_filename_component(_sub_base "${_sub}" NAME) - # If MIGRATE_MODULE either starts with the subdir, equals it, contains it as a path component, - # or the basename matches, include the subdir. This makes nested paths like - # 'Lts/Proxies/TrainingAreaBase' include both 'Lts' and its child 'Proxies'. - if(_mig MATCHES "^${_sub}(/|$)" OR _sub MATCHES "^${_mig}(/|$)" OR _mig MATCHES ".*/${_sub}(/|$)" OR _sub MATCHES ".*/${_mig}(/|$)" OR _mig_base STREQUAL _sub OR _sub_base STREQUAL _mig) - message(STATUS "Including subdir ${subdir} because MIGRATE_MODULE=${MIGRATE_MODULE}") - add_subdirectory(${subdir}) - else() - message(STATUS "Skipping subdir ${subdir} because MIGRATE_MODULE=${MIGRATE_MODULE}") - endif() - endif() -endfunction() - -# Add only guarded subdirectories -NGL_ADD_SUBDIR_GUARDED(TetraIF) -NGL_ADD_SUBDIR_GUARDED(Z/src) -NGL_ADD_SUBDIR_GUARDED(Lts) -# Special-case: when migrating TetraIF, also include TrainingAreaBase and Z so generated headers -# and internal logger target are available in the same build tree for TetraIF dependents. -if(DEFINED MIGRATE_MODULE AND MIGRATE_MODULE STREQUAL "TetraIF") - message(STATUS "Including helper modules for TetraIF migration: Lts/Proxies/TrainingAreaBase and Z/src") - add_subdirectory(Lts/Proxies/TrainingAreaBase) - add_subdirectory(Lts/Plugins/ProjectDataManagement) - add_subdirectory(Z/src) -endif() -# at last, to make sure we get all targets in NGL_TS_TARGETS property -NGL_ADD_SUBDIR_GUARDED(translations) -#----------------------------------------------------------------------------------------------------- - - -OPTION(BUILD_QAV "Build script for local QAV analysis" OFF) -IF (BUILD_QAV) - ADD_SUBDIRECTORY(qav) -ENDIF() diff --git a/CTestCustom.ctest b/CTestCustom.ctest deleted file mode 100644 index ee8083a4c..000000000 --- a/CTestCustom.ctest +++ /dev/null @@ -1,2 +0,0 @@ -# Minimal CTestCustom.ctest placeholder -# Created to satisfy top-level CMake configure step diff --git a/Lts/CMakeLists.txt b/Lts/CMakeLists.txt deleted file mode 100644 index 75ffd52d5..000000000 --- a/Lts/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -cmake_minimum_required(VERSION 3.16) -project(LtsPlaceholder) -SET(DOXYGEN_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen_Lts) -if (EXISTS "${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in") - ADD_DOXYGEN_TARGET(Lts ${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in ${CMAKE_BINARY_DIR}/DoxyfileLts - ${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg - ${DOXYGEN_OUT_DIR}) -else() - message(WARNING "Doxygen config ${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in not found; skipping ADD_DOXYGEN_TARGET(Lts)") -endif() - -# Add only existing subdirectories to allow partial checkouts / incremental migration -set(LTS_SUBDIRS Tools Application Common DDS Plugins Projects Proxies Services Viewer) -foreach(dir IN LISTS LTS_SUBDIRS) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") - # Use guarded add so top-level MIGRATE_MODULE can restrict which Lts subdirs are included - NGL_ADD_SUBDIR_GUARDED(${dir}) -else() - message(STATUS "Skipping missing Lts subdirectory: ${dir}") -endif() - -endforeach() - -IF (BUILD_TESTING) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Tests") - ADD_SUBDIRECTORY(Tests) - else() - message(STATUS "Skipping missing Tests directory under Lts (BUILD_TESTING is ON)") - endif() -ENDIF() - -OPTION(BUILD_DOCS "Build documentation as html files from Lts/Documentation" OFF) -IF (BUILD_DOCS) - ADD_SUBDIRECTORY(Documentation) -ENDIF() - -# Ensure codegen macros are available before calling CODEGEN_* helpers -INCLUDE(MacroCodeGenIdl) - -CODEGEN_WRITE_SERVICEFILE_INFO() diff --git a/Lts/Common/CMakeLists.txt b/Lts/Common/CMakeLists.txt deleted file mode 100755 index bd3700db0..000000000 --- a/Lts/Common/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -ADD_SUBDIRECTORY(CodeGen) -ADD_SUBDIRECTORY(httq) -#ADD_SUBDIRECTORY(LtsBotan) -ADD_SUBDIRECTORY(LtsCommandLineParser) -ADD_SUBDIRECTORY(LtsCoordTrans) -ADD_SUBDIRECTORY(LtsCoordinate) -ADD_SUBDIRECTORY(LtsElevation) -ADD_SUBDIRECTORY(LtsLicence) -ADD_SUBDIRECTORY(LtsPluginManager) -ADD_SUBDIRECTORY(LtsSession) -ADD_SUBDIRECTORY(LtsUtils) -ADD_SUBDIRECTORY(LtsWidgets) -ADD_SUBDIRECTORY(LtsAppDescriptor) -ADD_SUBDIRECTORY(LtsAppManager) -ADD_SUBDIRECTORY(LtsMultiMonitor) -ADD_SUBDIRECTORY(LtsLogin) -ADD_SUBDIRECTORY(LtsNotification) -ADD_SUBDIRECTORY(LtsTimeline) -ADD_SUBDIRECTORY(LtsPrinting) -ADD_SUBDIRECTORY(LtsSerialPortQuerry) -ADD_SUBDIRECTORY(LtsScriptTimeline) -ADD_SUBDIRECTORY(LtsWebSocket) -ADD_SUBDIRECTORY(LtsProjectionWorkstation) -ADD_SUBDIRECTORY(LtsVirtualKeyboard) -ADD_SUBDIRECTORY(PluginInterfaces) -ADD_SUBDIRECTORY(ProjectConfig) -ADD_SUBDIRECTORY(TopicEditorBase) # TODO: rename / move ? -#ADD_SUBDIRECTORY(LtsEventTimeline) -#ADD_SUBDIRECTORY(LtsTranslator) - -# Placeholder test scaffolding for incremental migration -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CommonUnitTestsPlaceholder/CMakeLists.txt") - add_subdirectory(CommonUnitTestsPlaceholder) -endif() - diff --git a/Lts/Common/CodeGen/CMakeLists.txt b/Lts/Common/CodeGen/CMakeLists.txt deleted file mode 100755 index 2fdff8411..000000000 --- a/Lts/Common/CodeGen/CMakeLists.txt +++ /dev/null @@ -1,155 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -FIND_PACKAGE(RTI) -FIND_PACKAGE(Grantlee5) - -NGL_ADD_LIBRARY(ALL codegen_base SHARED - codegen_export.h - SqlGenerator.cpp - SqlGenerator.h - StructBuilder.cpp - StructBuilder.h - XmlReader.cpp - XmlReader.h -) -NGL_TARGET_LINK_LIBRARIES(codegen_base - PRIVATE - Qt5::Xml - NGLLogger - ngltools - PUBLIC - Qt5::Core -) -NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -# If RTI target is available, propagate its include dirs/compile defs -if (TARGET RTI::nddscpp2) - NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base - PUBLIC - $ - ) - NGL_TARGET_COMPILE_DEFINITIONS(codegen_base - PUBLIC - $ - ) -else() - message(STATUS "RTI not found; building codegen_base without RTI include/compile definitions") -endif() -NGL_INSTALL_TARGET(codegen_base) - -if (TARGET Grantlee5::Templates) - NGL_ADD_LIBRARY(ALL codegen_grantlee STATIC - CppTopicGeneratorGrantlee.cpp - CppTopicGeneratorGrantlee.h - DaoGeneratorGrantlee.cpp - DaoGeneratorGrantlee.h - DatabaseGeneratorGrantlee.cpp - DatabaseGeneratorGrantlee.h - GeneratorGrantleeBase.cpp - GeneratorGrantleeBase.h - GrantleeWrappers.cpp - GrantleeWrappers.h - - svc.idl.grantlee - svc_enums.idl.grantlee - - svc_translation.cpp.grantlee - svc_translation.h.grantlee - - svc_dao.cpp.grantlee - svc_dao.h.grantlee - - svc_db.h.grantlee - svc_db_impl.h.grantlee - svc_db_impl.cpp.grantlee - - svc_dtc.h.grantlee - svc_dtc_impl.cpp.grantlee - svc_dtc_impl.h.grantlee - - svc_dts.h.grantlee - svc_dts_impl.cpp.grantlee - svc_dts_impl.h.grantlee - - svc_ftc.h.grantlee - svc_ftc_impl.cpp.grantlee - svc_ftc_impl.h.grantlee - - svc_fts.h.grantlee - svc_fts_impl.cpp.grantlee - svc_fts_impl.h.grantlee - - svc_json.cpp.grantlee - svc_json.h.grantlee - - svc_topic_type_plugin.cpp.grantlee - svc_topic_type_plugin.h.grantlee - - svc_record_replay_plugin.cpp.grantlee - svc_record_replay_plugin.h.grantlee - - codegen_simple.py - ) - - NGL_TARGET_LINK_LIBRARIES(codegen_grantlee - PRIVATE - Grantlee5::Templates - NGLLogger - ProjectConfig - PUBLIC - codegen_base - ) - NGL_TARGET_INCLUDE_DIRECTORIES(codegen_grantlee - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ) -else() - message(STATUS "Grantlee5 not found; skipping codegen_grantlee target") -endif() - -# Smoke-test: parse all .grantlee templates in this directory to detect parse/render regressions -if (TARGET Grantlee5::Templates) - find_package(Qt6 COMPONENTS Core QUIET) - if (Qt6_FOUND) - set(QTCORE_TARGET Qt6::Core) - else() - find_package(Qt5 COMPONENTS Core REQUIRED) - set(QTCORE_TARGET Qt5::Core) - endif() - - add_executable(grantlee_parser_smoke tests/GrantleeParserSmoke.cpp) - target_link_libraries(grantlee_parser_smoke PRIVATE Grantlee5::Templates ${QTCORE_TARGET}) - add_test(NAME GrantleeParserSmoke COMMAND grantlee_parser_smoke ${CMAKE_CURRENT_SOURCE_DIR}) -endif() - -NGL_ADD_EXECUTABLE(ALL codegen - main_grantlee.cpp -) -NGL_TARGET_LINK_LIBRARIES(codegen - codegen_grantlee - LtsCommandLineParser - nglcoreapplication - Qt5::Core -) - -NGL_WRAP_UI(codegen_gui_uics - main_grantlee_gui.ui -) -NGL_ADD_EXECUTABLE(WS AAR SV NO_TRANSLATION codegen_gui - ${codegen_gui_uics} - main_grantlee_gui.cpp - main_grantlee_gui.ui -) -NGL_TARGET_LINK_LIBRARIES(codegen_gui - codegen_grantlee - LtsCommandLineParser - nglapplication - Qt5::Widgets - Grantlee5::Templates -) diff --git a/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp b/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp deleted file mode 100755 index a041f8b6b..000000000 --- a/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp +++ /dev/null @@ -1,345 +0,0 @@ - - - -#include "CppTopicGeneratorGrantlee.h" -#include "StructBuilder.h" -#include -#include -#include - -using namespace CodeGen; - -CppTopicGeneratorGrantlee::CppTopicGeneratorGrantlee() - : GeneratorGrantleeBase("CodeGen.CppTopicGeneratorGrantlee") -{} - -bool CppTopicGeneratorGrantlee::init(const XmlReader *reader, StructBuilder *structBuilder) -{ - m_reader = reader; - if (!GeneratorGrantleeBase::initInternal(reader, structBuilder)) - return false; - - addCustomFunctions(reader->getCustomFunctions()); - - return initDataTopics() && initFunctionTopics() && initIdlStructs(); -} - - - -bool CppTopicGeneratorGrantlee::initDataTopics() -{ - DataTopicsWrapper *dataTopicsWrapper = new DataTopicsWrapper(QString(), this); - const auto dataTopics = m_reader->getIdlStructInformation(); - QVector keyColumns; - QVector trColumns; - for (const CodeGen::TopicTypeInformation &tti : dataTopics) - { - if (!tti.isDataObject) - continue; - - keyColumns.clear(); - trColumns.clear(); - const CompleteTopicTypeInformation ctti = m_structBuilder->topicTypeInformationByStructType(tti.name); - if (!ctti.isValid()) - { - ERROR_LOG(m_logger) << "could not find informations for struct " << tti.name; - continue; - } - DataTopicWrapper *dtw = dataTopicsWrapper->addDataTopic(ctti); - getSpecialColumns(ctti, keyColumns, trColumns); - if (keyColumns.size() == 1) - dtw->setKeyColumnData(keyColumns.first()); - dtw->setTrColumns(trColumns); - if (ctti.recordTopic) - { - const CompleteTableInformation cti(m_structBuilder->getRecordingTopicTable(ctti.name)); - if (!cti.isEmpty()) - dtw->setRecordTableInformation(cti); - } - else - { - if (ctti.isDataObject) - m_structBuilder->buildJSonStructureInformationFromIdlData(ctti); - } - } - m_ctx->insert(QStringLiteral("dataTopics"), dataTopicsWrapper); - - QSet exportedSubTopics; - DataTopicsWrapper *subTopicsWrapper = new DataTopicsWrapper(QString(), this); - const QVector> serializeTopics = m_structBuilder->getSerializeFromToJsonTopics(); - for (const QSharedPointer &dti : serializeTopics) - { - if (exportedSubTopics.contains(dti->idlType())) - continue; - if (dti->isStructure()) - { - const CompleteTopicTypeInformation ctti = m_structBuilder->topicTypeInformationByStructType(dti->idlType()); - if (!ctti.isValid()) - { - ERROR_LOG(m_logger) << "could not find informations for struct " << dti->idlType(); - continue; - } - const CompleteTableInformation cti(m_structBuilder->getRecordingTopicTable(ctti.name)); - if (cti.isEmpty()) - { - ERROR_LOG(m_logger) << "could not find informations for struct " << dti->idlType(); - continue; - } - DEBUG_LOG(m_logger) << "Adding substruct topic " << ctti.name; - DataTopicWrapper *dtw = subTopicsWrapper->addDataTopic(ctti); - dtw->setRecordTableInformation(cti); - exportedSubTopics += dti->idlType(); - } - } - m_ctx->insert(QStringLiteral("serializeTopics"), subTopicsWrapper); - - QVector jsonTopics; - const QMap> jsonExport = m_structBuilder->jsonStructureInformation(); - for (auto it = jsonExport.begin(); it != jsonExport.end(); ++it) - { - MemberInformationListWrapper *mil = new MemberInformationListWrapper(it.key(), it.value(), this); - jsonTopics += mil; - - } - m_ctx->insert(QStringLiteral("jsonTopics"), QVariant::fromValue(jsonTopics)); - - return true; -} - -bool CppTopicGeneratorGrantlee::getSpecialColumns(const CompleteTopicTypeInformation &tti, - QVector &keyColumns, - QVector &trColumns) const -{ - DEBUG_LOG(m_logger) << "Get special columns for " << tti.name << " - base is " << tti.baseValueType; - if (!tti.isValid()) - { - ERROR_LOG(m_logger) << "tti is not valid ..."; - return false; - } - for (const IdlData &ci : tti.members) - { - if (ci.isKey) - keyColumns += ci; - if (ci.isTranslatable) - trColumns += ci; - } - if (tti.baseValueType.isEmpty()) - return true; - return getSpecialColumns(topicTypeInformationByDerivedTopic(tti), keyColumns, trColumns); -} - - - -bool CppTopicGeneratorGrantlee::initFunctionTopics() -{ - - const QVector functionTopics = m_reader->getFunctionTopics(); - FunctionTopicsWrapper *functionTopicsWrapper(new FunctionTopicsWrapper(QStringLiteral("functionTopics"), this)); - for (const FunctionTopic &ft : functionTopics) - { - if (!handleStruct(ft, functionTopicsWrapper)) - return false; - } - - m_ctx->insert(QStringLiteral("functionTopics"), functionTopicsWrapper); - return true; -} - -bool CppTopicGeneratorGrantlee::handleStruct(const FunctionTopic &ft, - FunctionTopicsWrapper *functionTopicsWrapper) -{ - DEBUG_LOG(m_logger) << "handling FunctionTopic: " << ft.name; - - const CompleteTopicTypeInformation siRequest = m_structBuilder->topicTypeInformationByStructType(ft.requestTopicType); - if (!siRequest.isValid()) - { - WARNING_LOG(m_logger) << "Could not get topic informations about request topic " - << ft.requestTopicName; - return false; - } - - const CompleteTopicTypeInformation siReply = m_structBuilder->topicTypeInformationByStructType(ft.replyTopicType); - if (!siReply.isValid()) - { - WARNING_LOG(m_logger) << "Could not get topic informations about reply topic " - << ft.replyTopicType; - return false; - } - - FunctionTopicWrapper *ftw = functionTopicsWrapper->addFunctionTopic(ft, siRequest, siReply); - const QByteArray hash = QCryptographicHash::hash(ft.requestTopicName.toUtf8(), QCryptographicHash::Sha3_512).toHex().left(16); - ftw->setReplyHandlerHash(QStringLiteral("0x") + QString::fromLatin1(hash) + QStringLiteral("LL")); - if (!addRequesterRequestParams(ft.name, siRequest, ftw) || - !addRequesterReplyParams(ft.name, siReply, ftw)) - return false; - - return true; -} - -bool CppTopicGeneratorGrantlee::addRequesterRequestParams(const QString &request_name, - const CompleteTopicTypeInformation &request, - FunctionTopicWrapper *ftw) -{ - if (request.hasBaseValue()) - { - const CompleteTopicTypeInformation siBase = m_structBuilder->topicTypeInformationByStructType(request.baseValueType); - if (!siBase.isValid()) - { - WARNING_LOG(m_logger) << "Could not get topic informations about base request topic " - << request.baseValueType; - return false; - } - - if (!addRequesterRequestParams(request_name, siBase, ftw)) - return false; - } - ftw->addRequestParams(request.members); - - return true; -} - -bool CppTopicGeneratorGrantlee::addRequesterReplyParams(const QString &request_name, - const CompleteTopicTypeInformation &reply, - FunctionTopicWrapper *ftw) -{ - if (reply.hasBaseValue()) - { - const CompleteTopicTypeInformation siBase = m_structBuilder->topicTypeInformationByStructType(reply.baseValueType); - if (!siBase.isValid()) - { - WARNING_LOG(m_logger) << "Could not get topic informations about base reply topic " - << reply.baseValueType; - return false; - } - - if (!addRequesterReplyParams(request_name, siBase, ftw)) - return false; - } - ftw->addReplyParams(reply.members); - - return true; -} - - - -bool CppTopicGeneratorGrantlee::handleIdlStruct(const CompleteTopicTypeInformation &csi, - QSet &idlStructNames, - QVector &idlStructs) -{ - if (!csi.isValid()) - return true; - - if (idlStructNames.contains(csi.name)) - return true; - - - - - if (csi.hasBaseValue()) - { - const CompleteTopicTypeInformation ctti = m_structBuilder->topicTypeInformationByStructType(csi.baseValueType, false); - if (ctti.isValid()) - { - if (ctti.extensibility == dds::core::xtypes::ExtensibilityKind_def::FINAL) - { - ERROR_LOG(m_logger) << "Base struct (" << ctti.name - << ") has final extensibility - this is not allowed"; - return false; - } - if (!idlStructNames.contains(csi.baseValueType)) - { - if (!handleIdlStruct(ctti, idlStructNames, idlStructs)) - return false; - } - } - else - { - const int idx = csi.baseValueType.indexOf(QLatin1String("::")); - if (idx < 0) - { - ERROR_LOG(m_logger) << "idlStruct '" << csi.baseValueType - << "' not found in idl-file"; - return false; - } - } - } - - - for (const IdlData &idlData : csi.members) - { - QString idlType; - DEBUG_LOG(m_logger) << "member: " << idlData.type << " - " << idlData.name - << " " << idlData.dataTypeInfo->isStructure() - << " " << idlData.dataTypeInfo->isSequence(); - if (idlData.dataTypeInfo->isStructure()) - idlType = idlData.dataTypeInfo->idlType(); - else if (idlData.dataTypeInfo->isSequence()) - { - if (idlData.dataTypeInfo->innerDataType()->isStructure()) - idlType = idlData.dataTypeInfo->innerDataType()->idlType(); - } - if (idlType.isEmpty()) - continue; - if (!handleIdlStruct(m_structBuilder->topicTypeInformationByStructType(idlType, - false), - idlStructNames, - idlStructs)) - return false; - } - - - DEBUG_LOG(m_logger) << "Current structName: " << csi.name; - idlStructs += new IdlStructWrapper(csi, this); - - - idlStructNames.insert(csi.name); - - return true; -} - -bool CppTopicGeneratorGrantlee::initIdlStructs() -{ - - const QVector csiData = m_structBuilder->topicTypeInformations(); - - QSet idlStructNames; - QVector idlStructs; - idlStructs.reserve(csiData.size()); - for (const CompleteTopicTypeInformation &csi : csiData) - { - if (!handleIdlStruct(csi, idlStructNames, idlStructs)) - return false; - } - - m_ctx->insert(QStringLiteral("idl_structs"), QVariant::fromValue(idlStructs)); - - const QVector enumerations = m_structBuilder->enumInformations(); - QVector idlEnumerations; - idlEnumerations.reserve(enumerations.size()); - for (const EnumInformation &ei : enumerations) - idlEnumerations += new IdlEnumWrapper(ei, this); - m_ctx->insert(QStringLiteral("idl_enums"), QVariant::fromValue(idlEnumerations)); - - - exportUsedServices(m_reader); - return true; -} - - -CompleteTopicTypeInformation CppTopicGeneratorGrantlee::topicTypeInformationByDerivedTopic(const CompleteTopicTypeInformation &tti) const -{ - if (tti.baseValueType.isEmpty()) - return CompleteTopicTypeInformation(); - QString baseValueType = tti.baseValueType; - - - if (tti.name.contains(QLatin1String("::")) && - !tti.baseValueType.contains(QLatin1String("::"))) - { - - const QVector sl = tti.name.splitRef(QStringLiteral("::")); - baseValueType = sl.at(0) + QLatin1String("::") + tti.baseValueType; - } - DEBUG_LOG(m_logger) << "topicTypeInformationByStructName(" << baseValueType << ")"; - return m_structBuilder->topicTypeInformationByStructType(baseValueType); -} diff --git a/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h b/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h deleted file mode 100755 index c8f3b111c..000000000 --- a/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h +++ /dev/null @@ -1,47 +0,0 @@ - - -#pragma once - -#include "GeneratorGrantleeBase.h" -#include "StructBuilder.h" -#include "GrantleeWrappers.h" -#include "codegen_export.h" - -namespace CodeGen -{ - -class CppTopicGeneratorGrantlee final : public GeneratorGrantleeBase -{ - Q_OBJECT -public: - explicit CppTopicGeneratorGrantlee(); - - bool init(const XmlReader *reader, StructBuilder *structBuilder) override; - -private: - - bool initDataTopics(); - bool getSpecialColumns(const CompleteTopicTypeInformation &tti, - QVector &keyColumns, - QVector &trColumns) const; - - bool initFunctionTopics(); - bool handleStruct(const FunctionTopic &ft, - FunctionTopicsWrapper *functionTopicsWrapper); - bool addRequesterRequestParams(const QString &request_name, - const CompleteTopicTypeInformation &request, - FunctionTopicWrapper *ftw); - bool addRequesterReplyParams(const QString &request_name, - const CompleteTopicTypeInformation &reply, - FunctionTopicWrapper *ftw); - - bool handleIdlStruct(const CompleteTopicTypeInformation &csi, - QSet &idlStructNames, - QVector &idlStructs); - bool initIdlStructs(); - - CompleteTopicTypeInformation topicTypeInformationByDerivedTopic(const CompleteTopicTypeInformation &tti) const; - - const XmlReader *m_reader = nullptr; -}; -} diff --git a/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp b/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp deleted file mode 100755 index 2397892db..000000000 --- a/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp +++ /dev/null @@ -1,57 +0,0 @@ - - - -#include "DaoGeneratorGrantlee.h" -#include "StructBuilder.h" -#include -#include - -using namespace CodeGen; - -DaoGeneratorGrantlee::DaoGeneratorGrantlee() - : GeneratorGrantleeBase("CodeGen.DaoGeneratorGrantlee") -{} - -bool DaoGeneratorGrantlee::init(const XmlReader *reader, - StructBuilder *structBuilder) -{ - if (!GeneratorGrantleeBase::initInternal(reader, structBuilder)) - return false; - - - if (reader->isServiceExerciseIndependent()) - m_ctx->insert(QStringLiteral("isExerciseIndependent"), QLatin1String("1")); - - bool bHasDbNotifications = false; - QVector vlServiceStructs; - QVector vlStaticDataStructs; - const QVector csiData = structBuilder->structInformations(); - vlServiceStructs.reserve(csiData.size()); - vlStaticDataStructs.reserve(csiData.size()); - for (const CompleteStructInformation &csi : csiData) - { - DbStructWrapper *dbStructWrapper(new DbStructWrapper(csi, this)); - - if (csi.hasStaticContent) - vlStaticDataStructs += dbStructWrapper; - else - vlServiceStructs += dbStructWrapper; - if (!csi.dbNotifications.isEmpty()) - bHasDbNotifications = true; - } - - if (structBuilder->needPoint3d()) - m_ctx->insert(QStringLiteral("need_point3d"), QLatin1String("1")); - if (structBuilder->needPgArrayInclude()) - m_ctx->insert(QStringLiteral("need_pgarray"), QLatin1String("1")); - m_ctx->insert(QStringLiteral("cpp_structs"), QVariant::fromValue(vlServiceStructs)); - m_ctx->insert(QStringLiteral("data_structs"), QVariant::fromValue(vlStaticDataStructs)); - if (bHasDbNotifications) - m_ctx->insert(QStringLiteral("has_db_notifications"), QLatin1String("1")); - - m_ctx->insert(QStringLiteral("library_target"), QStringLiteral("COMMON")); - - exportUsedServices(reader); - - return true; -} diff --git a/Lts/Common/CodeGen/DaoGeneratorGrantlee.h b/Lts/Common/CodeGen/DaoGeneratorGrantlee.h deleted file mode 100755 index 06b48b586..000000000 --- a/Lts/Common/CodeGen/DaoGeneratorGrantlee.h +++ /dev/null @@ -1,25 +0,0 @@ - - -#pragma once - -#include "GeneratorGrantleeBase.h" -#include "StructBuilder.h" -#include "GrantleeWrappers.h" -#include "codegen_export.h" - -namespace CodeGen -{ -struct CompleteStructInformation; -class StructBuilder; -class XmlReader; - - -class DaoGeneratorGrantlee final : public GeneratorGrantleeBase -{ - Q_OBJECT -public: - explicit DaoGeneratorGrantlee(); - - bool init(const XmlReader *reader, StructBuilder *structBuilder) override; -}; -} diff --git a/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp b/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp deleted file mode 100755 index 53a472082..000000000 --- a/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp +++ /dev/null @@ -1,59 +0,0 @@ - - - -#include "DatabaseGeneratorGrantlee.h" -#include "StructBuilder.h" -#include -#include - -using namespace CodeGen; - -DatabaseGeneratorGrantlee::DatabaseGeneratorGrantlee(bool targetIsServer) - : GeneratorGrantleeBase("CodeGen.CppStructGeneratorGrantlee") - , m_targetIsServer(targetIsServer) -{} - -bool DatabaseGeneratorGrantlee::init(const XmlReader *reader, - StructBuilder *structBuilder) -{ - if (!GeneratorGrantleeBase::initInternal(reader, structBuilder)) - return false; - - - if (reader->isServiceExerciseIndependent()) - m_ctx->insert(QStringLiteral("isExerciseIndependent"), QLatin1String("1")); - - bool bHasDbNotifications = false; - QVector vlServiceStructs; - QVector vlStaticDataStructs; - const QVector csiData = structBuilder->structInformations(); - vlServiceStructs.reserve(csiData.size()); - vlStaticDataStructs.reserve(csiData.size()); - for (const CompleteStructInformation &csi : csiData) - { - DbStructWrapper *dbStructWrapper(new DbStructWrapper(csi, this)); - - if (csi.hasStaticContent) - vlStaticDataStructs += dbStructWrapper; - else - vlServiceStructs += dbStructWrapper; - if (!csi.dbNotifications.isEmpty()) - bHasDbNotifications = true; - } - addCustomFunctions(reader->getCustomFunctions()); - - if (structBuilder->needPoint3d()) - m_ctx->insert(QStringLiteral("need_point3d"), QLatin1String("1")); - if (structBuilder->needPgArrayInclude()) - m_ctx->insert(QStringLiteral("need_pgarray"), QLatin1String("1")); - m_ctx->insert(QStringLiteral("cpp_structs"), QVariant::fromValue(vlServiceStructs)); - m_ctx->insert(QStringLiteral("data_structs"), QVariant::fromValue(vlStaticDataStructs)); - if (bHasDbNotifications) - m_ctx->insert(QStringLiteral("has_db_notifications"), QLatin1String("1")); - - m_ctx->insert(QStringLiteral("library_target"), m_targetIsServer ? QStringLiteral("SERVER") : QStringLiteral("COMMON")); - - exportUsedServices(reader); - - return true; -} diff --git a/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h b/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h deleted file mode 100755 index 8204b5d8f..000000000 --- a/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h +++ /dev/null @@ -1,27 +0,0 @@ - - -#pragma once - -#include "GeneratorGrantleeBase.h" -#include "StructBuilder.h" -#include "GrantleeWrappers.h" -#include "codegen_export.h" - -namespace CodeGen -{ -struct CompleteStructInformation; -class StructBuilder; -class XmlReader; - - -class DatabaseGeneratorGrantlee final : public GeneratorGrantleeBase -{ - Q_OBJECT -public: - explicit DatabaseGeneratorGrantlee(bool targetIsServer); - - bool init(const XmlReader *reader, StructBuilder *structBuilder) override; -private: - bool m_targetIsServer; -}; -} diff --git a/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp b/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp deleted file mode 100755 index 704eb6170..000000000 --- a/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp +++ /dev/null @@ -1,200 +0,0 @@ - - - -#include "GeneratorGrantleeBase.h" -#include "XmlReader.h" -#include "GrantleeWrappers.h" -#include -#include -#include -#include - -using namespace CodeGen; -GeneratorGrantleeBase::GeneratorGrantleeBase(const char *loggerName) - : m_engine(new Grantlee::Engine) - , m_ctx(new Grantlee::Context) - , m_structBuilder(nullptr) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName)) -{ -} - -GeneratorGrantleeBase::~GeneratorGrantleeBase() -{ - delete m_engine; - delete m_ctx; -} - -bool GeneratorGrantleeBase::initInternal(const XmlReader *reader, StructBuilder *structBuilder) -{ - m_structBuilder = structBuilder; - - if (reader) - { - m_ctx->insert(QStringLiteral("namespace"), reader->getServiceNamespace()); - m_ctx->insert(QStringLiteral("namespace_abbr"), reader->getServiceAbbr().toLower()); - m_ctx->insert(QStringLiteral("namespace_abbr_upper"), reader->getServiceAbbr().toUpper()); - if (reader->getServiceNamespace() == QLatin1String("Core")) - m_ctx->insert(QStringLiteral("include_prefix"), QStringLiteral("DDS/%1").arg(reader->getServiceNamespace())); - else - m_ctx->insert(QStringLiteral("include_prefix"), reader->getServiceNamespace()); - m_ctx->insert(QStringLiteral("custom_record_replay"), reader->hasCustomRecordReplayPlugin()); - } - return true; -} - -bool GeneratorGrantleeBase::setTemplate(const QString &templateName, const QString &tmpl) -{ - m_template = m_engine->newTemplate(tmpl, QStringLiteral("myTemplate")); - if (m_template->error()) - { - WARNING_LOG(m_logger) << "Error parsing template '" << templateName - << "': " << m_template->errorString(); - return false; - } - return true; -} - -QString GeneratorGrantleeBase::render() const -{ - QString out = m_template->render(m_ctx); - QRegExp rx(QLatin1String(";\\n([ ]+\\n)*")); - out = out.replace(rx, QStringLiteral(";\n")); - return out; -} - -QString GeneratorGrantleeBase::errorString() const -{ - return m_template->errorString(); -} - - -bool GeneratorGrantleeBase::generateData(const QString &templateName, - const QString &outDirectory) -{ - QString content; - if (!readFile(templateName, content)) - return false; - if (!setTemplate(templateName, content)) - return false; - - content = render(); - - QString outFilename; - if (!getFilenameFromData(content, outFilename)) - return false; - - - QFile f(outDirectory + QLatin1Char('/') + outFilename); - if (!f.open(QIODevice::WriteOnly)) - { - ERROR_LOG(m_logger) << "Error opening file '" << f.fileName() << "' for writing: " - << f.errorString() << "(" << f.error() << ")"; - return false; - } - const QByteArray outData = content.toUtf8(); - f.write(outData); - f.close(); - - DEBUG_LOG(m_logger) << "Written data to " << f.fileName(); - return true; -} - -bool GeneratorGrantleeBase::readFile(const QString &filename, QString &data) const -{ - QFile f(filename); - if (!f.open(QIODevice::ReadOnly)) - { - ERROR_LOG(m_logger) << "Error opening file " << filename - << " - error: " << f.errorString(); - return false; - } - data = QString::fromUtf8(f.readAll()); - f.close(); - return true; -} - -bool GeneratorGrantleeBase::getFilenameFromData(const QString &dataToParse, QString &filename) const -{ - - const QString filenameMarker(QStringLiteral("// filename:")); - int idx = dataToParse.indexOf(filenameMarker); - if (idx < 0) - { - ERROR_LOG(m_logger) << "Could not find marker '" << filenameMarker - << "' in data!"; - return false; - } - idx += filenameMarker.length() + 1; - int endIdx = dataToParse.indexOf(QLatin1Char('\n'), idx); - if (endIdx < 0) - { - ERROR_LOG(m_logger) << "Could not find end marker '\\n' in data!"; - return false; - } - filename = dataToParse.midRef(idx, endIdx - idx).trimmed().toString(); - - return true; -} - -void GeneratorGrantleeBase::addCustomFunctions(const QVector &customFunctions) -{ - - QVariantList vlCustomDbFunctions; - QVariantList vlCustomDtcFunctions; - vlCustomDbFunctions.reserve(customFunctions.size()); - vlCustomDtcFunctions.reserve(customFunctions.size()); - for (const CustomServiceFunctionInformation &csfi : customFunctions) - { - const QString signature(csfi.returnValue + QLatin1Char(' ') + - csfi.methodName + QLatin1Char('(') + csfi.signature + QLatin1Char(')') + - (csfi.isConst ? QStringLiteral(" const") : QString())); - CustomFunctionWrapper *customFunctionWrapper(new CustomFunctionWrapper(signature, this)); - switch (csfi.type) - { - case CustomServiceFunctionInformation::Type::Db: - vlCustomDbFunctions += QVariant::fromValue(customFunctionWrapper); - break; - case CustomServiceFunctionInformation::Type::Dtc: - vlCustomDtcFunctions += QVariant::fromValue(customFunctionWrapper); - break; - case CustomServiceFunctionInformation::Type::DbNotify: - - WARNING_LOG(m_logger) << "Internal error - Trying to handle DbNotify in addCustomFunctions(): " - << csfi.methodName; - delete customFunctionWrapper; - break; - } - DEBUG_LOG(m_logger) << "Added custom db function " << csfi.methodName; - } - m_ctx->insert(QStringLiteral("custom_dtc_procs"), vlCustomDtcFunctions); - m_ctx->insert(QStringLiteral("custom_db_procs"), vlCustomDbFunctions); -} - -void GeneratorGrantleeBase::exportUsedServices(const XmlReader *reader) -{ - IdlServicesWrapper *idlServicesWrapper(new IdlServicesWrapper(QString(), this)); - const QMap usedServices = reader->usedServices(); - for (auto it = usedServices.begin(); it != usedServices.end(); ++it) - { - - if (!it.value()) - continue; - const QString &svc = it.key(); - const QString fullPath = reader->idlFileForService(svc); - const int idx = fullPath.lastIndexOf(QLatin1Char('.')); - if (idx < 0) - { - ERROR_LOG(m_logger) << "Could not parse file path: " << fullPath; - continue; - } - const QString idlFile = fullPath.left(idx); - if (idlFile.isEmpty()) - { - WARNING_LOG(m_logger) << "Could not find idl file for service " << svc; - continue; - } - idlServicesWrapper->addService(svc, idlFile); - DEBUG_LOG(m_logger) << "Added service " << svc; - } - m_ctx->insert(QStringLiteral("ext_services"), idlServicesWrapper); -} diff --git a/Lts/Common/CodeGen/GeneratorGrantleeBase.h b/Lts/Common/CodeGen/GeneratorGrantleeBase.h deleted file mode 100755 index a06998d91..000000000 --- a/Lts/Common/CodeGen/GeneratorGrantleeBase.h +++ /dev/null @@ -1,53 +0,0 @@ - - -#pragma once - -#include "codegen_export.h" -#include "XmlReader.h" -#include -#include - -namespace NGL -{ -class INGLLogger; -} - -namespace Grantlee -{ -class Context; -class Engine; -class TemplateImpl; -using Template = QSharedPointer; -} -namespace CodeGen -{ -class StructBuilder; - -class GeneratorGrantleeBase : public QObject -{ - Q_OBJECT -public: - virtual ~GeneratorGrantleeBase(); - virtual bool init(const XmlReader *reader, StructBuilder *structBuilder) = 0; - bool setTemplate(const QString &templateName, const QString &tmpl); - QString render() const; - QString errorString() const; - bool generateData(const QString &templateName, - const QString &outDirectory); - -protected: - explicit GeneratorGrantleeBase(const char *loggerName); - - bool initInternal(const XmlReader *reader, StructBuilder *structBuilder); - bool readFile(const QString &filename, QString &data) const; - bool getFilenameFromData(const QString &dataToParse, QString &filename) const; - void addCustomFunctions(const QVector &customFunctions); - void exportUsedServices(const XmlReader *reader); -protected: - Grantlee::Engine *m_engine; - Grantlee::Context *m_ctx; - Grantlee::Template m_template; - StructBuilder *m_structBuilder; - NGL::INGLLogger &m_logger; -}; -} diff --git a/Lts/Common/CodeGen/GrantleeWrappers.cpp b/Lts/Common/CodeGen/GrantleeWrappers.cpp deleted file mode 100755 index b2317a3cc..000000000 --- a/Lts/Common/CodeGen/GrantleeWrappers.cpp +++ /dev/null @@ -1,226 +0,0 @@ - - - -#include "GrantleeWrappers.h" -#include - -using namespace CodeGen; - -QString IdlStructWrapper::extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity) -{ - switch (extensiblity.underlying()) - { - case dds::core::xtypes::ExtensibilityKind_def::FINAL: - return QStringLiteral("FINAL_EXTENSIBILITY"); - case dds::core::xtypes::ExtensibilityKind_def::MUTABLE: - return QStringLiteral("MUTABLE_EXTENSIBILITY"); - case dds::core::xtypes::ExtensibilityKind_def::EXTENSIBLE: - return QStringLiteral("EXTENSIBLE_EXTENSIBILITY"); - } - Q_ASSERT_X(false, "IdlStructWrapper::extensibility2String", "Unknown extensiblity kind"); - return QString(); -} - -QString DataTopicWrapper::typeFq() const -{ - const QString &t = type(); - const QString &s = serviceName(); - if (s.isEmpty()) - return t; - if (t.startsWith(s)) - return t; - return s + QStringLiteral("::") + t; -} - -QString MemberInformationWrapper::ddsToQVariant() const -{ - return QStringLiteral("toQVariant(data.") + cppMemberName() + QStringLiteral("())"); -} - -QString MemberInformationWrapper::qVariantToDds() const -{ - const QString ddsType = isOptional() ? - (QStringLiteral("dds::core::optional<") + cppDdsType() + QLatin1Char('>')) : - (cppDdsType()); - - return QStringLiteral("data.") + cppMemberName() + - QStringLiteral("() = fromQVariant<") + ddsType + QStringLiteral(">(q.value(i++))"); -} - -QString MemberInformationWrapper::ddsToJsonElement() const -{ - - - - - - - - - - if (cppMemberName().isEmpty()) - return QStringLiteral("obj[QStringLiteral(\"") + cppType() + QStringLiteral("\")] = element;"); - - if (!isOptional()) - return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + - cppToJsonPre() + QStringLiteral("element.") + cppMemberName() + QStringLiteral("()") + cppToJsonPost(); - - return QStringLiteral("if (element.") + cppMemberName() + QStringLiteral("().is_set()) ") + - QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + - cppToJsonPre() + QStringLiteral("element.") + cppMemberName() + QStringLiteral("().get()") + cppToJsonPost(); -} - -QString MemberInformationWrapper::jsonElementToDds() const -{ - - - - - - - - - - - - - - if (cppMemberName().isEmpty()) - return QStringLiteral("element = ") + - cppFromQvPre2() + QStringLiteral("objElement.value(QStringLiteral(\"") + cppType() + QStringLiteral("\"))") + cppFromJsonPost(); - - QString ret(QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"") + cppMemberName() + QStringLiteral("\"))); ")); - if (isOptional()) - ret += QStringLiteral("if (!val.isNull()) "); - - ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral("(") + - cppFromJsonPre2() + QStringLiteral("val") + cppFromJsonPost() + QStringLiteral(")"); - - return ret; -} - -QString MemberInformationWrapper::dbToJsonElement() const -{ - - - - - - - - - - if (cppMemberName().isEmpty()) - return QStringLiteral("obj[QStringLiteral(\"") + cppType() + QStringLiteral("\")] = element;"); - - if (isOptional() || cppType() == QStringLiteral("QDateTime")) - { - QString ret = QStringLiteral("if (") + ((cppType() == QStringLiteral("QDateTime")) ? QString() : QStringLiteral("!")) + - QStringLiteral("element.") + cppMemberName() + - ((cppType() == QStringLiteral("QDateTime")) ? QStringLiteral(".isValid()) ") : QStringLiteral(".isNull()) ")) + - QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = "); - if (isOptional()) - return ret += cppFromQvPre2() + QStringLiteral("element.") + cppMemberName() + cppFromQvPost(); - } - - return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + - cppToJsonPre() + QStringLiteral("element.") + cppMemberName() + cppToJsonPost(); -} - -QString MemberInformationWrapper::jsonElementToDb() const -{ - - - - - - - - - - - - - - if (cppMemberName().isEmpty()) - return QStringLiteral("element = ") + - cppFromQvPre2() + QStringLiteral("objElement.value(QStringLiteral(\"") + cppType() + QStringLiteral("\"))") + cppFromJsonPost(); - - QString ret(QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"") + cppMemberName() + QStringLiteral("\"))); ")); - if (isOptional()) - ret += QStringLiteral("if (!val.isUndefined()) "); - - ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral(" = ") + - cppFromJsonPre2() + QStringLiteral("val") + cppFromJsonPost(); - - return ret; -} - -QVariantList DbStructWrapper::insertStructures() -{ - - QStringList tableNames; - QMap> structures; - QMap primaryKeys; - QMap fkComplexTableKeys; - - tableNames.reserve(5); - for (const QVariant &v : qAsConst(m_member)) - { - MemberInformationWrapper *mi = v.value(); - const QString &tableName = mi->info().db.table; - if (mi->info().db.usedForInsert) - { - auto &tnStructure = structures[tableName]; - tnStructure.append(mi); - if (!tableNames.contains(tableName)) - tableNames.push_back(tableName); - } - if (mi->info().fk) - fkComplexTableKeys[mi->info().fk->destination.tableName] = mi; - if (mi->info().db.isPrimaryKey) - { - if (!primaryKeys.contains(tableName)) - primaryKeys[tableName] = mi; - } - if (mi->info().db.isAssignmentForPrimaryTablePk) - fkComplexTableKeys[mi->info().db.table] = mi; - } - - - QVariantList vl; - vl.reserve(tableNames.size()); - for (int i = 0; i < tableNames.size(); ++i) - { - const QString &tn = tableNames.at(i); - MemberInformationWrapper *serialMember = fkComplexTableKeys.value(tn); - MemberInformationWrapper *baseId = nullptr; - MemberInformationWrapper *primaryKey = primaryKeys.value(tn); - if (i == 0 && tableNames.size() > 1) - baseId = primaryKeys.value(tn); - - - - if (tableNames.size() == 1) - { - baseId = nullptr; - - if (!primaryKey) - { - NGL::INGLLogger &logger = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("DbStructWrapper"); - WARNING_LOG(logger) << "Table " << tn << " has no primary key!"; - } - else - { - if (primaryKey->dbHasSequence()) - serialMember = primaryKey; - } - } - - const QVector members = structures.value(tn); - TableWrapper *tw = new TableWrapper(tn, members, serialMember, baseId, primaryKey, this); - vl += QVariant::fromValue(tw); - } - - return vl; -} diff --git a/Lts/Common/CodeGen/GrantleeWrappers.h b/Lts/Common/CodeGen/GrantleeWrappers.h deleted file mode 100755 index 5e5b02db0..000000000 --- a/Lts/Common/CodeGen/GrantleeWrappers.h +++ /dev/null @@ -1,643 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include "StructBuilder.h" - -namespace CodeGen -{ -class NameWrapper : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString name READ name) - Q_PROPERTY(QString nameCc READ nameCc) - Q_PROPERTY(QString nameFlc READ nameFlc) -public: - NameWrapper(const QString &name, QObject *parent) - : QObject(parent) - , m_name(name) - {} - - QString name() const { return m_name; } - QString nameCc() const { return StructBuilder::camelCaseString(name(), true); } - QString nameFlc() const { return StructBuilder::firstCharLowercase(nameCc()); } -private: - const QString m_name; -}; - -class VariantListWrapper : public NameWrapper -{ - Q_OBJECT - Q_PROPERTY(QVariantList member READ member) -public: - - using NameWrapper::NameWrapper; - ~VariantListWrapper() - { - for (const QVariant &v : qAsConst(m_member)) - { - auto ptr = v.value(); - delete ptr; - } - } - - QVariantList member() const { return m_member; } -protected: - QVariantList m_member; -}; - - -class MemberInformationWrapper final : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString cppMemberName READ cppMemberName) - Q_PROPERTY(QString cppMemberNameCc READ cppMemberNameCc) - Q_PROPERTY(QString cppStructName READ cppStructName) - Q_PROPERTY(QString cppType READ cppType) - Q_PROPERTY(QString cppToQvPre READ cppToQvPre) - Q_PROPERTY(QString cppToQvPost READ cppToQvPost) - Q_PROPERTY(QString cppFromQvPre1 READ cppFromQvPre1) - Q_PROPERTY(QString cppFromQvPre2 READ cppFromQvPre2) - Q_PROPERTY(QString cppFromQvPost READ cppFromQvPost) - Q_PROPERTY(QString cppDefault READ cppDefault) - Q_PROPERTY(QString dbTableName READ dbTableName) - Q_PROPERTY(QString dbColumnName READ dbColumnName) - Q_PROPERTY(QString dbColumnNameView READ dbColumnNameView) - Q_PROPERTY(QString ddsToQVariant READ ddsToQVariant) - Q_PROPERTY(QString qVariantToDds READ qVariantToDds) - Q_PROPERTY(QString ddsToJsonElement READ ddsToJsonElement) - Q_PROPERTY(QString jsonElementToDds READ jsonElementToDds) - Q_PROPERTY(QString dbToJsonElement READ dbToJsonElement) - Q_PROPERTY(QString jsonElementToDb READ jsonElementToDb) - Q_PROPERTY(bool dbHasSequence READ dbHasSequence) - Q_PROPERTY(bool isOptional READ isOptional) -public: - MemberInformationWrapper(const MemberInformation &mi, QObject *parent) - : QObject(parent) - , m_mi(mi) - {} - - QString cppMemberName() const { return m_mi.cpp.memberName; } - QString cppMemberNameCc() const { return StructBuilder::camelCaseString(m_mi.cpp.memberName, true); } - QString cppStructName() const { return m_mi.cpp.structName; } - QString cppType() const { return m_mi.cpp.type; } - QString cppToQvPre() const { return m_mi.cpp.toVariantPre; } - QString cppToQvPost() const { return m_mi.cpp.toVariantPost; } - QString cppFromQvPre1() const { return m_mi.cpp.fromVariantPre1; } - QString cppFromQvPre2() const { return m_mi.cpp.fromVariantPre2; } - QString cppFromQvPost() const { return m_mi.cpp.fromVariantPost; } - QString cppDefault() const { return m_mi.cpp.defaultValue; } - QString dbTableName() const { return m_mi.db.table; } - QString dbColumnName() const { return m_mi.db.columnName; } - QString dbColumnNameView() const { return m_mi.db.getColumnNameView(); } - QString cppDdsType() const { return m_mi.cpp.ddsType.isEmpty() ? cppType() : m_mi.cpp.ddsType; } - QString ddsToQVariant() const; - QString qVariantToDds() const; - QString ddsToJsonElement() const; - QString jsonElementToDds() const; - QString dbToJsonElement() const; - QString jsonElementToDb() const; - bool dbHasSequence() const { return m_mi.db.hasSequence; } - bool isOptional() const { return m_mi.cpp.isOptional; } - - - QString cppToJsonPre() const { return m_mi.cpp.toJsonPre.isNull() ? m_mi.cpp.toVariantPre : m_mi.cpp.toJsonPre; } - QString cppToJsonPost() const { return m_mi.cpp.toJsonPost.isNull() ? m_mi.cpp.toVariantPost : m_mi.cpp.toJsonPost; } - QString cppFromJsonPre2() const { return m_mi.cpp.fromJsonPre2.isNull() ? m_mi.cpp.fromVariantPre2: m_mi.cpp.fromJsonPre2; } - QString cppFromJsonPost() const { return m_mi.cpp.fromJsonPost.isNull() ? m_mi.cpp.fromVariantPost : m_mi.cpp.fromJsonPost; } - - MemberInformation info() const - { - return m_mi; - } -private: - const MemberInformation m_mi; -}; - -class MemberInformationListWrapper : public VariantListWrapper -{ - Q_OBJECT - Q_PROPERTY(QVariantList cppMember READ cppMember) - Q_PROPERTY(QVariantList jsonMember READ jsonMember) - Q_PROPERTY(QVariantList fkMember READ fkMember) - Q_PROPERTY(QVariantList selectRemoveMember READ selectRemoveMember) - Q_PROPERTY(QVariantList viewMember READ viewMember) - Q_PROPERTY(MemberInformationWrapper *ddsKey READ ddsKey) - Q_PROPERTY(MemberInformationWrapper *primaryKey READ primaryKey) -public: - explicit MemberInformationListWrapper(const QString &name, const QVector &member, QObject *parent) - : VariantListWrapper(name, parent) - { - for (const MemberInformation &mi : member) - m_member += QVariant::fromValue(new MemberInformationWrapper(mi, this)); - } - - QVariantList memberByFilter(const std::function &acceptFilter) const - { - QVariantList vl; - vl.reserve(m_member.size()); - for (const QVariant &v : m_member) - { - MemberInformationWrapper *mi = v.value(); - if (mi && acceptFilter(mi->info())) - vl += v; - } - return vl; - } - - QVariantList cppMember() const - { - return memberByFilter([](const MemberInformation &mi) { return mi.cpp.usedForCppStruct; }); - } - QVariantList jsonMember() const - { - return memberByFilter([](const MemberInformation &mi) { return mi.cpp.usedForJson; }); - } - QVariantList fkMember() const - { - return memberByFilter([](const MemberInformation &mi) { return mi.db.isForeignKey; }); - } - QVariantList selectRemoveMember() const - { - return memberByFilter([](const MemberInformation &mi) { return mi.db.generateSelectRemove; }); - } - QVariantList viewMember() const - { - return memberByFilter([](const MemberInformation &mi) { return mi.db.usedForView; }); - } - MemberInformationWrapper *ddsKey() const - { - for (const QVariant &v : m_member) - { - MemberInformationWrapper *mi = v.value(); - if (mi->info().isDdsKey) - return mi; - } - return nullptr; - } - MemberInformationWrapper *primaryKey() const - { - for (const QVariant &v : m_member) - { - MemberInformationWrapper *mi = v.value(); - if (mi->info().db.isPrimaryKey) - return mi; - } - return nullptr; - } -}; - - -class TableWrapper final : public NameWrapper -{ - Q_OBJECT - Q_PROPERTY(QVector member READ member) - - Q_PROPERTY(MemberInformationWrapper *serialMember READ serialMember) - - - Q_PROPERTY(MemberInformationWrapper *baseId READ baseId) - - Q_PROPERTY(MemberInformationWrapper *primaryKey READ primaryKey) -public: - - explicit TableWrapper(const QString &name, - const QVector &member, - MemberInformationWrapper *serialMember, - MemberInformationWrapper *baseId, - MemberInformationWrapper *primaryKey, - QObject *parent) - : NameWrapper(name, parent) - , m_member(member) - , m_serialMember(serialMember) - , m_baseId(baseId) - , m_primaryKey(primaryKey) - {} - - QVector member() const { return m_member; } - MemberInformationWrapper *serialMember() const { return m_serialMember; } - MemberInformationWrapper *baseId() const { return m_baseId; } - MemberInformationWrapper *primaryKey() const { return m_primaryKey; } - -private: - QVector m_member; - MemberInformationWrapper *m_serialMember; - MemberInformationWrapper *m_baseId; - MemberInformationWrapper *m_primaryKey; -}; - -class DbStructWrapper final : public MemberInformationListWrapper -{ - Q_OBJECT - Q_PROPERTY(QVariantList insertStructures READ insertStructures) - Q_PROPERTY(QString viewTableName READ viewTableName) - Q_PROPERTY(bool deleteWithoutPkAllowed READ deleteWithoutPkAllowed) - Q_PROPERTY(MemberInformationWrapper *primaryKey READ primaryKey) - Q_PROPERTY(QVariantList dbNotifications READ dbNotifications) - -public: - explicit DbStructWrapper(const CompleteStructInformation &csi, QObject *parent) - : MemberInformationListWrapper(csi.structName, csi.member, parent) - , m_csi(csi) - { - m_dbNotifications.reserve(m_csi.dbNotifications.size()); - for (const auto ¬ification : qAsConst(m_csi.dbNotifications)) - m_dbNotifications += QVariant(notification.methodName); - } - - MemberInformationWrapper *primaryKey() const - { - for (const QVariant &v : m_member) - { - MemberInformationWrapper *mi = v.value(); - if (mi->info().db.isPrimaryKey && mi->info().db.table == primaryTableName()) - return mi; - } - return nullptr; - } - QVariantList insertStructures(); - - bool deleteWithoutPkAllowed() const { return m_csi.tables.size() == 1; } - QVariantList dbNotifications() const { return m_dbNotifications; } - QString primaryTableName() const { return m_csi.primaryTable->name; } - QString viewTableName() const { return (m_csi.tables.size() == 1) ? primaryTableName() : m_csi.viewName; } -private: - const CompleteStructInformation m_csi; - QVariantList m_dbNotifications; -}; - -class CustomFunctionWrapper final : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString signature READ signature) -public: - explicit CustomFunctionWrapper(const QString &signature, QObject *parent) - : QObject(parent) - , m_signature(signature) - {} - QString signature() const { return m_signature; } -private: - const QString m_signature; -}; - - -class IdlEnumKeyValueWrapper final : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString key READ key) - Q_PROPERTY(int value READ value) -public: - explicit IdlEnumKeyValueWrapper(const EnumKeyValuePair &ekvp, QObject *parent) - : QObject(parent) - , m_ekvp(ekvp) - {} - - QString key() const { return m_ekvp.key; } - int value() const { return m_ekvp.value; } - -private: - const EnumKeyValuePair m_ekvp; -}; - -class IdlDataWrapper final : public NameWrapper -{ - Q_OBJECT - Q_PROPERTY(QString type READ type) - Q_PROPERTY(QString comment READ comment) - Q_PROPERTY(QString isKey READ isKey) - Q_PROPERTY(QString isOptional READ isOptional) - Q_PROPERTY(QString cppType READ cppType) - Q_PROPERTY(QString cppTypeCr READ cppTypeCr) - Q_PROPERTY(QString nameGetter READ nameGetter) -public: - IdlDataWrapper(const IdlData &data, - QObject *parent) - : NameWrapper(data.name, parent) - , m_data(data) - {} - - QString type() const { return m_data.dataTypeInfo->idlType(); } - QString comment() const { return m_data.comment; } - QString isKey() const { return m_data.isKey ? QStringLiteral(" \t //@key") : QString(); } - QString isOptional() const { return m_data.isOptional ? QStringLiteral(" \t //@Optional") : QString(); } - QString cppType() const { return m_data.dataTypeInfo->cppType(); } - QString cppTypeCr() const { return m_data.dataTypeInfo->cppTypeConstRef(); } - QString nameGetter() const { return m_data.name + (m_data.isOptional ? QStringLiteral("().get") : QString()); } -private: - const IdlData m_data; -}; - -class IdlEnumWrapper final : public VariantListWrapper -{ - Q_OBJECT -public: - explicit IdlEnumWrapper(const EnumInformation &ei, QObject *parent) - : VariantListWrapper(ei.enumName, parent) - { - m_member.reserve(ei.values.size()); - for (const auto &val : ei.values) - m_member += QVariant::fromValue(new IdlEnumKeyValueWrapper(val, this)); - } -}; - -class IdlStructWrapper final : public VariantListWrapper -{ - Q_OBJECT - Q_PROPERTY(QString comment READ comment) - Q_PROPERTY(QString baseValueType READ baseValueType) - Q_PROPERTY(QString extensibility READ extensibility) -public: - explicit IdlStructWrapper(const CompleteTopicTypeInformation &ctti, QObject *parent) - : VariantListWrapper(ctti.name, parent) - , m_ctti(ctti) - { - m_member.reserve(m_ctti.members.size()); - for (const auto &val : qAsConst(m_ctti.members)) - m_member += QVariant::fromValue(new IdlDataWrapper(val, this)); - } - - QString comment() const { return m_ctti.comment; } - QString baseValueType() const { return m_ctti.baseValueType; } - QString extensibility() const { return extensibility2String(m_ctti.extensibility); } - -private: - const CompleteTopicTypeInformation m_ctti; - static QString extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity); -}; - -class IdlServiceWrapper final : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString svcName READ svcName) - Q_PROPERTY(QString baseFilePath READ baseFilePath) -public: - IdlServiceWrapper(const QString &svcName, const QString &baseFilePath) - : m_svcName(svcName) - , m_baseFilePath(baseFilePath) - {} - QString svcName() const { return m_svcName; } - QString baseFilePath() const { return m_baseFilePath; } -private: - const QString m_svcName; - const QString m_baseFilePath; -}; - -class FunctionTopicWrapper final : public NameWrapper -{ - Q_OBJECT - Q_PROPERTY(QString onReply READ onReply) - Q_PROPERTY(QString onRequest READ onRequest) - Q_PROPERTY(QString onError READ onError) - Q_PROPERTY(bool isCoreFt READ isCoreFt) - Q_PROPERTY(bool routeInReplay READ routeInReplay) - - Q_PROPERTY(QString reply READ reply) - Q_PROPERTY(QString replyFlc READ replyFlc) - Q_PROPERTY(QString replyHandler READ replyHandler) - Q_PROPERTY(QString replyHandlerFlc READ replyHandlerFlc) - Q_PROPERTY(QString replyType READ replyType) - Q_PROPERTY(QString replyTypeFro READ replyTypeFro) - Q_PROPERTY(QVariantList replyParams READ replyParams) - Q_PROPERTY(QString replyHandlerHash READ replyHandlerHash WRITE setReplyHandlerHash) - - Q_PROPERTY(QString request READ request) - Q_PROPERTY(QString requestFlc READ requestFlc) - Q_PROPERTY(QString requestHandler READ requestHandler) - Q_PROPERTY(QString requestHandlerFlc READ requestHandlerFlc) - Q_PROPERTY(QString requestType READ requestType) - Q_PROPERTY(QVariantList requestParams READ requestParams) - -public: - FunctionTopicWrapper(const FunctionTopic &ft, - const CompleteTopicTypeInformation &siRequest, - const CompleteTopicTypeInformation &siReply, - QObject *parent) - : NameWrapper(ft.name, parent) - , m_ft(ft) - , m_request(siRequest) - , m_reply(siReply) - {} - QString onReply() const { return QLatin1String("on") + m_ft.replyTopicName; } - QString onRequest() const { return QLatin1String("on") + m_ft.requestTopicName; } - QString onError() const { return QLatin1String("on") + m_ft.name + QLatin1String("Error"); } - bool isCoreFt() const { return (m_ft.replyTopicType == QStringLiteral("Core::FunctionReply") || m_ft.replyTopicType == QStringLiteral("FunctionReply")); } - bool routeInReplay() const { return m_ft.routeInReplay; } - - QString reply() const { return m_ft.replyTopicName; } - QString replyFlc() const { return StructBuilder::firstCharLowercase(reply()); } - QString replyHandler() const { return m_ft.replyTopicName + QLatin1String("Handler"); } - QString replyHandlerFlc() const { return StructBuilder::firstCharLowercase(replyHandler()); } - QString replyType() const { return m_reply.name; } - QString replyTypeFro() const { return isCoreFt() ? QStringLiteral("Core::FunctionReplyObject") : m_reply.name; } - - QString request() const { return m_ft.requestTopicName; } - QString requestFlc() const { return StructBuilder::firstCharLowercase(request()); } - QString requestHandler() const { return m_ft.requestTopicName + QLatin1String("Handler"); } - QString requestHandlerFlc() const { return StructBuilder::firstCharLowercase(requestHandler()); } - QString requestType() const { return m_request.name; } - - void addRequestParams(const QVector ¶ms) - { - for (const auto ¶m : params) - m_requestParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this))); - } - QVariantList requestParams() const - { - return m_requestParams; - } - void addReplyParams(const QVector ¶ms) - { - for (const auto ¶m : params) - m_replyParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this))); - } - QVariantList replyParams() const - { - return m_replyParams; - } - - QString replyHandlerHash() const - { - return m_replyHandlerHash; - } - void setReplyHandlerHash(const QString &hash) - { - m_replyHandlerHash = hash; - } - -private: - const FunctionTopic m_ft; - const CompleteTopicTypeInformation m_request; - const CompleteTopicTypeInformation m_reply; - QVariantList m_requestParams; - QVariantList m_replyParams; - QString m_replyHandlerHash; -}; - -class FunctionTopicsWrapper final : public VariantListWrapper -{ - Q_OBJECT -public: - Q_PROPERTY(QVariantList topicsToRouteInReplay READ topicsToRouteInReplay) - - using VariantListWrapper::VariantListWrapper; - - FunctionTopicWrapper *addFunctionTopic(const FunctionTopic &ft, - const CompleteTopicTypeInformation &siRequest, - const CompleteTopicTypeInformation &siReply) - { - FunctionTopicWrapper *ret = new FunctionTopicWrapper(ft, siRequest, siReply, this); - m_member += QVariant::fromValue(ret); - return ret; - } - - - QVariantList topicsToRouteInReplay() const - { - QVariantList vl; - vl.reserve(m_member.size()); - for (const QVariant &v : m_member) - { - FunctionTopicWrapper *ft = v.value(); - if (ft->routeInReplay()) - vl += v; - } - return vl; - } -}; - -class DataTopicWrapper final : public NameWrapper -{ - Q_OBJECT - Q_PROPERTY(QString serviceName READ serviceName) - Q_PROPERTY(QString type READ type) - Q_PROPERTY(QString typeFq READ typeFq) - Q_PROPERTY(QVariant key READ key) - Q_PROPERTY(bool dtcImpl READ dtcImpl) - Q_PROPERTY(bool dtsImpl READ dtsImpl) - Q_PROPERTY(bool recordTopic READ recordTopic) - Q_PROPERTY(QVariant recTable READ recTable) - Q_PROPERTY(QVariantList trColumns READ trColumns) - Q_PROPERTY(bool hasTrColumns READ hasTrColumns) - Q_PROPERTY(bool routeToClient READ routeToClient) - Q_PROPERTY(bool routeToServer READ routeToServer) - Q_PROPERTY(bool routeInReplay READ routeInReplay) - Q_PROPERTY(bool autosubscribe READ isAutoSubscribe) - -public: - DataTopicWrapper(const CompleteTopicTypeInformation &ctti, QObject *parent) - : NameWrapper(ctti.name, parent) - , m_ctti(ctti) - , m_keyData(nullptr) - , m_recordData(nullptr) - {} - - void setKeyColumnData(const IdlData &data) - { - delete m_keyData; - m_keyData = new IdlDataWrapper(data, this); - } - - void setTrColumns(const QVector ¶ms) - { - for (const auto ¶m : params) - m_trColumns.push_back(QVariant::fromValue(new IdlDataWrapper(param, this))); - } - - QVariantList trColumns() const - { - return m_trColumns; - } - - bool hasTrColumns() const - { - return !m_trColumns.empty(); - } - - void setRecordTableInformation(const CompleteTableInformation &cti) - { - delete m_recordData; - m_recordData = new MemberInformationListWrapper(cti.name, cti.columns, this); - m_ctti.recordTopic = true; - } - - QString serviceName() const { return m_ctti.serviceName; } - QString type() const { return m_ctti.name; } - QString typeFq() const; - QVariant key() const { return m_keyData ? QVariant::fromValue(m_keyData) : QVariant(); } - bool dtsImpl() const { return m_ctti.dtsAutogen; } - bool dtcImpl() const { return m_ctti.dtcAutogen; } - bool recordTopic() const { return m_ctti.recordTopic && m_recordData; } - bool routeToClient() const { return m_ctti.routeToClient; } - bool routeToServer() const { return m_ctti.routeToServer; } - bool routeInReplay() const { return m_ctti.routeInReplay; } - bool isAutoSubscribe() const { return m_ctti.autosubscribe; } - QVariant recTable() const { return recordTopic() ? QVariant::fromValue(m_recordData) : QVariant(); } -private: - CompleteTopicTypeInformation m_ctti; - IdlDataWrapper *m_keyData; - MemberInformationListWrapper *m_recordData; - QVariantList m_trColumns; -}; - -class DataTopicsWrapper final : public VariantListWrapper -{ - Q_OBJECT -public: - Q_PROPERTY(QVariantList topicsToRouteInReplay READ topicsToRouteInReplay) - Q_PROPERTY(QVariantList topicsToRouteToClient READ topicsToRouteToClient) - Q_PROPERTY(QVariantList topicsToRouteToServer READ topicsToRouteToServer) - - using VariantListWrapper::VariantListWrapper; - - DataTopicWrapper *addDataTopic(const CompleteTopicTypeInformation &ctti) - { - DataTopicWrapper *ret = new DataTopicWrapper(ctti, this); - m_member += QVariant::fromValue(ret); - return ret; - } - - - QVariantList memberByFilter(const std::function &acceptFilter) const - { - QVariantList vl; - vl.reserve(m_member.size()); - for (const QVariant &v : m_member) - { - DataTopicWrapper *dtw = v.value(); - if (dtw && acceptFilter(dtw)) - vl += v; - } - return vl; - } - - QVariantList topicsToRouteInReplay() const - { - return memberByFilter([](const DataTopicWrapper *dtw) { return dtw->routeInReplay(); }); - } - QVariantList topicsToRouteToClient() const - { - return memberByFilter([](const DataTopicWrapper *dtw) { return dtw->routeToClient(); }); - } - QVariantList topicsToRouteToServer() const - { - return memberByFilter([](const DataTopicWrapper *dtw) { return dtw->routeToServer(); }); - } -}; - -class IdlServicesWrapper final : public VariantListWrapper -{ - Q_OBJECT -public: - using VariantListWrapper::VariantListWrapper; - - void addService(const QString &svcName, const QString &baseFilePath) - { - m_member += QVariant::fromValue(new IdlServiceWrapper(svcName, baseFilePath)); - } -}; - -} diff --git a/Lts/Common/CodeGen/README.txt b/Lts/Common/CodeGen/README.txt deleted file mode 100755 index f128ada89..000000000 --- a/Lts/Common/CodeGen/README.txt +++ /dev/null @@ -1,37 +0,0 @@ -# -# Hinweise für den CodeGen -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -Erstellt: Mai 2022, Norbert Thoden - -Der Aufruf des CodeGen kann z.B. so aussehen: - -codegen -s /tmp/ppm_serviceORG.xml -i /shd/CTC/TOOLS/TDS/TDS-build/service_idl_files.txt -a dds_topics -o /tmp /shd/CTC/TOOLS/TDS/TDS-source/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee -Edit Juni 2023, Niklas Schmidt - -Ebenso ist es nicht möglich einen Struct aus einem anderen Service(Service B) zu verwenden, -der in sich widerum einen anderen Struct aus dem anderen Service (Service B) beinhaltet. - -Bsp: - -Service A = Struct1{ int id, - Struct2 test - } - -Service B = Struct2{ - int id2, - Struct3 test - } - -Service B = Struct3{ - std::string test - } - -Mögliche Diagnose: - -Wenn Struct3 aus Service A aufgerufen wird, ist wahrscheinlich nicht mehr gekennzeichnet dass der Struct3 aus Service B kommt. Dieser kann somit nicht -aufgelöst werden diff --git a/Lts/Common/CodeGen/SqlGenerator.cpp b/Lts/Common/CodeGen/SqlGenerator.cpp deleted file mode 100755 index bd800cc79..000000000 --- a/Lts/Common/CodeGen/SqlGenerator.cpp +++ /dev/null @@ -1,943 +0,0 @@ - - - -#include "SqlGenerator.h" -#include "StructBuilder.h" -#include "XmlReader.h" - -#include -#include -#include - -#include - -using namespace CodeGen; - - -static QString getExerciseSchemaName(int exerciseId) -{ - return QStringLiteral("ex_") + QString::number(exerciseId); -} - -SqlGenerator::SqlGenerator(XmlReader *xmlReader, StructBuilder *structBuilder) - : m_xmlReader(xmlReader) - , m_structBuilder(structBuilder) - , m_createdByXmlFile(false) - , m_isValid(true) - , m_exerciseId(0) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")) -{} - -SqlGenerator::SqlGenerator(const QString &xmlFile) - : m_xmlReader(new XmlReader) - , m_structBuilder(new StructBuilder(m_xmlReader)) - , m_createdByXmlFile(true) - , m_isValid(false) - , m_exerciseId(0) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")) -{ - m_isValid = m_xmlReader->readServiceFile(xmlFile, QString()) && m_structBuilder->buildStructures(); -} - -SqlGenerator::~SqlGenerator() -{ - if (m_createdByXmlFile) - { - delete m_xmlReader; - delete m_structBuilder; - } -} - - -bool SqlGenerator::generateData(int exerciseId) -{ - DEBUG_LOG(m_logger) << "Generating SQL data for service " << getServiceAbbr() << " in exercise " << exerciseId << ", this: " << this; - - m_exerciseId = exerciseId; - m_sqlGlobal = SqlStatements(); - m_sqlExercise = SqlStatements(); - m_sqlItemsGlobal = SqlItems(); - m_sqlItemsExercise = SqlItems(); - m_referencedSevices.clear(); - - if (!handleTables()) - return false; - - if (!handleViews()) - return false; - - if (!handleEnumerations()) - return false; - - return true; -} - -bool SqlGenerator::createStatementsForTable(const QString &tableName, const QString &tableNameOut) -{ - const QMap tables = m_structBuilder->getDbTables(); - for (const CompleteTableInformation &cti : tables) - { - if (cti.name != tableName) - continue; - CompleteTableInformation ctiOut = cti; - ctiOut.name = tableNameOut; - return handleTable(ctiOut); - } - return false; -} - -bool SqlGenerator::handleTables() -{ - const QMap tables = m_structBuilder->getDbTables(); - for (const CompleteTableInformation &cti : tables) - { - if (!handleTable(cti)) - return false; - } - - const QMap recTables = m_structBuilder->getRecordingTopicTables(); - MemberInformation miColumnIsValid; - miColumnIsValid.db.columnName = QStringLiteral("dds_is_alive"); - miColumnIsValid.db.datatype = QStringLiteral("boolean"); - miColumnIsValid.db.defaultValue = QStringLiteral("true"); - MemberInformation miInsertionTime; - miInsertionTime.cpp.type = QStringLiteral("timestamp"); - miInsertionTime.db.columnName = QStringLiteral("insert_time"); - miInsertionTime.db.defaultValue = QStringLiteral("now()"); - miInsertionTime.db.usedForInsert = false; - m_structBuilder->fixupQVariantConversions(miInsertionTime); - ConstraintInformation ci; - ci.name = QStringLiteral("IDX_sample_time"); - ci.affectedColumns = QStringList({QStringLiteral("sample_time")}); - ci.type = ConstraintInformation::Type::index; - for (const CompleteTableInformation &cti : recTables) - { - CompleteTableInformation ctiTmp(cti); - ctiTmp.columns.push_back(miColumnIsValid); - ctiTmp.columns.push_back(miInsertionTime); - ctiTmp.constraints.push_back(ci); - if (!handleTable(ctiTmp)) - return false; - } - return true; -} - -bool SqlGenerator::handleViews() -{ - const QMap views = m_structBuilder->getDbViews(); - for (const ViewInformation &vi : views) - { - if (!handleView(vi)) - return false; - } - - return true; -} - -bool SqlGenerator::handleView(const ViewInformation &vi) -{ - DEBUG_LOG(m_logger) << "Handle view " << vi.name; - - const QString parent = vi.baseTable; - - DEBUG_LOG(m_logger) << "Parent = " << parent; - - if (!parent.isEmpty()) - { - const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(parent); - const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n")); - const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;")); - - DEBUG_LOG(m_logger) << "csi.primaryTable = " << csi.primaryTable.data()->name; - - if (!csi.primaryTable.isNull()) - { - QStringList fromTables; - fromTables += vi.baseSchema+QLatin1Char('.')+vi.baseTable; - - QStringList whereConditions; - if (vi.filter.contains(QLatin1String("exercise_id"))) - whereConditions += QLatin1String("exercise_id=")+QString::number(m_exerciseId); - - QStringList fqColumnNames; - - - for (const MemberInformation &mi : csi.member) - { - if (mi.db.usedForView) - fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") + mi.db.getColumnNameView(); - } - - DEBUG_LOG(m_logger) << "fqColumnNames = " << fqColumnNames.join(QStringLiteral(", ")); - - - if (!whereConditions.isEmpty()) - addToCreateViews(createViewStmt.arg(csi.viewName, - fqColumnNames.join(QStringLiteral(", ")), - fromTables.join(QStringLiteral(", ")), - whereConditions.join(QStringLiteral(" AND\n"))), - vi.exerciseDependency); - addToDropViews(dropViewStmt.arg(csi.viewName), vi.exerciseDependency); - } - } - - return true; - -} - -bool SqlGenerator::handleTable(const CompleteTableInformation &cti) -{ - DEBUG_LOG(m_logger) << "Handle table " << cti.name; - - if (cti.columns.isEmpty()) - { - WARNING_LOG(m_logger) << "Table " << cti.name - << " has no DatabaseServiceStruct - not generating anything for this!"; - return true; - } - - const QString dropTableStmt(QStringLiteral("DROP TABLE IF EXISTS %1 CASCADE;")); - const QString createTableStmt(QStringLiteral("CREATE TABLE IF NOT EXISTS %1 (%2\n);")); - const QString commentTableStmt(QStringLiteral("COMMENT ON TABLE %1 IS '%2';")); - - QStringList tableComments; - addToDropTables(dropTableStmt.arg(cti.name), cti.exerciseDependency); - - if ((cti.exerciseDependency & Global) == Global) - { - addToCreateTables(createTableStmt.arg(cti.name, - getTableColumnCreateStatements(cti.name, - cti.columns, - &tableComments, - Global)), - Global); - } - if ((cti.exerciseDependency & Exercise) == Exercise) - { - addToCreateTables(createTableStmt.arg(cti.name, - getTableColumnCreateStatements(cti.name, - cti.columns, - &tableComments, - Exercise)), - Exercise); - } - addToTables(cti.name, cti.exerciseDependency); - - if (!cti.comment.isEmpty()) - addToCreateTables(commentTableStmt.arg(cti.name, fixupComment(cti.comment)), cti.exerciseDependency); - addToCreateTables(tableComments, cti.exerciseDependency); - - const QString parent = m_xmlReader->getStruct2BaseClassRelations().value(cti.name); - const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(cti.name); - if (!parent.isEmpty()) - { - const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n")); - const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;")); - if (!csi.primaryTable.isNull()) - { - QStringList fqColumnNames; - QStringList fromTables; - QStringList whereConditions; - fromTables += cti.name; - - - for (const MemberInformation &mi : csi.member) - { - if (mi.db.usedForView) - fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") + mi.db.getColumnNameView(); - if (mi.cpp.isReadOnly && mi.db.isForeignKey) - { - - - - for (const QSharedPointer &tiPtr : csi.tables) - { - if (tiPtr->name == mi.db.table) - { - const QSharedPointer fki = tiPtr->fkInformationForColumn(mi.db.table, mi.db.columnName); - if (!fki.isNull()) - { - - if (!fromTables.contains(fki->destination.tableName)) - { - whereConditions += fki->destination.tableName + QLatin1Char('.') + fki->destination.columnName + - QLatin1String(" = ") + - fki->source.tableName + QLatin1Char('.') + fki->source.columnName; - fromTables += fki->destination.tableName; - } - } - break; - } - } - } - } - if (!whereConditions.isEmpty()) - { - addToCreateViews(createViewStmt.arg(csi.viewName, - fqColumnNames.join(QStringLiteral(", ")), - fromTables.join(QStringLiteral(", ")), - whereConditions.join(QStringLiteral(" AND\n"))), - cti.exerciseDependency); - } - addToDropViews(dropViewStmt.arg(csi.viewName), cti.exerciseDependency); - } - } - - if (!generateDbNotifications(cti.name, csi.dbNotifications, cti.exerciseDependency)) - return false; - - generateForeignKeys(cti.foreignKeys, cti.exerciseDependency); - - generateConstraints(cti.name, cti.constraints, cti.exerciseDependency); - - return true; -} - - -QString SqlGenerator::getTableColumnCreateStatements(const QString &tableName, - const QVector &columns, - QStringList *tableComments, - ExerciseDependency ed) -{ - Q_ASSERT(ed == Global || ed == Exercise); - - const QString commentColumnStmt(QStringLiteral("COMMENT ON COLUMN %1 IS '%2';")); - - - int maxLength = 1; - for (const MemberInformation &mi : columns) - maxLength = std::max(maxLength, mi.db.columnName.length()); - maxLength += 1; - - QStringList retCols; - retCols.reserve(columns.size() + 1); - retCols += QString(); - for (int i = 0; i < columns.size(); ++i) - { - const MemberInformation &mi = columns.at(i); - const FullQualifiedColumnInformation &ci = mi.db; - const QString spaces(maxLength - ci.columnName.length(), QLatin1Char(' ')); - QString column = ci.columnName + spaces + ci.datatype; - if (ci.hasSequence) - { - - const QString seqDescr = generateSequence(tableName, ci, ed); - QString schema; - if (ed == Global) - schema = m_xmlReader->getServiceAbbr(); - else - schema = getExerciseSchemaName(m_exerciseId); - - column += QLatin1String(" DEFAULT nextval(( '") + - schema + QLatin1Char('.') + seqDescr + - QLatin1String("'::text)::regclass)"); - } - if (!ci.canBeNull) - column += QLatin1String(" NOT NULL"); - if (!ci.hasSequence && !ci.defaultValue.isEmpty()) - column += QLatin1String(" DEFAULT ") + ci.defaultValue; - if (i < columns.size() - 1) - column += QLatin1Char(','); - if (!ci.comment.isEmpty()) - { - const QString comment(fixupComment(ci.comment)); - const QString fqn = ci.table + QLatin1Char('.') + ci.columnName; - *tableComments += commentColumnStmt.arg(fqn, comment); - column += QLatin1String(" -- ") + comment; - } - - retCols += column; - - if (ci.isPrimaryKey) - generatePrimaryKey(tableName, ci, ed); - } - return retCols.join(QStringLiteral("\n ")); -} - -QString SqlGenerator::generateSequence(const QString &tableName, - const FullQualifiedColumnInformation &ci, - ExerciseDependency ed) -{ - const QString sequenceName = tableName + QLatin1Char('_') + ci.columnName + QLatin1String("_seq"); - - - - const QString createSequenceStmt(QLatin1String("CREATE SEQUENCE IF NOT EXISTS %1 INCREMENT %2 START %3 MINVALUE %4;")); - - const int minValue = ci.sequenceStart < 1 ? ci.sequenceStart : 1; - - addToCreateSequences(createSequenceStmt.arg(sequenceName).arg(ci.sequenceIncrement).arg(ci.sequenceStart).arg(minValue), ed); - addToSequences(sequenceName, ed); - - return sequenceName; -} - -void SqlGenerator::generatePrimaryKey(const QString &tableName, - const FullQualifiedColumnInformation &ci, - ExerciseDependency ed) -{ - QString pkName = QLatin1String("PK_") + tableName + QLatin1Char('_') + ci.columnName; - - if (pkName.size() > 63) pkName.resize(63); - - addToPks(pkName, ed); - - - - const QString pkConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 ADD CONSTRAINT %2 PRIMARY KEY (%3);")); - addToCreatePks(pkConstraint.arg(tableName, pkName, ci.columnName), ed); - - const QString pkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); - addToDropConstraints(pkDropConstraint.arg(tableName, pkName), ed); -} - -void SqlGenerator::generateForeignKeys(const QVector > &foreignKeys, - ExerciseDependency ed) -{ - for (const QSharedPointer &fk : foreignKeys) - generateForeignKey(fk, ed); -} - -void SqlGenerator::generateForeignKey(const QSharedPointer &foreignKey, - ExerciseDependency ed) -{ - QString fkName = QLatin1String("FK_") + foreignKey->source.tableName + - QLatin1Char('_') + foreignKey->source.columnName + - QLatin1Char('_') + foreignKey->destination.tableName + - QLatin1Char('_') + foreignKey->destination.columnName; - - - - if (fkName.size() > 63) fkName = foreignKey->name; - - addToFks(fkName, ed); - - QString fkConstraint(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 FOREIGN KEY (%3) REFERENCES %4 (%5)")); - switch (foreignKey->deleteAction) - { - case ForeignKeyInformation::OnDeleteAction::NoAction: - fkConstraint += QStringLiteral(" ON DELETE NO ACTION"); - break; - case ForeignKeyInformation::OnDeleteAction::Cascade: - fkConstraint += QStringLiteral(" ON DELETE CASCADE ON UPDATE CASCADE"); - break; - case ForeignKeyInformation::OnDeleteAction::Restrict: - fkConstraint += QStringLiteral(" ON DELETE RESTRICT"); - break; - case ForeignKeyInformation::OnDeleteAction::SetNull: - fkConstraint += QStringLiteral(" ON DELETE SET NULL"); - break; - case ForeignKeyInformation::OnDeleteAction::SetDefault: - fkConstraint += QStringLiteral(" ON DELETE SET DEFAULT"); - break; - } - - fkConstraint += QLatin1Char(';'); - - - - QString destination; - if (!foreignKey->destination.schema.isEmpty()) - { - const QString &extSchema = foreignKey->destination.schema; - destination = extSchema + QLatin1Char('.') + foreignKey->destination.tableName; - } - else - destination = foreignKey->destination.tableName; - - const QString &extSvcAbbr = foreignKey->destination.serviceAbbr; - - if (!extSvcAbbr.isEmpty() && !m_referencedSevices.contains(extSvcAbbr)) - m_referencedSevices += extSvcAbbr; - - addToCreateFks(fkConstraint.arg(foreignKey->source.tableName, fkName, foreignKey->source.columnName, - destination, foreignKey->destination.columnName), ed); - - const QString fkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); - addToDropConstraints(fkDropConstraint.arg(foreignKey->source.tableName, fkName), ed); - -} - - -void SqlGenerator::generateConstraints(const QString &tableName, - const QVector &constraints, - ExerciseDependency ed) -{ - for (const ConstraintInformation &constraint : constraints) - generateConstraint(tableName, constraint, ed); -} - -void SqlGenerator::generateConstraint(const QString &tableName, - const ConstraintInformation &constraint, - ExerciseDependency ed) -{ - const QString dropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); - switch (constraint.type) - { - case ConstraintInformation::Type::index: - { - QString constraintName = QLatin1String("IDX_") + tableName + QLatin1Char('_') + - constraint.affectedColumns.join(QStringLiteral("_")); - - if (constraintName.size() > 63) constraintName.resize(63); - - const QString index(QStringLiteral("CREATE INDEX %1 ON %2 (%3);")); - - addToCreateConstraints(index.arg(constraintName, tableName, constraint.affectedColumns.join(QStringLiteral(", "))), ed); - addToConstraints(constraintName, ed); - - addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed); - - break; - } - case ConstraintInformation::Type::unique: - { - QString constraintName = QLatin1String("UQ_") + tableName + QLatin1Char('_') + - constraint.affectedColumns.join(QStringLiteral("_")); - - if (constraintName.size() > 63) constraintName.resize(63); - const QString unique(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 UNIQUE (%3);")); - - addToCreateConstraints(unique.arg(tableName, constraintName, constraint.affectedColumns.join(QStringLiteral(", "))), ed); - addToConstraints(constraintName, ed); - - addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed); - - break; - } - case ConstraintInformation::Type::unknown: - break; - } -} - -bool SqlGenerator::handleEnumerations() -{ - const QVector enumerations = m_xmlReader->getEnumerations(); - for (const EnumInformation &ei : enumerations) - { - if (!handleEnumeration(ei)) - return false; - } - return true; -} - -bool SqlGenerator::handleEnumeration(const EnumInformation &ei) -{ - const QStringList sql = handleEnumeration(ei, QString()); - if (m_sqlItemsGlobal.tables.contains(ei.tableName)) - m_sqlGlobal.enumerationValues += sql; - if (m_sqlItemsExercise.tables.contains(ei.tableName)) - m_sqlExercise.enumerationValues += sql; - - return true; -} - -QStringList SqlGenerator::handleEnumeration(const EnumInformation &ei, const QString &dbSchema) const -{ - DEBUG_LOG(m_logger) << "Handle enumeration " << ei.enumName; - - if (ei.tableName.isEmpty()) - { - INFO_LOG(m_logger) << "No table for enumeration " << ei.enumName << " - not creating inserts for this enum"; - return QStringList(); - } - - const QString insertEnum(QStringLiteral("INSERT INTO %1 (%2) VALUES (%3);")); - const QString columnNames(QStringLiteral("id, name")); - const QString tablename = dbSchema.isEmpty() ? ei.tableName : dbSchema + QLatin1Char('.') + ei.tableName; - - - QStringList enumerationValues; - for (const EnumKeyValuePair &evp : ei.values) - { - const QString values(QString::number(evp.value) + QLatin1String(", '") + - fixupComment(evp.key) + QLatin1Char('\'')); - enumerationValues += insertEnum.arg(tablename, columnNames, values); - } - - return enumerationValues; -} - -bool SqlGenerator::generateDbNotifications(const QString &tableName, - const QVector ¬ifications, - ExerciseDependency ed) -{ - for (const DbNotifcationInformation ¬ification : notifications) - { - if (notification.paramNames.isEmpty() || - notification.paramNames.size() > 1) - { - ERROR_LOG(m_logger) << "Can not handle more than one parameter"; - continue; - } - generateDbNotification(notification, ed); - if (!generateDbNotificationTrigger(tableName, notification, ed)) - return false; - } - return true; -} - -void SqlGenerator::generateDbNotification(const DbNotifcationInformation ¬ification, - ExerciseDependency ed) -{ - const QString func(QStringLiteral("CREATE OR REPLACE FUNCTION public.%1()\n" - "RETURNS TRIGGER AS $$\n" - "DECLARE\n" - " toNotify RECORD;\n" - " toReturn RECORD;\n" - "BEGIN\n" - " IF (TG_OP = 'INSERT') THEN\n" - " toNotify = NEW;\n" - " toReturn = NEW;\n" - " ELSIF (TG_OP = 'UPDATE') THEN\n" - " toNotify = OLD;\n" - " toReturn = NEW;\n" - " ELSIF (TG_OP = 'DELETE') THEN\n" - " toNotify = OLD;\n" - " toReturn = OLD;\n" - " END IF;\n" - " \n" - " PERFORM pg_notify('%2', CAST(toNotify.%3 AS text));\n" - " RETURN toReturn;\n" - "END;\n" - "$$ LANGUAGE plpgsql;")); - - const QString triggerFuncName(getNotificationFunctionName(notification)); - addToDbFunctions(func.arg(triggerFuncName, notification.methodName, notification.paramNames.first()), ed); -} - -bool SqlGenerator::generateDbNotificationTrigger(const QString &tableName, - const DbNotifcationInformation ¬ification, - ExerciseDependency ed) -{ - - const QString drop(QStringLiteral("DROP TRIGGER IF EXISTS %1 ON %2;")); - const QString func(QStringLiteral("CREATE TRIGGER %1\n") + - QStringLiteral("%2 ON %3 FOR EACH ROW EXECUTE PROCEDURE public.%4();")); - - for (DbNotifcationInformation::NotificationType notifyType : notification.notificationType) - { - QString when; - QString triggerNameShortcut; - switch (notifyType) - { - case DbNotifcationInformation::NotificationType::BeforeInsert: - when = QStringLiteral("BEFORE INSERT"); - triggerNameShortcut = QStringLiteral("bi"); - break; - case DbNotifcationInformation::NotificationType::AfterInsert: - when = QStringLiteral("AFTER INSERT"); - triggerNameShortcut = QStringLiteral("ai"); - break; - case DbNotifcationInformation::NotificationType::BeforeUpdate: - when = QStringLiteral("BEFORE UPDATE"); - triggerNameShortcut = QStringLiteral("bu"); - break; - case DbNotifcationInformation::NotificationType::AfterUpdate: - when = QStringLiteral("AFTER UPDATE"); - triggerNameShortcut = QStringLiteral("au"); - break; - case DbNotifcationInformation::NotificationType::BeforeDelete: - when = QStringLiteral("BEFORE DELETE"); - triggerNameShortcut = QStringLiteral("bd"); - break; - case DbNotifcationInformation::NotificationType::AfterDelete: - when = QStringLiteral("AFTER DELETE"); - triggerNameShortcut = QStringLiteral("ad"); - break; - } - const QString triggerName(QLatin1String("trg_") + triggerNameShortcut + QLatin1Char('_') + notification.methodName); - const QString triggerFuncName(getNotificationFunctionName(notification)); - addToDbFunctionTrigger(drop.arg(triggerName, tableName), ed); - addToDbFunctionTrigger(func.arg(triggerName, when, tableName, triggerFuncName), ed); - } - return true; -} - -QString SqlGenerator::getNotificationFunctionName(const DbNotifcationInformation ¬ification) const -{ - return getServiceAbbr() + QLatin1Char('_') + notification.methodName; -} - - -QString SqlGenerator::fixupComment(const QString &comment) -{ - - QString ret = comment; - ret = ret.remove(QLatin1Char('\'')); - ret = ret.replace(QLatin1Char(';'), QLatin1Char(',')); - return ret; -} - -QStringList SqlGenerator::getDbSettings(const QString &dbSchema) const -{ - QStringList data; - - - QString dbUser(QStringLiteral("ets_")); - if (m_exerciseId > 0) - dbUser += QStringLiteral("ex_00"); - else - dbUser += m_xmlReader->getServiceAbbr().toLower(); - - - data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + - QLatin1String(" GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE ON TABLES TO ") + dbUser + QLatin1Char(';')); - - - data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + - QLatin1String(" GRANT SELECT, REFERENCES ON TABLES TO ets_ro;")); - - data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + - QLatin1String(" GRANT EXECUTE ON FUNCTIONS TO ") + dbUser + QLatin1Char(';')); - - data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + - QLatin1String(" GRANT ALL ON SEQUENCES TO ") + dbUser + QLatin1Char(';')); - - - data.append(QLatin1String("SET SEARCH_PATH TO ") + dbSchema + QLatin1String(", PUBLIC;")); - - QStringList deps; - deps.reserve(m_referencedSevices.size()); - for(const QString &svc : m_referencedSevices) - deps += QStringLiteral("-- DEPENDS: ") + svc; - - return deps + data; -} - -QStringList SqlGenerator::getAllSqlStmts(const QString &dbSchema, ExerciseDependency ed) const -{ - DEBUG_LOG(m_logger) << "Creating statements for service " << getServiceAbbr() << " in exercise " << m_exerciseId << ", this: " << this; - - QStringList data; - - const SqlStatements &sql = getStatements(ed); - if (sql.isEmpty()) - return data; - - if (!dbSchema.isEmpty()) - data << getDbSettings(dbSchema); - - - data.append(QStringLiteral("\n-- Drop tables/views/sequences/constraints")); - data.append(sql.dropViews); - data.append(sql.dropConstraints); - data.append(sql.dropTables); - data.append(sql.dropSequences); - - - data.append(QStringLiteral("\n-- Create tables")); - data.append(sql.createTables); - - - data.append(QStringLiteral("\n-- Create sequence(s)")); - data.append(sql.createSequences); - - - data.append(QStringLiteral("\n-- Create primary keys")); - data.append(sql.createPrimaryKeys); - - - data.append(QStringLiteral("\n-- Create foreign keys")); - data.append(sql.createForeignKeys); - - - data.append(QStringLiteral("\n-- Create views")); - data.append(sql.createViews); - - - data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); - data.append(QStringLiteral("\n-- Inserts for static data i.e. enums\n")); - data.append(sql.enumerationValues); - - - data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); - data.append(QStringLiteral("\n-- Notify functions for callbacks (signals in db interface)\n")); - data.append(sql.dbFunctions); - - - data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); - data.append(QStringLiteral("\n-- Notify function trigger for callbacks (signals in db interface)\n")); - data.append(sql.dbFunctionTrigger); - - - data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); - data.append(QStringLiteral("\n-- Indexes \n")); - data.append(sql.createConstraints); - - return data; -} - -QStringList SqlGenerator::getEnumerationInsertValues(const QString &table, const QString &dbSchema) const -{ - const QVector enumerations = m_xmlReader->getEnumerations(); - - for (const EnumInformation &ei : enumerations) - { - if (ei.tableName == table) - return handleEnumeration(ei, dbSchema); - } - return QStringList(); -} - -bool SqlGenerator::writeSqlToFile(const QString &dbSchema, const QString &sqlFile, ExerciseDependency ed) const -{ - return writeFile(sqlFile, getAllSqlStmts(dbSchema, ed).join(QLatin1Char('\n'))); -} - -bool SqlGenerator::writeSql(const QString &filePath, ExerciseDependency ed) -{ - Q_ASSERT(ed == Global || ed == Exercise); - - - QString sqlFile = filePath + getServiceAbbr(); - if (ed == ExerciseDependency::Exercise) - sqlFile += QLatin1Char('_') + getExerciseSchemaName(m_exerciseId); - sqlFile += QLatin1String(".sql"); - - const SqlStatements &sql = getStatements(ed); - if (sql.isEmpty()) - { - DEBUG_LOG(m_logger) << "Sql file is emtpy: " << sqlFile; - return writeFile(sqlFile, QString()); - } - - - - const QString dbSchema = (ed == ExerciseDependency::Exercise ? - getExerciseSchemaName(m_exerciseId) : - getServiceAbbr()); - - INFO_LOG(m_logger) << "Creating sql-stmt for schema (" << dbSchema << ")"; - return writeSqlToFile(dbSchema, sqlFile, ed); -} - - -template -static void addInternal(QStringList &global, - QStringList &exercise, - const T &stmt, - ExerciseDependency ed) -{ - switch (ed) - { - case ExerciseDependency::Global: - global += stmt; - break; - case ExerciseDependency::Exercise: - exercise += stmt; - break; - case ExerciseDependency::GlobalAndExercise: - global += stmt; - exercise += stmt; - break; - case ExerciseDependency::Undefined: - break; - } -} - -void SqlGenerator::addToDropSequences(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dropSequences, m_sqlExercise.dropSequences, stmt, ed); -} - -void SqlGenerator::addToDropTables(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dropTables, m_sqlExercise.dropTables, stmt, ed); -} - -void SqlGenerator::addToDropViews(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dropViews, m_sqlExercise.dropViews, stmt, ed); -} - -void SqlGenerator::addToCreateConstraints(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createConstraints, m_sqlExercise.createConstraints, stmt, ed); -} - -void SqlGenerator::addToDropConstraints(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dropConstraints, m_sqlExercise.dropConstraints, stmt, ed); -} - -void SqlGenerator::addToCreateFks(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createForeignKeys, m_sqlExercise.createForeignKeys, stmt, ed); -} - -void SqlGenerator::addToCreatePks(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createPrimaryKeys, m_sqlExercise.createPrimaryKeys, stmt, ed); -} - -void SqlGenerator::addToCreateSequences(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createSequences, m_sqlExercise.createSequences, stmt, ed); -} - -void SqlGenerator::addToCreateTables(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmt, ed); -} - -void SqlGenerator::addToCreateTables(const QStringList &stmts, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmts, ed); -} - -void SqlGenerator::addToCreateViews(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.createViews, m_sqlExercise.createViews, stmt, ed); -} - -void SqlGenerator::addToDbFunctions(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dbFunctions, m_sqlExercise.dbFunctions, stmt, ed); -} - -void SqlGenerator::addToDbFunctionTrigger(const QString &stmt, ExerciseDependency ed) -{ - addInternal(m_sqlGlobal.dbFunctionTrigger, m_sqlExercise.dbFunctionTrigger, stmt, ed); -} - -void SqlGenerator::addToConstraints(const QString &name, ExerciseDependency ed) -{ - addInternal(m_sqlItemsGlobal.constraints, m_sqlItemsExercise.constraints, name, ed); -} - -void SqlGenerator::addToFks(const QString &name, ExerciseDependency ed) -{ - addInternal(m_sqlItemsGlobal.foreignKeys, m_sqlItemsExercise.foreignKeys, name, ed); -} - -void SqlGenerator::addToPks(const QString &name, ExerciseDependency ed) -{ - addInternal(m_sqlItemsGlobal.primaryKeys, m_sqlItemsExercise.primaryKeys, name, ed); -} - -void SqlGenerator::addToSequences(const QString &name, ExerciseDependency ed) -{ - addInternal(m_sqlItemsGlobal.sequences, m_sqlItemsExercise.sequences, name, ed); -} - -void SqlGenerator::addToTables(const QString &name, ExerciseDependency ed) -{ - addInternal(m_sqlItemsGlobal.tables, m_sqlItemsExercise.tables, name, ed); -} - -bool SqlGenerator::writeFile(const QString &fn, const QString &content) const -{ - QFile f(fn); - if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { - ERROR_LOG(m_logger) << "Error opening file '" << f.fileName() << "' for writing: " - << f.errorString(); - return false; - } - f.write(content.toUtf8()); - if (!content.isEmpty()) - f.write("\n"); - f.close(); - return true; -} diff --git a/Lts/Common/CodeGen/SqlGenerator.h b/Lts/Common/CodeGen/SqlGenerator.h deleted file mode 100755 index 409949df5..000000000 --- a/Lts/Common/CodeGen/SqlGenerator.h +++ /dev/null @@ -1,175 +0,0 @@ - - -#pragma once - -#include -#include -#include "StructBuilder.h" -#include "codegen_export.h" - -namespace NGL -{ -class INGLLogger; -} - -namespace CodeGen -{ -struct ColumnInformation; -struct ConstraintInformation; -struct EnumInformation; -struct ForeignKeyInformation; -struct TableInformation; -class XmlReader; - -class CODEGEN_BASE_EXPORT SqlGenerator final -{ -public: - struct SqlStatements - { - QStringList dropViews; - QStringList dropTables; - QStringList dropSequences; - QStringList dropConstraints; - - QStringList createTables; - QStringList createSequences; - QStringList createPrimaryKeys; - QStringList createForeignKeys; - QStringList createViews; - QStringList createConstraints; - - QStringList enumerationValues; - QStringList dbFunctions; - QStringList dbFunctionTrigger; - - bool isEmpty() const - { - return dropViews.isEmpty() && dropTables.isEmpty() && dropSequences.isEmpty() && dropConstraints.isEmpty(); - } - }; - - struct SqlItems - { - QStringList tables; - QStringList sequences; - QStringList primaryKeys; - QStringList foreignKeys; - QStringList constraints; - }; - -public: - Q_DISABLE_COPY(SqlGenerator) - - explicit SqlGenerator(XmlReader *xmlReader, StructBuilder *structBuilder); - explicit SqlGenerator(const QString &xmlFile); - - ~SqlGenerator(); - - bool isValid() const { return m_isValid; } - - bool generateData(int exerciseId); - - bool createStatementsForTable(const QString &tableName, const QString &tableNameOut); - - SqlStatements getStatements(ExerciseDependency ed) const - { - return (ed == ExerciseDependency::Exercise ? m_sqlExercise : m_sqlGlobal); - } - - SqlItems getSqlItems(ExerciseDependency ed) const - { - return (ed == ExerciseDependency::Exercise ? m_sqlItemsExercise : m_sqlItemsGlobal); - } - - QStringList getEnumerationInsertValues(const QString &table, const QString &dbSchema) const; - QStringList getReferencedServices() const - { - return m_referencedSevices; - } - - QStringList getDbSettings(const QString &dbSchema) const; - - - bool writeSql(const QString &filePath, ExerciseDependency ed); - - bool writeSqlToFile(const QString &dbSchema, const QString &sqlFile, ExerciseDependency ed) const; - - - QString getServiceAbbr() const { return m_xmlReader->getServiceAbbr(); } - bool isServiceExerciseIndependent() const { return m_xmlReader->isServiceExerciseIndependent(); } - bool isServiceGlobal() const { return (m_xmlReader->defaultExerciseDependency() == CodeGen::ExerciseDependency::Global || m_xmlReader->defaultExerciseDependency() == CodeGen::ExerciseDependency::GlobalAndExercise); } - CodeGen::ExerciseDependency getExerciseDependency() const { return m_xmlReader->defaultExerciseDependency();} - QStringList getGroupNames() const { return m_xmlReader->getGroupNames(); } - - QStringList getAllSqlStmts(const QString &dbSchema, ExerciseDependency ed) const; - - -protected: - -private: - bool handleTables(); - bool handleViews(); - bool handleTable(const CompleteTableInformation &cti); - bool handleView(const ViewInformation &vi); - QString getTableColumnCreateStatements(const QString &tableName, - const QVector &columns, - QStringList *tableComments, ExerciseDependency ed); - QString generateSequence(const QString &tableName, const FullQualifiedColumnInformation &ci, ExerciseDependency ed); - void generatePrimaryKey(const QString &tableName, const FullQualifiedColumnInformation &ci, ExerciseDependency ed); - void generateForeignKeys(const QVector > &foreignKeys, ExerciseDependency ed); - void generateForeignKey(const QSharedPointer &foreignKey, ExerciseDependency ed); - void generateConstraints(const QString &tableName, const QVector &constraints, ExerciseDependency ed); - void generateConstraint(const QString &tableName, const ConstraintInformation &constraint, ExerciseDependency ed); - bool handleEnumerations(); - bool handleEnumeration(const EnumInformation &ei); - QStringList handleEnumeration(const EnumInformation &ei, const QString &dbSchema) const; - bool generateDbNotifications(const QString &tableName, const QVector ¬ifications, ExerciseDependency ed); - void generateDbNotification(const DbNotifcationInformation ¬ification, ExerciseDependency ed); - bool generateDbNotificationTrigger(const QString &tableName, const DbNotifcationInformation ¬ification, ExerciseDependency ed); - QString getNotificationFunctionName(const DbNotifcationInformation ¬ification) const; - static QString fixupComment(const QString &comment); - -private: - XmlReader *m_xmlReader; - StructBuilder *m_structBuilder; - bool m_createdByXmlFile; - - void addToDropSequences(const QString &stmt, ExerciseDependency ed); - void addToDropTables(const QString &stmt, ExerciseDependency ed); - void addToDropViews(const QString &stmt, ExerciseDependency ed); - void addToDropConstraints(const QString &stmt, ExerciseDependency ed); - - void addToCreateConstraints(const QString &stmt, ExerciseDependency ed); - void addToCreateFks(const QString &stmt, ExerciseDependency ed); - void addToCreatePks(const QString &stmt, ExerciseDependency ed); - void addToCreateSequences(const QString &stmt, ExerciseDependency ed); - void addToCreateTables(const QString &stmt, ExerciseDependency ed); - void addToCreateTables(const QStringList &stmts, ExerciseDependency ed); - void addToCreateViews(const QString &stmt, ExerciseDependency ed); - - void addToDbFunctions(const QString &stmt, ExerciseDependency ed); - void addToDbFunctionTrigger(const QString &stmt, ExerciseDependency ed); - - void addToConstraints(const QString &name, ExerciseDependency ed); - void addToFks(const QString &name, ExerciseDependency ed); - void addToPks(const QString &name, ExerciseDependency ed); - void addToSequences(const QString &name, ExerciseDependency ed); - void addToTables(const QString &name, ExerciseDependency ed); - - bool writeFile(const QString &fn, const QString &content) const; - - SqlStatements m_sqlGlobal; - SqlStatements m_sqlExercise; - - SqlItems m_sqlItemsGlobal; - SqlItems m_sqlItemsExercise; - - QStringList m_referencedSevices; - - bool m_isValid; - int m_exerciseId; - - NGL::INGLLogger &m_logger; -}; - -} diff --git a/Lts/Common/CodeGen/StructBuilder.cpp b/Lts/Common/CodeGen/StructBuilder.cpp deleted file mode 100755 index b5315c55f..000000000 --- a/Lts/Common/CodeGen/StructBuilder.cpp +++ /dev/null @@ -1,1431 +0,0 @@ - - - -#include "StructBuilder.h" - -#include - -#include - -using namespace CodeGen; - - - - -class DataTypeInfoIdl : public DataTypeInfo -{ -protected: - DataTypeInfoIdl(const QString &name, const QString &idlType, bool isPod) - : DataTypeInfo(name, isPod) - , m_idlType(idlType) - {} -public: - QString idlType() const override final { return m_idlType; } -private: - const QString m_idlType; -}; - -class DataTypeInfoSimple : public DataTypeInfoIdl -{ -public: - DataTypeInfoSimple(const QString &name, - const QString &idlType, - const QString &ddsType, - const QString &sqlType, - const QString &cppType, - bool isPod) - : DataTypeInfoIdl(name, idlType, isPod) - , m_ddsType(ddsType) - , m_sqlType(sqlType) - , m_cppType(cppType) - {} - QString ddsType() const override final { return m_ddsType; } - QString sqlType() const override final { return m_sqlType; } - QString cppType() const override final { return m_cppType; } -protected: - QString m_ddsType; - QString m_sqlType; - QString m_cppType; -}; - -class DataTypeInfoStructure final : public DataTypeInfoSimple -{ -public: - DataTypeInfoStructure(const QString &name, const QString &idlType, StructBuilder *structBuilder) - : DataTypeInfoSimple(name, idlType, QString(), idlType, idlType, false) - { - const CompleteTopicTypeInformation ctti = structBuilder->topicTypeInformationByStructType(idlType); - Q_ASSERT(ctti.isValid()); - - m_member.reserve(ctti.members.size()); - for (const IdlData &idlData : ctti.members) - m_member += DataTypeInfo::fromIdlDataType(idlData.name, idlData.type, structBuilder); - m_serviceName = ctti.serviceName; - if (ctti.name.startsWith(m_serviceName + QStringLiteral("::"))) - m_ddsType = ctti.name; - else - m_ddsType = m_serviceName + QStringLiteral("::") + ctti.name; - } - bool isStructure() const override final { return true; } - virtual QVector> member() const override final { return m_member; } -private: - QVector> m_member; - QString m_serviceName; -}; - -class DataTypeInfoSequence final : public DataTypeInfoIdl -{ -public: - DataTypeInfoSequence(const QString &name, const QString &idlType, StructBuilder *structBuilder, const QString &svcName) - : DataTypeInfoIdl(name, idlType, false) - { - - const int start = idlType.indexOf(QLatin1Char('<')); - const int end = idlType.lastIndexOf(QLatin1Char('>')); - Q_ASSERT(start >= 0 && end >= 0 && end > start); - const QString inner = idlType.mid(start + 1, end - start - 1); - m_innerDataType = DataTypeInfo::fromIdlDataType(QStringLiteral("inner"), inner, structBuilder, svcName); - Q_ASSERT(m_innerDataType); - } - - QString ddsType() const override final - { - return QLatin1String("std::vector<") + m_innerDataType->ddsType() + QLatin1Char('>'); - } - QString sqlType() const override final - { - return QStringLiteral("jsonb"); - } - QString cppType() const override final - { - return QLatin1String("std::vector<") + m_innerDataType->cppType() + QLatin1Char('>'); - } - QString cppTypeConstRef() const override final - { - return QLatin1String("const std::vector<") + m_innerDataType->cppType() + QLatin1String("> &"); - } - virtual bool isSequence() const override final { return true; } - QSharedPointer innerDataType() const override final { return m_innerDataType; } -private: - QSharedPointer m_innerDataType; -}; - -QSharedPointer DataTypeInfo::fromIdlDataType(const QString &name, - const QString &idlType, - StructBuilder *structBuilder, - const QString &svcName) -{ - const QString fullIdlType = (svcName.isEmpty() || idlType.contains(QLatin1String("::"))) - ? idlType : svcName + QLatin1String("::") + idlType; - - if (idlType == QLatin1String("sequence")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("std::vector"), - QStringLiteral("bytea"), - QStringLiteral("QByteArray"), - false)); - if (idlType == QLatin1String("boolean")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("bool"), - QStringLiteral("boolean"), - QStringLiteral("bool"), - true)); - if (idlType == QLatin1String("char")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("char"), - QStringLiteral("char"), - QStringLiteral("char"), - true)); - if (idlType == QLatin1String("double")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("double"), - QStringLiteral("double precision"), - QStringLiteral("double"), - true)); - if (idlType == QLatin1String("long")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("int32_t"), - QStringLiteral("integer"), - QStringLiteral("qint32"), - true)); - if (idlType == QLatin1String("long long")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("rti::core::int64"), - QStringLiteral("bigint"), - QStringLiteral("qint64"), - true)); - if (idlType == QLatin1String("short")) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("int16_t"), - QStringLiteral("smallint"), - QStringLiteral("qint16"), - true)); - if (idlType.startsWith(QLatin1String("string"))) - return QSharedPointer(new DataTypeInfoSimple(name, - idlType, - QStringLiteral("std::string"), - QStringLiteral("text"), - QStringLiteral("std::string"), - false)); - if (idlType.startsWith(QLatin1String("sequence"))) - return QSharedPointer(new DataTypeInfoSequence(name, idlType, structBuilder, svcName)); - - - const EnumInformation ei = structBuilder->enumInformationByType(fullIdlType, true, false); - if (ei.isValid()) - return QSharedPointer(new DataTypeInfoSimple(name, idlType, ei.enumName, QStringLiteral("integer"), ei.enumName, true)); - - - const CompleteTopicTypeInformation ctti = structBuilder->topicTypeInformationByStructType(fullIdlType, true); - if (ctti.isValid()) - return QSharedPointer(new DataTypeInfoStructure(name, idlType, structBuilder)); - - - return QSharedPointer(new DataTypeInfoSimple(name, idlType, idlType, QStringLiteral("integer"), idlType, true)); -} - - - - - -StructBuilder::StructBuilder(XmlReader *xmlReader) - : m_needPoint3d(false) - , m_needPgArrayInclude(false) - , m_xmlReader(xmlReader) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.StructBuilder")) - , m_loggerCSV(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("fileCSV")) -{} - -bool StructBuilder::buildStructures() -{ - m_structInfos.clear(); - m_topicTypeInfos.clear(); - m_enumInfos.clear(); - m_sqlTableInformations.clear(); - m_sqlTableInformationsAlreadyAvailable.clear(); - m_sqlViewInformations.clear(); - m_externalTopicTypes.clear(); - m_serializeFromToJsonTopics.clear(); - m_jsonStructureInformation.clear(); - - m_needPoint3d = false; - m_needPgArrayInclude = false; - - - const QMap > tables = m_xmlReader->getTables(); - for (const QSharedPointer &ti : tables) - { - CompleteTableInformation cti(*ti); - if (cti.exerciseDependency == ExerciseDependency::Undefined) - cti.exerciseDependency = m_xmlReader->defaultExerciseDependency(); - - m_sqlTableInformations[ti->name] = cti; - } - - const QMap > views = m_xmlReader->getViews(); - for (const QSharedPointer &vi : views) - { - ViewInformation cti(*vi); - if (cti.exerciseDependency == ExerciseDependency::Undefined) - cti.exerciseDependency = m_xmlReader->defaultExerciseDependency(); - - m_sqlViewInformations[vi->name] = cti; - } - - - const QVector enumData = m_xmlReader->getEnumerations(); - for (const EnumInformation &ei : enumData) - { - if (!handleEnum(ei)) - return false; - } - - const QVector structsToGenerate = m_xmlReader->getStructToGenerate(); - for (const ServiceStructInformation &ssi : structsToGenerate) - { - if (!handleTable(ssi)) - return false; - } - - const QVector idlStructsToGenerate = m_xmlReader->getIdlStructToGenerate(); - for (const TopicTypeInformation &tti : idlStructsToGenerate) - { - DEBUG_LOG(m_loggerCSV) << tti.name; - if (!handleIdlStructInfo(tti, m_topicTypeInfos)) - return false; - } - const QVector idlExternalTopicTypes = m_xmlReader->getExternalTopicTypes(); - for (const TopicTypeInformation &tti : idlExternalTopicTypes) - { - const ServiceTypeStruct sts = extractServiceAndTypeFromTypename(tti.name); - if (!sts.isExternalType()) - { - ERROR_LOG(m_logger) << "Invalid external topic type name: '" << tti.name << "'"; - return false; - } - if (!handleIdlStructInfo(tti, m_externalTopicTypes, sts.service)) - return false; - } - - return true; -} - -bool StructBuilder::handleEnum(const EnumInformation &ei) -{ - m_enumInfos.push_back(ei); - - - if (ei.tableName.isEmpty()) - return true; - - QSharedPointer ti = m_xmlReader->getTable(ei.tableName); - if (ti.isNull()) - { - WARNING_LOG(m_logger) << "Could not find table information for enum: " - << ei.enumName << ", table: " << ei.tableName; - return false; - } - - CompleteTableInformation cti = m_sqlTableInformations[ei.tableName]; - for (const ColumnInformation &ci : qAsConst(ti->plainColumns)) - { - MemberInformation mi; - mi.db = FullQualifiedColumnInformation(ci, ei.tableName); - cti.columns.push_back(std::move(mi)); - } - m_sqlTableInformations[ei.tableName] = cti; - - return true; -} - -CompleteStructInformation StructBuilder::structInformationByTableName(const QString &tableName) const -{ - for (const CompleteStructInformation &csi : m_structInfos) - { - if (csi.primaryTable->name == tableName) - return csi; - } - return CompleteStructInformation(); -} - -EnumInformation StructBuilder::enumInformationByType(const QString &type, bool checkExternals, bool verbose) -{ - if (type.isEmpty()) - return EnumInformation(); - const ServiceTypeStruct extInfo = extractServiceAndTypeFromTypename(type); - if (extInfo.isExternalType()) - { - if (!checkExternals) - { - ERROR_LOG(m_logger) << "Could not find enumeration '" << type << "'"; - return EnumInformation(); - } - if (extInfo.type.isEmpty()) - { - WARNING_LOG(m_logger) << "Could not parse external enumeration '" << type << "'"; - return EnumInformation(); - } - return m_xmlReader->getExternalEnumeration(extInfo.service, extInfo.type, verbose); - } - - for (const EnumInformation &ei : qAsConst(m_enumInfos)) - { - if (ei.enumName == type) - return ei; - } - if (verbose) - ERROR_LOG(m_logger) << "Could not find enumeration '" << type << "'"; - return EnumInformation(); -} - -CompleteTopicTypeInformation StructBuilder::topicTypeInformationByStructType(const QString &type, bool checkExternals) -{ - DEBUG_LOG(m_logger) << "topicTypeInformationByStructType(" << type << ")"; - if (type.isEmpty()) - return CompleteTopicTypeInformation(); - - const ServiceTypeStruct extInfo = extractServiceAndTypeFromTypename(type); - if (extInfo.isExternalType()) - { - if (!checkExternals) - return CompleteTopicTypeInformation(); - - return externalTopicTypeByStructType(extInfo); - } - for (const CompleteTopicTypeInformation &csi : qAsConst(m_topicTypeInfos)) - { - if (csi.name == type) - return csi; - } - - DEBUG_LOG(m_logger) << "Type '" << type << "' not found in idlStructs - maybe not yet exported or an enum"; - const QVector idlStructsToGenerate = m_xmlReader->getIdlStructToGenerate(); - for (const TopicTypeInformation &tti : idlStructsToGenerate) - { - if (tti.name == type) - { - DEBUG_LOG(m_logger) << "Found not yet exported type " << type; - if (!handleIdlStructInfo(tti, m_topicTypeInfos)) - return CompleteTopicTypeInformation(); - return topicTypeInformationByStructType(type); - } - } - ERROR_LOG(m_logger) << "Could not find type '" << type << "' in idlStructs"; - return CompleteTopicTypeInformation(); -} - -CompleteTopicTypeInformation StructBuilder::externalTopicTypeByStructType(const ServiceTypeStruct &sts) -{ - const QString typeToSearch = sts.fqType(); - DEBUG_LOG(m_logger) << "externalTopicTypeByStructType " << typeToSearch; - for (const CompleteTopicTypeInformation &ctti : qAsConst(m_externalTopicTypes)) - { - if (ctti.name == typeToSearch) - return ctti; - } - if (!loadExternalTopicType(sts)) - return CompleteTopicTypeInformation(); - return externalTopicTypeByStructType(sts); -} - -bool StructBuilder::loadExternalTopicType(const ServiceTypeStruct &sts) -{ - DEBUG_LOG(m_logger) << "loadExternalTopicType(" << sts.fqType() << ")"; - - - const TopicTypeInformation tti = m_xmlReader->loadExternalTopicType(sts.service, sts.type); - if (!tti.isValid()) - { - DEBUG_LOG(m_logger) << "Could not find external topic '" << sts.fqType() << "'"; - return false; - } - - if (!handleIdlStructInfo(tti, m_externalTopicTypes, sts.service)) - return false; - return true; -} - - -bool StructBuilder::getColumnsFromTable(const QString &tableName, - QVector &columns, - QVector &tableNames) const -{ - if (tableName.isEmpty()) - return true; - - const QString parentTable = m_xmlReader->getStruct2BaseClassRelations().value(tableName); - if (!getColumnsFromTable(parentTable, columns, tableNames)) - return false; - tableNames += tableName; - const QSharedPointer ti = m_xmlReader->getTable(tableName); - QVector constraints; - constraints.reserve(ti->constraints.size()); - for (const ConstraintInformation &ci : qAsConst(ti->constraints)) - { - if (ci.affectedColumns.size() != 1) - continue; - if (ci.type != ConstraintInformation::Type::index) - continue; - constraints += ci; - } - - columns.reserve(columns.size() + ti->plainColumns.size()); - for (const auto &plainColum : qAsConst(ti->plainColumns)) - { - ColumnInformationEx cie(ColumnInformationEx(plainColum, ti)); - for (int j = 0; j < constraints.size(); ++j) - { - const ConstraintInformation &ci = constraints.at(j); - if (ci.affectedColumns.first() == cie.columnName) - { - cie.generateSelectRemove = true; - constraints.remove(j); - break; - } - } - columns += cie; - } - return true; -} - -bool StructBuilder::handleTable(const ServiceStructInformation &ssi) -{ - DEBUG_LOG(m_loggerCSV) << "StructInfo name: #" << ssi.name; - - CompleteStructInformation csi; - csi.structName = ssi.name; - csi.primaryTable = m_xmlReader->getTable(ssi.primaryTable); - csi.viewName = csi.primaryTable->name + QLatin1String("_view"); - csi.hasStaticContent = ssi.hasStaticContent; - csi.dbNotifications = ssi.dbNotifications; - - DEBUG_LOG(m_loggerCSV) << "primaryTable: " << csi.primaryTable; - DEBUG_LOG(m_loggerCSV) << "hasStaticContent: " << csi.hasStaticContent; - - - QVector columns; - QVector tableNames; - if (!getColumnsFromTable(ssi.primaryTable, columns, tableNames)) - { - WARNING_LOG(m_logger) << "Could not retrieve columns from table " - << ssi.primaryTable; - return false; - } - DEBUG_LOG(m_logger) << "Got " << columns.size() << " columns for table " - << ssi.primaryTable; - - QSet sqlTables; - - - csi.member.reserve(columns.size() + 1); - MemberInformation miPkFirstTable, miPkLastTable; - for (const ColumnInformationEx &ci : qAsConst(columns)) - { - DEBUG_LOG(m_logger) << "handling column " << ci.columnName; - - const QSharedPointer ti = ci.table; - if (ti.isNull()) - { - WARNING_LOG(m_logger) << "Could not find table for column " << ci.columnName - << " primary table is " << ssi.primaryTable; - return false; - } - csi.tables += ti; - sqlTables += ti->name; - - MemberInformation memberInfo; - memberInfo.ea.column = ci.columnName; - memberInfo.ea.table = ti->name; - memberInfo.ea.type = ci.datatype; - - memberInfo.db = FullQualifiedColumnInformation(ci, memberInfo.ea.table); - - if (ci.hasSequence) - { - memberInfo.db.usedForInsert = !ci.isPrimaryKey; - memberInfo.db.hasSequence = true; - } - else - memberInfo.db.usedForInsert = true; - - memberInfo.db.usedForView = !ci.isPrimaryKey; - - memberInfo.cpp.structName = ssi.name; - memberInfo.cpp.memberName = camelCaseString(ci.columnName); - memberInfo.cpp.type = ci.datatype; - memberInfo.cpp.usedForCppStruct = !ci.isPrimaryKey; - - if (ci.isForeignKey) - { - const QSharedPointer fki = ti->fkInformationForColumn(ti->name, ci.columnName); - if (!fki.isNull()) - { - - for(const auto &tbl : qAsConst(csi.tables)) - { - if (tbl->name == fki->destination.tableName) - { - memberInfo.cpp.isReadOnly = true; - memberInfo.fk = fki; - memberInfo.cpp.memberName = QLatin1String("priv_") + memberInfo.cpp.memberName; - break; - } - } - - if (memberInfo.fk.isNull()) - { - - if (fki->destination.service.isEmpty()) - { - - for (const EnumInformation &ei : qAsConst(m_enumInfos)) - { - if (ei.tableName == fki->destination.tableName) - { - memberInfo.enumeration = QSharedPointer(new EnumInformation(ei)); - break; - } - } - } - else - { - - QString enumType = fki->destination.service + QStringLiteral("::") + - camelCaseString(fki->destination.tableName, true); - - EnumInformation ei = enumInformationByType(enumType, true, false); - DEBUG_LOG(m_logger) << "Checking if " << enumType << " is an enum: " << ei.isValid(); - if (!ei.isValid() && fki->destination.tableName.contains(QLatin1Char('_'))) - { - - enumType = fki->destination.service + QStringLiteral("::") + - camelCaseString(fki->destination.tableName.section(QLatin1Char('_'), 1), true); - ei = enumInformationByType(enumType, true, false); - DEBUG_LOG(m_logger) << "Checking if " << enumType << " is an enum: " << ei.isValid(); - } - if (ei.isValid() && - ei.tableName == fki->destination.tableName) - memberInfo.enumeration = QSharedPointer(new EnumInformation(ei)); - } - } - } - } - MemberInformation memberInfoJson = memberInfo; - memberInfoJson.cpp.usedForJson = true; - memberInfoJson.cpp.usedForCppStruct = false; - memberInfoJson.db.isForeignKey = false; - memberInfoJson.db.generateSelectRemove = false; - memberInfoJson.db.usedForView = false; - memberInfoJson.db.usedForInsert = false; - - if (ci.isOptional) - { - memberInfo.cpp.isOptional = true; - memberInfoJson.cpp.isOptional = true; - memberInfo.ea.type = QStringLiteral("QVariant"); - memberInfo.cpp.type = memberInfo.ea.type; - } - - if (!fixupCustomDbDatatypes(memberInfo, csi.member) || - !fixupQVariantConversions(memberInfo)) - return false; - - if (!fixupCustomDbDatatypes(memberInfoJson, csi.member) || - !fixupQVariantConversions(memberInfoJson)) - return false; - - if (ci.isPrimaryKey && tableNames.first() == ti->name) - { - - miPkFirstTable = memberInfo; - } - if (ci.isPrimaryKey && tableNames.last() == ti->name) - { - - miPkLastTable = memberInfo; - } - - csi.member.push_back(memberInfo); - csi.member.push_back(memberInfoJson); - - addToSqlTableInformation(memberInfo); - - DEBUG_LOG(m_loggerCSV) << memberInfo.toString(); - } - - if (!miPkFirstTable.isEmpty()) - { - miPkFirstTable.db.usedForInsert = false; - miPkFirstTable.db.usedForView = true; - miPkFirstTable.cpp.usedForCppStruct = true; - csi.member.push_front(miPkFirstTable); - } - - if (tableNames.size() > 1 && !miPkLastTable.isEmpty()) - { - - - miPkLastTable.db.usedForInsert = false; - miPkLastTable.db.usedForView = true; - miPkLastTable.cpp.usedForCppStruct = true; - miPkLastTable.db.isAssignmentForPrimaryTablePk = true; - miPkLastTable.db.columnNameView = - miPkLastTable.db.table + QLatin1Char('_') + miPkLastTable.db.columnName; - - QString name = miPkLastTable.db.table; - if (name.startsWith(m_xmlReader->getServiceAbbr().toLower())) - name = name.mid(m_xmlReader->getServiceAbbr().size() + 1); - miPkLastTable.cpp.memberName = camelCaseString(QLatin1String("priv_") + name + QLatin1String("_id")); - csi.member.push_back(miPkLastTable); - } - - - m_structInfos.push_back(csi); - - m_sqlTableInformationsAlreadyAvailable += sqlTables; - return true; -} - -bool StructBuilder::handleIdlStructInfo(const TopicTypeInformation &tti, - QVector &destination, - const QString &svcName) -{ - - for (const CompleteTopicTypeInformation &c : qAsConst(destination)) - { - if (c.name == tti.name) - return true; - } - - CompleteTopicTypeInformation ctti(tti); - ctti.members.reserve(tti.columns.size()); - - - for (const auto &ci : tti.columns) - { - IdlData idlData(ci); - idlData.dataTypeInfo = DataTypeInfo::fromIdlDataType(idlData.name, idlData.type, this, svcName); - ctti.members.push_back(idlData); - - - if (idlData.isKey && idlData.isOptional) - { - ERROR_LOG(m_logger) << "Column option isKey & isOptional are mutually exclusive!"; - ERROR_LOG(m_logger) << "Change column '" << tti.name << "." - << idlData.name << "'' to have either key or optional parameter "; - return false; - } - } - - - if (!svcName.isEmpty() && - !ctti.baseValueType.isEmpty() && - !ctti.baseValueType.contains(QLatin1String("::"))) - ctti.baseValueType = svcName + QStringLiteral("::") + ctti.baseValueType; - - ctti.serviceName = svcName.isEmpty() ? m_xmlReader->getServiceNamespace() : svcName; - - destination.push_back(ctti); - - return true; -} - -bool StructBuilder::fixupCustomDbDatatypes(MemberInformation &memberInfo, QVector &members) -{ - if (!fixupDatatypePoint2d(memberInfo, members)) - return false; - if (!fixupDatatypePoint3d(memberInfo, members)) - return false; - if (!fixupDatatypeIntegerArray(memberInfo)) - return false; - return true; -} - -bool StructBuilder::fixupDatatypePoint2d(MemberInformation &memberInfo, - QVector &members) -{ - if (memberInfo.ea.type.toLower() != QLatin1String("point_2d")) - return true; - - MemberInformation mi = memberInfo; - mi.db.datatype = QStringLiteral("double precision"); - mi.cpp.type = QStringLiteral("QPointF"); - mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); - - - MemberInformation miX = mi; - MemberInformation miY = mi; - - miX.db.columnName = memberInfo.db.columnName + QLatin1String("_x"); - miY.db.columnName = memberInfo.db.columnName + QLatin1String("_y"); - - miY.cpp.usedForCppStruct = false; - miY.cpp.usedForJson = false; - - miX.cpp.toVariantPost = QStringLiteral(".x()"); - miY.cpp.toVariantPost = QStringLiteral(".y()"); - - miX.cpp.fromVariantPre1 = QStringLiteral(".rx()"); - miY.cpp.fromVariantPre1 = QStringLiteral(".ry()"); - - miX.cpp.memberName = camelCaseString(miX.cpp.memberName); - - - miX.cpp.toJsonPre = QStringLiteral("toJson("); - miX.cpp.toJsonPost = QStringLiteral(")"); - miX.cpp.fromJsonPre2 = QStringLiteral("fromJson("); - miX.cpp.fromJsonPost = QStringLiteral(")"); - - members.push_back(miX); - - if (memberInfo.cpp.usedForCppStruct) - addToSqlTableInformation(miX); - - DEBUG_LOG(m_loggerCSV) << miX.toString(); - - memberInfo = miY; - - return true; -} - -bool StructBuilder::fixupDatatypePoint3d(MemberInformation &memberInfo, - QVector &members) -{ - if (memberInfo.ea.type.toLower() != QLatin1String("point_3d")) - return true; - - MemberInformation mi = memberInfo; - mi.db.datatype = QStringLiteral("double precision"); - mi.cpp.type = QStringLiteral("LtsUtils::QPoint3D"); - mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); - - - MemberInformation miX = mi; - MemberInformation miY = mi; - MemberInformation miZ = mi; - - miX.db.columnName = memberInfo.db.columnName + QLatin1String("_x"); - miY.db.columnName = memberInfo.db.columnName + QLatin1String("_y"); - miZ.db.columnName = memberInfo.db.columnName + QLatin1String("_z"); - - miY.cpp.usedForCppStruct = false; - miZ.cpp.usedForCppStruct = false; - miY.cpp.usedForJson = false; - miZ.cpp.usedForJson = false; - - miX.cpp.toVariantPost = QStringLiteral(".x()"); - miY.cpp.toVariantPost = QStringLiteral(".y()"); - miZ.cpp.toVariantPost = QStringLiteral(".z()"); - - miX.cpp.fromVariantPre1 = QStringLiteral(".rx()"); - miY.cpp.fromVariantPre1 = QStringLiteral(".ry()"); - miZ.cpp.fromVariantPre1 = QStringLiteral(".rz()"); - - miX.cpp.memberName = camelCaseString(miX.cpp.memberName); - - - miX.cpp.toJsonPre = QStringLiteral("toJson("); - miX.cpp.toJsonPost = QStringLiteral(")"); - miX.cpp.fromJsonPre2 = QStringLiteral("fromJson("); - miX.cpp.fromJsonPost = QStringLiteral(")"); - - members.push_back(miX); - members.push_back(miY); - - if (memberInfo.cpp.usedForCppStruct) - { - addToSqlTableInformation(miX); - addToSqlTableInformation(miY); - } - DEBUG_LOG(m_loggerCSV) << miX.toString(); - - memberInfo = miZ; - - m_needPoint3d = true; - - return true; -} - -bool StructBuilder::fixupDatatypeIntegerArray(MemberInformation &memberInfo) -{ - - if (memberInfo.ea.type.toLower() == QLatin1String("integer[]")) - { - memberInfo.db.datatype = memberInfo.ea.type.toLower(); - memberInfo.cpp.type = QStringLiteral("LtsUtils::IntArray"); - memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::IntArray::fromPgString("); - memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); - memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); - - m_needPgArrayInclude = true; - } - else if (memberInfo.ea.type.toLower() == QLatin1String("bigint[]")) - { - memberInfo.db.datatype = memberInfo.ea.type.toLower(); - memberInfo.cpp.type = QStringLiteral("LtsUtils::BigintArray"); - memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::BigintArray::fromPgString("); - memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); - memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); - - m_needPgArrayInclude = true; - } - else if (memberInfo.cpp.type.toLower() == QLatin1String("sequence")) - { - memberInfo.db.datatype = QStringLiteral("integer[]"); - memberInfo.cpp.type = QStringLiteral("std::vector"); - memberInfo.cpp.fromVariantPre2 = QStringLiteral("std::vector(LtsUtils::IntArray::fromPgString("); - memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())())"); - memberInfo.cpp.toVariantPre = QStringLiteral("LtsUtils::IntArray("); - memberInfo.cpp.toVariantPost = QStringLiteral(").toPgString()"); - - m_needPgArrayInclude = true; - } - else if (memberInfo.ea.type.toLower() == QLatin1String("integer[][]")) - { - memberInfo.db.datatype = memberInfo.ea.type.toLower(); - memberInfo.cpp.type = QStringLiteral("LtsUtils::IntArray2D"); - memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::IntArray2D::fromPgString("); - memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); - memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); - - m_needPgArrayInclude = true; - } - else if (memberInfo.ea.type.toLower() == QLatin1String("bigint[][]")) - { - memberInfo.db.datatype = memberInfo.ea.type.toLower(); - memberInfo.cpp.type = QStringLiteral("LtsUtils::BigintArray2D"); - memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::BigintArray2D::fromPgString("); - memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); - memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); - - m_needPgArrayInclude = true; - } - - return true; -} - - -bool StructBuilder::fixupQVariantConversions(MemberInformation &mi) const -{ - - if (!mi.cpp.toVariantPost.isEmpty()) - return true; - mi.cpp.toVariantPre.clear(); - mi.cpp.toVariantPost.clear(); - mi.cpp.fromVariantPre1.clear(); - mi.cpp.fromVariantPre2.clear(); - mi.cpp.fromVariantPost.clear(); - - if (mi.enumeration) - { - QString svcName(mi.enumeration->serviceName); - if (!svcName.isEmpty()) - svcName += QStringLiteral("::"); - mi.cpp.type = svcName + mi.enumeration->enumName; - mi.cpp.fromVariantPre2 = QStringLiteral("static_cast<") + - svcName + mi.enumeration->enumName + - QStringLiteral("_def::type>("); - mi.cpp.fromVariantPost = QStringLiteral(".toInt())"); - mi.cpp.toVariantPre = QStringLiteral("static_cast("); - mi.cpp.toVariantPost = QStringLiteral(".underlying())"); - if (!mi.enumeration->values.isEmpty()) - { - mi.cpp.defaultValue = svcName + mi.enumeration->enumName + QStringLiteral("::") + - mi.enumeration->values.first().key; - } - return true; - } - const QString datatype = mi.cpp.type.toLower(); - if (datatype == QLatin1String("bigint") || - datatype == QLatin1String("qint64") || - datatype == QLatin1String("long long")) - { - mi.cpp.type = QStringLiteral("qint64"); - mi.cpp.fromVariantPost = QStringLiteral(".toLongLong()"); - mi.cpp.fromJsonPost = QStringLiteral(".toDouble()"); - mi.cpp.defaultValue = QLatin1Char('0'); - } - else if (datatype == QLatin1String("boolean") || - datatype == QLatin1String("bool")) - { - mi.cpp.type = QStringLiteral("bool"); - mi.cpp.fromVariantPost = QStringLiteral(".toBool()"); - mi.cpp.defaultValue = QStringLiteral("false"); - } - else if (datatype == QLatin1String("bytea") || - datatype == QLatin1String("QByteArray")) - { - - mi.cpp.type = QStringLiteral("QByteArray"); - mi.cpp.fromVariantPost = QStringLiteral(".toByteArray()"); - mi.cpp.toJsonPre = QStringLiteral("QString::fromLatin1("); - mi.cpp.toJsonPost = QStringLiteral(".toBase64())"); - mi.cpp.fromJsonPre2 = QStringLiteral("QByteArray::fromBase64("); - mi.cpp.fromJsonPost = QStringLiteral(".toString().toLatin1())"); - mi.cpp.defaultValue.clear(); - } - else if (datatype == QLatin1String("sequence")) - { - - mi.cpp.type = QStringLiteral("QByteArray"); - mi.cpp.ddsType = QStringLiteral("std::vector"); - - mi.cpp.toVariantPre = QStringLiteral("octetSequenceToQVariant("); - mi.cpp.toVariantPost = QStringLiteral(")"); - - mi.cpp.fromVariantPre2 = QStringLiteral("qVariantToOctetSequence("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - - mi.cpp.toJsonPre = QStringLiteral("toJson("); - mi.cpp.toJsonPost = QStringLiteral(")"); - mi.cpp.fromJsonPre2 = QStringLiteral("fromJson>("); - mi.cpp.fromJsonPost = QStringLiteral(")"); - - mi.cpp.defaultValue.clear(); - } - else if (datatype == QLatin1String("char")) - { - mi.cpp.type = QStringLiteral("char"); - mi.cpp.fromVariantPost = QStringLiteral(".toChar().toLatin1()"); - mi.cpp.fromJsonPost = QStringLiteral(".toInt()"); - mi.cpp.defaultValue = QStringLiteral("\\0"); - } - else if (datatype == QLatin1String("integer") || - datatype == QLatin1String("qint32") || - datatype == QLatin1String("long")) - { - mi.cpp.type = QStringLiteral("qint32"); - mi.cpp.fromVariantPost = QStringLiteral(".toInt()"); - mi.cpp.defaultValue = QLatin1Char('0'); - } - else if (datatype == QLatin1String("real") || - datatype == QLatin1String("double") || - datatype == QLatin1String("double precision") || - datatype == QLatin1String("float")) - { - mi.cpp.type = QStringLiteral("double"); - mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); - mi.cpp.defaultValue = QStringLiteral("0.0"); - mi.db.datatype = QStringLiteral("double precision"); - } - else if (datatype == QLatin1String("numeric") || - datatype == QLatin1String("decimal")) - { - mi.cpp.type = QStringLiteral("double"); - mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); - mi.cpp.defaultValue = QStringLiteral("0.0"); - mi.db.datatype = QStringLiteral("numeric"); - } - else if (datatype == QLatin1String("smallint") || - datatype == QLatin1String("qint16") || - datatype == QLatin1String("short")) - { - mi.cpp.type = QStringLiteral("qint16"); - mi.cpp.fromVariantPost = QStringLiteral(".toInt()"); - mi.cpp.defaultValue = QLatin1Char('0'); - } - else if (datatype == QLatin1String("text") || - datatype == QLatin1String("QString")) - { - - mi.cpp.type = QStringLiteral("QString"); - mi.cpp.fromVariantPost = QStringLiteral(".toString()"); - mi.cpp.defaultValue = QString(); - } - else if (datatype == QLatin1String("json") || - datatype == QLatin1String("jsonb")) - { - - mi.cpp.type = QStringLiteral("QString"); - mi.cpp.fromVariantPre2 = QStringLiteral("qVariantToJsonString("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - mi.cpp.fromJsonPre2 = QStringLiteral("jsonValueToJsonString("); - mi.cpp.fromJsonPost = QStringLiteral(")"); - mi.cpp.toVariantPre = QStringLiteral("fixupEmptyJsonString("); - mi.cpp.toVariantPost = QStringLiteral(")"); - mi.cpp.defaultValue = QStringLiteral("QLatin1String(\"{}\")"); - } - else if (datatype.startsWith(QLatin1String("string"))) - { - - mi.cpp.type = QStringLiteral("std::string"); - mi.cpp.toVariantPre = QStringLiteral("QString::fromStdString("); - mi.cpp.toVariantPost = QStringLiteral(")"); - - mi.cpp.fromVariantPost = QStringLiteral(".toString().toStdString()"); - - mi.cpp.fromJsonPre2 = QStringLiteral("fromJson("); - mi.cpp.fromJsonPost = QStringLiteral(")"); - } - else if (datatype == QLatin1String("timestamp") || - datatype == QLatin1String("qdatetime")) - { - mi.db.datatype = QStringLiteral("bigint"); - if (mi.db.defaultValue.toLower() == QStringLiteral("now()")) - mi.db.defaultValue = QStringLiteral("(extract(epoch from now()) * 1000)"); - mi.cpp.type = QStringLiteral("QDateTime"); - mi.cpp.fromVariantPre2 = QStringLiteral("NSqlQuery::qVariantToQDateTime("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - mi.cpp.toVariantPre = QStringLiteral("NSqlQuery::qDateTimeToQVariant("); - mi.cpp.toVariantPost = QStringLiteral(")"); - mi.cpp.toJsonPre = QLatin1String(""); - mi.cpp.toJsonPost = QStringLiteral(".toMSecsSinceEpoch()"); - mi.cpp.fromJsonPre2 = QStringLiteral("QDateTime::fromMSecsSinceEpoch("); - mi.cpp.fromJsonPost = QStringLiteral(".toDouble())"); - } - else if (datatype == QLatin1String("qvariant")) - { - - } - else - { - ERROR_LOG(m_logger) << "Unknown cpp datatype: " << mi.cpp.type; - return false; - } - return true; -} - - - - - - -QVector StructBuilder::buildDbTableInformationFromIdlData(const CompleteTopicTypeInformation &ctti, - const QString &tableName, - const QString &columnNamePrefix, - const QString &cppStructure) -{ - QVector ret; - if (!ctti.isValid()) - return ret; - - if (!ctti.baseValueType.isEmpty()) - { - ret += buildDbTableInformationFromIdlData(topicTypeInformationByStructType(ctti.baseValueType), - tableName, - columnNamePrefix, - cppStructure); - } - - for (const IdlData &member : ctti.members) - { - if (member.dataTypeInfo->isStructure() && !member.isOptional) - { - const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(member.type); - if (!cttiChild.isValid()) - { - ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; - continue; - } - QString pre; - if (!columnNamePrefix.isEmpty()) - pre += columnNamePrefix; - pre += member.name + QLatin1Char('_'); - ret += buildDbTableInformationFromIdlData(cttiChild, - tableName, - StructBuilder::camelCaseToUnderline(pre), - cppStructure + member.name + QStringLiteral("().") - ); - continue; - } - - if(member.dataTypeInfo->isSequence() && member.dataTypeInfo->innerDataType()->isStructure()) - { - const CompleteTopicTypeInformation &cttiChild = topicTypeInformationByStructType(member.dataTypeInfo->innerDataType()->idlType()); - if (!cttiChild.isValid()) - { - ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; - continue; - } - buildDbTableInformationFromIdlData(cttiChild, - tableName, - columnNamePrefix, - member.name - ); - - } - - MemberInformation mi; - mi.isDdsKey = member.isKey; - mi.db.table = tableName; - mi.db.columnName = columnNamePrefix + StructBuilder::camelCaseToUnderline(member.name); - mi.db.datatype = member.dataTypeInfo->sqlType(); - mi.db.usedForView = true; - mi.cpp.memberName = cppStructure + member.name; - mi.cpp.type = member.type; - mi.cpp.usedForCppStruct = true; - mi.cpp.isOptional = member.isOptional; - - - const EnumInformation ei = enumInformationByType(member.type, true, false); - if (ei.isValid()) - mi.enumeration = QSharedPointer(new EnumInformation(ei)); - - if ((member.dataTypeInfo->isSequence() && member.dataTypeInfo->innerDataType()->idlType() != QLatin1String("octet")) || - (member.dataTypeInfo->isStructure() && member.isOptional)) - { - - - mi.db.datatype = QStringLiteral("jsonb"); - mi.cpp.toJsonPre = QStringLiteral("toJson("); - mi.cpp.toJsonPost = QStringLiteral(")"); - mi.cpp.type = member.dataTypeInfo->ddsType(); - - - mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - - if (member.dataTypeInfo->isSequence()) - addToSerializeFromToJsonTopics(member.dataTypeInfo->innerDataType()); - else - addToSerializeFromToJsonTopics(DataTypeInfo::fromIdlDataType(mi.cpp.memberName, member.type, this)); - } - else - { - fixupCustomDbDatatypes(mi, ret); - fixupQVariantConversions(mi); - } - ret += mi; - } - return ret; -} - - - - - - -bool StructBuilder::buildJSonStructureInformationFromIdlData(const CompleteTopicTypeInformation &ctti) -{ - if (!ctti.isValid()) - return false; - if (m_jsonStructureInformation.contains(ctti.name)) - return true; - - if (!ctti.baseValueType.isEmpty()) - { - if (!buildJSonStructureInformationFromIdlData(topicTypeInformationByStructType(ctti.baseValueType))) - return false; - } - - QVector content; - content.reserve(ctti.members.size()); - for (const IdlData &member : ctti.members) - { - MemberInformation mi; - mi.isDdsKey = member.isKey; - mi.db.table = ctti.name; - mi.db.columnName = StructBuilder::camelCaseToUnderline(member.name); - mi.db.datatype = member.dataTypeInfo->sqlType(); - mi.db.usedForView = true; - mi.cpp.memberName = member.name; - mi.cpp.type = member.type; - mi.cpp.usedForCppStruct = true; - mi.cpp.isOptional = member.isOptional; - - if (member.dataTypeInfo->isStructure()) - { - const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(member.type); - if (!cttiChild.isValid()) - { - ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; - continue; - } - if (!buildJSonStructureInformationFromIdlData(cttiChild)) - return false; - - mi.cpp.toJsonPre = QStringLiteral("toJson("); - mi.cpp.toJsonPost = QStringLiteral(")"); - mi.cpp.type = member.dataTypeInfo->ddsType(); - - - mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - } - else if (member.dataTypeInfo->isSequence() && - member.dataTypeInfo->innerDataType()->idlType() != QLatin1String("octet")) - { - auto inner = member.dataTypeInfo->innerDataType(); - if (inner->isStructure()) - { - const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(inner->idlType()); - if (cttiChild.isValid()) - { - if (!buildJSonStructureInformationFromIdlData(cttiChild)) - return false; - } - else - WARNING_LOG(m_logger) << "Can not find structure '" << member.type << "'"; - } - - - - mi.db.datatype = QStringLiteral("jsonb"); - mi.cpp.toJsonPre = QStringLiteral("toJson("); - mi.cpp.toJsonPost = QStringLiteral(")"); - mi.cpp.type = member.dataTypeInfo->ddsType(); - - - mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); - mi.cpp.fromVariantPost = QStringLiteral(")"); - } - else - { - - const EnumInformation ei = enumInformationByType(member.type, true, false); - if (ei.isValid()) - mi.enumeration = QSharedPointer(new EnumInformation(ei)); - - fixupCustomDbDatatypes(mi, content); - fixupQVariantConversions(mi); - } - content += mi; - } - m_jsonStructureInformation[ctti.name] = content; - - return true; -} - -CompleteTableInformation StructBuilder::completeTableInformation(const CompleteTopicTypeInformation &ctti) -{ - CompleteTableInformation cti; - cti.structName = ctti.name; - cti.name = QStringLiteral("rec_") + camelCaseToUnderline(ctti.name); - cti.comment = QStringLiteral("recorded data for topic ") + ctti.name; - cti.columns = buildDbTableInformationFromIdlData(ctti, cti.name, QString(), QString()); - buildJSonStructureInformationFromIdlData(ctti); - cti.exerciseDependency = ExerciseDependency::Exercise; - return cti; -} - -MemberInformation StructBuilder::getRecordPrimaryKey() const -{ - MemberInformation miPrimaryKey; - miPrimaryKey.db.isPrimaryKey = true; - miPrimaryKey.db.columnName = QStringLiteral("rec_id"); - miPrimaryKey.db.datatype = QStringLiteral("bigint"); - miPrimaryKey.db.hasSequence = true; - miPrimaryKey.db.sequenceStart = 1; - miPrimaryKey.db.sequenceIncrement = 1; - miPrimaryKey.db.usedForView = false; - miPrimaryKey.cpp.type = QStringLiteral("bigint"); - miPrimaryKey.cpp.usedForCppStruct = false; - fixupQVariantConversions(miPrimaryKey); - return miPrimaryKey; -} - -CompleteTableInformation StructBuilder::getRecordingTopicTable(const QString &structName) -{ - const auto vec = structName.contains(QLatin1String("::")) ? m_externalTopicTypes : m_topicTypeInfos; - - for (const CompleteTopicTypeInformation &ctti : qAsConst(vec)) - { - if (structName != ctti.name) - continue; - MemberInformation miPrimaryKey(getRecordPrimaryKey()); - miPrimaryKey.db.table = ctti.name; - return getRecordingTopicTable(ctti, getRecordPrimaryKey()); - } - return CompleteTableInformation(); -} - -CompleteTableInformation StructBuilder::getRecordingTopicTable(const CompleteTopicTypeInformation &ctti, - const MemberInformation &miPrimaryKey) -{ - CompleteTableInformation cti = completeTableInformation(ctti); - cti.columns.push_back(miPrimaryKey); - return cti; -} - -QMap StructBuilder::getRecordingTopicTables() -{ - - QMap ret; - - MemberInformation miPrimaryKey(getRecordPrimaryKey()); - - for (const CompleteTopicTypeInformation &ctti : qAsConst(m_topicTypeInfos)) - { - if (!ctti.recordTopic) - continue; - if (!ctti.isDataObject) - { - WARNING_LOG(m_logger) << "Trying to record topic '" << ctti.name - << "' which is not directly published - ignoring!"; - continue; - } - DEBUG_LOG(m_logger) << "Rec table: " << ctti.name; - miPrimaryKey.db.table = ctti.name; - ret.insert(ctti.name, getRecordingTopicTable(ctti, miPrimaryKey)); - } - return ret; -} - -void StructBuilder::addToSerializeFromToJsonTopics(const QSharedPointer &toAdd) -{ - typedef QSharedPointer sDTI; - for (const sDTI &dti : qAsConst(m_serializeFromToJsonTopics)) - { - if (dti->cppType() == toAdd->cppType()) - return; - } - m_serializeFromToJsonTopics += toAdd; -} - -QVector> StructBuilder::getSerializeFromToJsonTopics() const -{ - return m_serializeFromToJsonTopics; -} - -QString StructBuilder::firstCharLowercase(const QString &txt) -{ - QString ret = txt; - if (txt.isEmpty()) - return ret; - ret[0] = ret[0].toLower(); - return ret; -} - -QString StructBuilder::camelCaseString(const QString &in, bool bFirstUppercase) -{ - Q_DECL_CONSTEXPR const QChar s_underline(QLatin1Char('_')); - QString out; - out.reserve(in.size()); - bool bNextIsUpperCase = bFirstUppercase; - for (const auto c : in) - { - if (c == s_underline) - bNextIsUpperCase = true; - else - { - out += (bNextIsUpperCase ? c.toUpper() : c); - bNextIsUpperCase = false; - } - } - return out; -} - -QString StructBuilder::camelCaseToUnderline(const QString &in) -{ - QString out; - if (in.isEmpty()) - return out; - out.reserve(in.size()); - out += in.at(0).toLower(); - for (int i = 1; i < in.size(); ++i) - { - const QChar c = in.at(i); - if (!c.isLetter() || c.isLower()) - out += c; - else - out += QLatin1Char('_') + c.toLower(); - } - return out; -} - -StructBuilder::ServiceTypeStruct StructBuilder::extractServiceAndTypeFromTypename(const QString &type) -{ - const QVector sl = type.splitRef(QStringLiteral("::")); - QStringRef extServiceRef;; - QStringRef extTypeRef; - if (sl.size() == 1) - extTypeRef = sl.at(0); - else if (sl.size() == 2) - { - extServiceRef = sl.at(0); - extTypeRef = sl.at(1); - } - else - WARNING_LOG(m_logger) << "Error parsing type: " << type; - StructBuilder::ServiceTypeStruct ret; - if (!extServiceRef.isEmpty()) - { - - if (extServiceRef.contains(QLatin1Char('<'))) - ret.service = extServiceRef.split(QLatin1Char('<')).at(1).toString(); - else - ret.service = extServiceRef.toString(); - } - if (!extTypeRef.isEmpty()) - { - - if (extTypeRef.contains(QLatin1Char('>'))) - ret.type = extTypeRef.split(QLatin1Char('>')).at(0).toString(); - else - ret.type = extTypeRef.toString(); - } - return ret; -} - -void StructBuilder::addToSqlTableInformation(const MemberInformation &mi) -{ - if (!m_sqlTableInformationsAlreadyAvailable.contains(mi.db.table)) - m_sqlTableInformations[mi.db.table].columns.append(mi); -} diff --git a/Lts/Common/CodeGen/StructBuilder.h b/Lts/Common/CodeGen/StructBuilder.h deleted file mode 100755 index 595da91a9..000000000 --- a/Lts/Common/CodeGen/StructBuilder.h +++ /dev/null @@ -1,341 +0,0 @@ - - -#pragma once - -#include -#include "XmlReader.h" -#include "codegen_export.h" - -namespace NGL -{ -class INGLLogger; -} - -namespace CodeGen -{ -class StructBuilder; - -struct EaData -{ - QString table; - QString column; - QString type; - - QString toString() const - { - return table + - QLatin1Char(';') + column + - QLatin1Char(';') + type; - } -}; - -struct FullQualifiedColumnInformation : public ColumnInformation -{ - QString table; - bool usedForInsert = false; - bool usedForView = false; - QString columnNameView; - - QString getColumnNameView() const { return columnNameView.isEmpty() ? columnName : columnNameView; } - - FullQualifiedColumnInformation() = default; - FullQualifiedColumnInformation(const ColumnInformation &ci, const QString &tableName) - : ColumnInformation(ci) - , table(tableName) - {} - - QString toString() const - { - return table + - QLatin1Char(';') + columnName + - QLatin1Char(';') + datatype + - - QLatin1Char(';') + (isPrimaryKey ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + (isForeignKey ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + (usedForInsert ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + (usedForView ? QLatin1String("true") : QLatin1String("false")); - } -}; - -class DataTypeInfo -{ -public: - DataTypeInfo(const QString &name, bool isPod) - : m_name(name) - , m_isPod(isPod) - {} - virtual ~DataTypeInfo() = default; - virtual QString name() const { return m_name; } - virtual QString idlType() const = 0; - virtual QString ddsType() const = 0; - virtual QString sqlType() const = 0; - virtual QString cppType() const = 0; - virtual QString cppTypeConstRef() const { return isPod() ? cppType() : QLatin1String("const ") + cppType() + QLatin1String(" &"); } - virtual bool isPod() const { return m_isPod; } - virtual bool isSequence() const { return false; } - virtual bool isStructure() const { return false; } - virtual QVector> member() const { return QVector>(); } - virtual QSharedPointer innerDataType() const { return QSharedPointer(); } - - static QSharedPointer fromIdlDataType(const QString &name, - const QString &idlType, - StructBuilder *structBuilder, - const QString &svcName = QString()); -protected: - const QString m_name; - const bool m_isPod; -}; - -struct IdlData : public IdlColumnInformation -{ - QSharedPointer dataTypeInfo; - - IdlData() = default; - explicit IdlData(const IdlColumnInformation &ci) - : IdlColumnInformation(ci) - {} - - QString toString() const - { - return name + - QLatin1Char(';') + type + - - QLatin1Char(';') + (isKey ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + (isOptional ? QLatin1String("true") : QLatin1String("false")); - } -}; - - - -struct CppData -{ - QString structName; - QString memberName; - QString defaultValue; - bool usedForCppStruct = false; - bool usedForJson = false; - bool isReadOnly = false; - bool isOptional = false; - QString type; - QString ddsType; - QString toVariantPre; - QString toVariantPost; - QString fromVariantPre1; - QString fromVariantPre2; - QString fromVariantPost; - - QString toJsonPre; - QString toJsonPost; - QString fromJsonPre2; - QString fromJsonPost; - - QString toString() const - { - return structName + - QLatin1Char(';') + memberName + - QLatin1Char(';') + (usedForCppStruct ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + (isReadOnly ? QLatin1String("true") : QLatin1String("false")) + - QLatin1Char(';') + type + - QLatin1Char(';') + toVariantPre + - QLatin1Char(';') + toVariantPost + - QLatin1Char(';') + fromVariantPre1 + - QLatin1Char(';') + fromVariantPre2 + - QLatin1Char(';') + fromVariantPost; - } -}; - -struct MemberInformation -{ - EaData ea; - FullQualifiedColumnInformation db; - CppData cpp; - bool isDdsKey = false; - QSharedPointer fk; - QSharedPointer enumeration; - - QString toString() const - { - return ea.toString() + QLatin1Char(';') + - db.toString() + QLatin1Char(';') + - cpp.toString(); - } - bool isEmpty() const - { - return ea.column.isEmpty(); - } -}; - -struct CompleteStructInformation -{ - QString structName; - QString viewName; - QSet > tables; - QSharedPointer primaryTable; - QVector member; - QVector dbNotifications; - bool hasStaticContent = false; - - bool isEmpty() const - { - return primaryTable.isNull(); - } -}; - -struct CompleteTopicTypeInformation final : public TopicTypeInformationBase -{ - QString serviceName; - - QVector members; - - CompleteTopicTypeInformation() = default; - explicit CompleteTopicTypeInformation(const TopicTypeInformationBase &s) - : TopicTypeInformationBase(s) - {} - - bool hasBaseValue() const { return !baseValueType.isEmpty(); } - bool isValid() const { return TopicTypeInformationBase::isValid() && !members.isEmpty(); } -}; - - -struct CompleteTableInformation : public TableInformation -{ - QVector columns; - explicit CompleteTableInformation(const TableInformation &o) - : TableInformation(o) - {} - CompleteTableInformation() = default; - bool isEmpty() const { return columns.isEmpty(); } -}; - -class CODEGEN_BASE_EXPORT StructBuilder -{ - struct ColumnInformationEx : public ColumnInformation - { - QSharedPointer table; - - ColumnInformationEx() = default; - ColumnInformationEx(const ColumnInformation &o, - const QSharedPointer &ti) - : ColumnInformation(o) - , table(ti) - {} - }; - -public: - struct ServiceTypeStruct - { - QString service; - QString type; - bool bIsSequence = false; - bool isExternalType() const { return !service.isEmpty(); } - bool isSequence() const { return bIsSequence; } - QString fqType() const { return service + QStringLiteral("::") + type; } - }; - - explicit StructBuilder(XmlReader *xmlReader); - ~StructBuilder() = default; - - bool buildStructures(); - QVector structInformations() const - { - return m_structInfos; - } - QVector topicTypeInformations() const - { - return m_topicTypeInfos; - } - - CompleteStructInformation structInformationByTableName(const QString &tableName) const; - - EnumInformation enumInformationByType(const QString &type, bool checkExternals = true, bool verbose = true); - - CompleteTopicTypeInformation topicTypeInformationByStructType(const QString &type, bool checkExternals = true); - CompleteTopicTypeInformation externalTopicTypeByStructType(const ServiceTypeStruct &sts); - - QVector enumInformations() const - { - return m_enumInfos; - } - - QMap getDbTables() const - { - return m_sqlTableInformations; - } - - QMap getDbViews() const - { - return m_sqlViewInformations; - } - - bool needPoint3d() const - { - return m_needPoint3d; - } - - bool needPgArrayInclude() const - { - return m_needPgArrayInclude; - } - - CompleteTableInformation completeTableInformation(const CompleteTopicTypeInformation &ctti); - CompleteTableInformation getRecordingTopicTable(const QString &structName); - QMap getRecordingTopicTables(); - QVector > getSerializeFromToJsonTopics() const; - bool fixupQVariantConversions(MemberInformation &mi) const; - - Q_REQUIRED_RESULT static QString firstCharLowercase(const QString &txt); - Q_REQUIRED_RESULT static QString camelCaseString(const QString &in, bool bFirstUppercase = false); - Q_REQUIRED_RESULT static QString camelCaseToUnderline(const QString &in); - - bool buildJSonStructureInformationFromIdlData(const CompleteTopicTypeInformation &ctti); - QMap> jsonStructureInformation() const { return m_jsonStructureInformation; } - -private: - bool handleEnum(const EnumInformation &ei); - bool handleTable(const ServiceStructInformation &ssi); - bool handleIdlStructInfo(const TopicTypeInformation &tti, - QVector &destination, - const QString &svcName = QString()); - bool loadExternalTopicType(const ServiceTypeStruct &sts); - - bool getColumnsFromTable(const QString &tableName, - QVector &columns, - QVector &tableNames) const; - - bool fixupCustomDbDatatypes(MemberInformation &memberInfo, QVector &members); - bool fixupDatatypePoint2d(MemberInformation &memberInfo, QVector &members); - bool fixupDatatypePoint3d(MemberInformation &memberInfo, QVector &members); - bool fixupDatatypeIntegerArray(MemberInformation &memberInfo); - - QVector buildDbTableInformationFromIdlData(const CompleteTopicTypeInformation &ctti, - const QString &tableName, - const QString &columnNamePrefix, - const QString &cppStructure); - void addToSerializeFromToJsonTopics(const QSharedPointer &toAdd); - CompleteTableInformation getRecordingTopicTable(const CompleteTopicTypeInformation &ctti, - const MemberInformation &miPrimaryKey); - MemberInformation getRecordPrimaryKey() const; - - ServiceTypeStruct extractServiceAndTypeFromTypename(const QString &type); - - void addToSqlTableInformation(const MemberInformation &mi); -private: - QVector m_structInfos; - QVector m_topicTypeInfos; - mutable QVector m_externalTopicTypes; - QVector m_enumInfos; - QMap m_sqlTableInformations; - QMap m_sqlViewInformations; - QSet m_sqlTableInformationsAlreadyAvailable; - QVector> m_serializeFromToJsonTopics; - - QMap> m_jsonStructureInformation; - - bool m_needPoint3d; - bool m_needPgArrayInclude; - - XmlReader *m_xmlReader; - NGL::INGLLogger &m_logger; - NGL::INGLLogger &m_loggerCSV; -}; -} diff --git a/Lts/Common/CodeGen/XmlReader.cpp b/Lts/Common/CodeGen/XmlReader.cpp deleted file mode 100755 index 532a5877f..000000000 --- a/Lts/Common/CodeGen/XmlReader.cpp +++ /dev/null @@ -1,927 +0,0 @@ - - -#include "XmlReader.h" - -#include - -#include -#include -#include -#include -#include - -using namespace CodeGen; - - -class XmlReaderException : public std::exception -{ -public: - explicit XmlReaderException(const QString &msg) noexcept - : m_msg(msg.toUtf8()) - {} - - ~XmlReaderException() = default; - - virtual const char *what() const noexcept - { - return m_msg.data(); - } -private: - const QByteArray m_msg; -}; - -XmlReader::XmlReader() - : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.XmlReader")) -{} - -bool XmlReader::readServiceFile(const QString &svcFile, - const QString &idlServiceInfoFile, - const XmlReader *parent) -{ - m_externalXmlInformations.clear(); - m_idlFiles.clear(); - m_xmlFiles.clear(); - m_usedServices.clear(); - m_fullServiceName2Abbreviation.clear(); - - m_svcNamespace.clear(); - m_svcAbbr.clear(); - m_svcProcNames.clear(); - m_customRecordReplay = false; - m_exerciseDependency = ExerciseDependency::Exercise; - m_dbClassTables.clear(); - m_struct2BaseClass.clear(); - m_structsToGenerate.clear(); - m_idlStructsToGenerate.clear(); - m_tables.clear(); - m_views.clear(); - m_enumerations.clear(); - m_functionTopics.clear(); - m_externalTopicTypes.clear(); - m_customFunctions.clear(); - m_prio = 0; - m_svcDevMode = false; - - - if (parent) - { - m_idlFiles = parent->m_idlFiles; - m_xmlFiles = parent->m_xmlFiles; - m_fullServiceName2Abbreviation = parent->m_fullServiceName2Abbreviation; - m_externalXmlInformations = parent->m_externalXmlInformations; - } - - - - loadIdlInfofile(idlServiceInfoFile); - - - QFile f(svcFile); - if (!f.exists()) - { - ERROR_LOG(m_logger) << "Service file '" << svcFile << "' not found!"; - return false; - } - if (!f.open(QIODevice::ReadOnly)) - { - m_errorString = QLatin1String("Can not open service file '") + svcFile + - QLatin1String("' for reading: ") + f.errorString(); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - INFO_LOG(m_logger) << "SvcFile: " << svcFile; - - - QString errMsg; - int errLine = 0; - int errColumn = 0; - QDomDocument doc; - if (!doc.setContent(&f, &errMsg, &errLine, &errColumn)) - { - m_errorString = QLatin1String("Error parsing service file '") + svcFile + QLatin1String("': ") + - errMsg + QLatin1String(" at ") + QString::number(errLine) + - QLatin1Char(':') + QString::number(errColumn); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - - - - - - - - QDomElement domRoot = doc.documentElement(); - if (!domRoot.hasAttribute(QStringLiteral("namespace"))) - { - m_errorString = QLatin1String("Xml file '") + svcFile + - QLatin1String("' is not a valid service file: service name/namespace is missing"); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - m_svcNamespace = getAttribute(domRoot, QStringLiteral("namespace")); - m_svcAbbr = getAttribute(domRoot, QStringLiteral("abbreviation")).toLower(); - m_svcProcNames = getAttribute(domRoot, QStringLiteral("procNames")).split(QLatin1Char(',')); - m_customRecordReplay = getAttributeBool(domRoot, QStringLiteral("customRecordReplay"), true, false); - - QString devModeStr = getAttribute(domRoot, QStringLiteral("devMode"), true); - m_svcDevMode = devModeStr == QStringLiteral("1") || - devModeStr == QStringLiteral("true"); - INFO_LOG(m_logger) << "devMode: " << m_svcDevMode; - - const QString groupNamesString = getAttribute(domRoot, QStringLiteral("groups"), true); - if (!groupNamesString.isEmpty()) - { - m_groupNames = groupNamesString.split(QLatin1Char(','), QString::SkipEmptyParts); - INFO_LOG(m_logger) << "groups: " << groupNamesString; - } - else - m_groupNames.clear(); - - m_exerciseDependency = string2ExerciseDependency(getAttribute(domRoot, QStringLiteral("exerciseDependency"), true)); - - - - if (m_groupNames.contains(QStringLiteral("Core")) || - m_groupNames.contains(QStringLiteral("Master"))) - { - if (m_exerciseDependency != ExerciseDependency::Global && - m_exerciseDependency != ExerciseDependency::GlobalAndExercise) - { - WARNING_LOG(m_logger) << "Service " << getServiceNamespace() - << " is not marked as exercise independent but should be started in group Core/Master"; - } - } - - INFO_LOG(m_logger) << "exerciseDependency: " << (int)m_exerciseDependency; - if ( domRoot.hasAttribute(QStringLiteral("prio"))) - { - m_prio = getAttribute(domRoot, QStringLiteral("prio"), 0).toInt(); - } - try - { - parseDBSserviceStructs(domRoot.firstChildElement(QStringLiteral("dbServiceStructs"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing dbServiceStructs: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseDBTables(domRoot.firstChildElement(QStringLiteral("tables"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing dbtables: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseDBViews(domRoot.firstChildElement(QStringLiteral("views"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing dbviews: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseEnumerations(domRoot.firstChildElement(QStringLiteral("enumerations"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing enumerations: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseCustomDtcFunctions(domRoot.firstChildElement(QStringLiteral("customDtcFunctions"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing custom dtc functions: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseTopicTypes(domRoot.firstChildElement(QStringLiteral("topicTypes"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing idl strucs: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - try - { - parseFunctionTopics(domRoot.firstChildElement(QStringLiteral("functionTopics"))); - } - catch (const XmlReaderException &ex) - { - m_errorString = QLatin1String("Error parsing function topics: ") + QLatin1String(ex.what()); - ERROR_LOG(m_logger) << m_errorString; - return false; - } - - return true; -} - -QString XmlReader::getAttribute(const QDomElement &element, - const QString &attributeName, - bool bOptional) -{ - if (!element.hasAttribute(attributeName)) - { - if (bOptional) - return QString(); - throw XmlReaderException(QLatin1String("Element ") + element.tagName() + - QLatin1String(" has no attribute ") + attributeName); - } - return element.attribute(attributeName); -} - -bool XmlReader::getAttributeBool(const QDomElement &element, - const QString &attributeName, - bool bOptional, bool defaultValue) -{ - QString str = getAttribute(element, attributeName, bOptional); - if (str.isEmpty()) - return defaultValue; - bool bOk; - bool ret = (str.toInt(&bOk) != 0); - if (!bOk) - throw XmlReaderException(QLatin1String("Can not parse boolean value: ") + str); - return ret; -} - -int XmlReader::getAttributeInt(const QDomElement &element, - const QString &attributeName, - bool bOptional, int defaultValue) -{ - QString str = getAttribute(element, attributeName, bOptional); - if (str.isEmpty()) - return defaultValue; - bool bOk; - int ret = str.toInt(&bOk); - if (!bOk) - { - - ret = str.toUInt(&bOk,16); - if(!bOk) - throw XmlReaderException(QLatin1String("Can not parse int value: ") + str); - } - return ret; -} - -void XmlReader::parseDBSserviceStructs(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - - QDomElement domDbStruct = rootElement.firstChildElement(QStringLiteral("serviceStruct")); - while (!domDbStruct.isNull()) - { - ServiceStructInformation ssi; - ssi.name = getAttribute(domDbStruct, QStringLiteral("name")); - ssi.primaryTable = getAttribute(domDbStruct, QStringLiteral("table")); - ssi.hasStaticContent = getAttributeBool(domDbStruct, QStringLiteral("static"), true, false); - - QDomElement domFunc = domDbStruct.firstChildElement(QStringLiteral("function")); - while (!domFunc.isNull()) - { - m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Db); - domFunc = domFunc.nextSiblingElement(domFunc.tagName()); - } - - QDomElement domNotify = domDbStruct.firstChildElement(QStringLiteral("notification")); - while (!domNotify.isNull()) - { - ssi.dbNotifications.append(parseDbNotification(domNotify)); - domNotify = domNotify.nextSiblingElement(domNotify.tagName()); - } - - m_structsToGenerate.push_back(ssi); - - domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName()); - } - std::sort(m_structsToGenerate.begin(), m_structsToGenerate.end()); - - - domDbStruct = rootElement.firstChildElement(QStringLiteral("table")); - while (!domDbStruct.isNull()) - { - const QString table = getAttribute(domDbStruct, QStringLiteral("name")); - const QString parentTable = getAttribute(domDbStruct, QStringLiteral("base"), true); - m_struct2BaseClass[table] = parentTable; - - domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName()); - } - INFO_LOG(m_logger) << "Read " << m_structsToGenerate.size() << " database service struct(s)"; -} - -void XmlReader::parseDBTables(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - QDomElement domDbTable = rootElement.firstChildElement(QStringLiteral("table")); - while (!domDbTable.isNull()) - { - QSharedPointer ti(new TableInformation); - ti->name = getAttribute(domDbTable, QStringLiteral("name")); - ti->structName = QLatin1String("t_") + ti->name; - ti->comment = getAttribute(domDbTable, QStringLiteral("comment"), true); - ti->exerciseDependency = string2ExerciseDependency(getAttribute(domDbTable, QStringLiteral("exerciseDependency"), true)); - - QDomElement domColumn = domDbTable.firstChildElement(QStringLiteral("column")); - while (!domColumn.isNull()) - { - ti->plainColumns.append(parseTableColumn(domColumn, ti->name)); - domColumn = domColumn.nextSiblingElement(domColumn.tagName()); - } - - QDomElement domForeignKey = domDbTable.firstChildElement(QStringLiteral("foreignKey")); - while (!domForeignKey.isNull()) - { - ti->foreignKeys.append(parseForeignKey(domForeignKey)); - domForeignKey = domForeignKey.nextSiblingElement(domForeignKey.tagName()); - } - QDomElement domConstraint = domDbTable.firstChildElement(QStringLiteral("constraint")); - while (!domConstraint.isNull()) - { - ti->constraints.append(parseConstraint(domConstraint)); - domConstraint = domConstraint.nextSiblingElement(domConstraint.tagName()); - } - - m_tables.insert(ti->name, ti); - - domDbTable = domDbTable.nextSiblingElement(domDbTable.tagName()); - } - - INFO_LOG(m_logger) << "Read " << m_tables.size() << " database table(s)"; -} - -void XmlReader::parseDBViews(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - QDomElement domDbView = rootElement.firstChildElement(QStringLiteral("view")); - while (!domDbView.isNull()) - { - QSharedPointer vi(new ViewInformation); - vi->name = getAttribute(domDbView, QStringLiteral("name")); - vi->structName = QLatin1String("v_") + vi->name; - vi->baseSchema = m_svcAbbr; - vi->baseTable = getAttribute(domDbView, QStringLiteral("base")); - vi->comment = getAttribute(domDbView, QStringLiteral("comment"), true); - vi->filter = getAttribute(domDbView, QStringLiteral("filter")); - vi->exerciseDependency = string2ExerciseDependency(getAttribute(domDbView, QStringLiteral("exerciseDependency"), true)); - - DEBUG_LOG(m_logger) << "Base table = " << vi->baseTable; - - - m_views.insert(vi->name, vi); - - domDbView = domDbView.nextSiblingElement(domDbView.tagName()); - } - - INFO_LOG(m_logger) << "Read " << m_views.size() << " database view(s)"; -} - -void XmlReader::parseEnumerations(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - QDomElement domEnum = rootElement.firstChildElement(QStringLiteral("enumeration")); - while (!domEnum.isNull()) - { - EnumInformation enumeration; - enumeration.serviceName = getServiceNamespace(); - enumeration.enumName = getAttribute(domEnum, QStringLiteral("name")); - enumeration.tableName = getAttribute(domEnum, QStringLiteral("table"), true); - - QDomElement enumVal = domEnum.firstChildElement(QStringLiteral("attribute")); - while (!enumVal.isNull()) - { - EnumKeyValuePair evp; - evp.key = getAttribute(enumVal, QStringLiteral("key")); - evp.value = getAttributeInt(enumVal, QStringLiteral("value")); - enumeration.values.append(evp); - - enumVal = enumVal.nextSiblingElement(enumVal.tagName()); - } - - m_enumerations.push_back(enumeration); - - domEnum = domEnum.nextSiblingElement(domEnum.tagName()); - } - INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " enumeration(s)"; -} - -void XmlReader::parseCustomDtcFunctions(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - QDomElement domElement = rootElement.firstChildElement(QStringLiteral("customDtcFunction")); - while (!domElement.isNull()) - { - - QDomElement domFunc = domElement.firstChildElement(QStringLiteral("function")); - while (!domFunc.isNull()) - { - m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Dtc); - domFunc = domFunc.nextSiblingElement(domFunc.tagName()); - } - domElement = domElement.nextSiblingElement(domElement.tagName()); - } - INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " customDtcFunction(s)"; -} - - - -void XmlReader::parseTopicTypes(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - - QDomElement domIdlStruct = rootElement.firstChildElement(QStringLiteral("topicType")); - while (!domIdlStruct.isNull()) - { - TopicTypeInformation isi; - isi.name = getAttribute(domIdlStruct, QStringLiteral("name")); - isi.baseValueType = getAttribute(domIdlStruct, QStringLiteral("baseValueType"), true); - - - const QVector sl = isi.baseValueType.splitRef(QStringLiteral("::")); - if (sl.size() == 2) - loadExternalTopicType(sl.at(0).toString(), sl.at(1).toString()); - - const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true); - - if (tmp.compare(QLatin1String("FINAL"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::FINAL; - if (tmp.compare(QLatin1String("MUTABLE"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::MUTABLE; - if (tmp.compare(QLatin1String("EXTENSIBLE"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE; - - isi.comment = getAttribute(domIdlStruct, QStringLiteral("comment"), true); - isi.isDataObject = getAttributeBool(domIdlStruct, QStringLiteral("isDataObject"), true, false); - isi.dtsAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTS"), true, true); - isi.dtcAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTC"), true, true); - isi.recordTopic = getAttributeBool(domIdlStruct, QStringLiteral("recordTopic"), true, false); - isi.routeToClient = getAttributeBool(domIdlStruct, QStringLiteral("routeToClient"), true, true); - isi.routeToServer = getAttributeBool(domIdlStruct, QStringLiteral("routeToServer"), true, false); - isi.routeInReplay = getAttributeBool(domIdlStruct, QStringLiteral("routeInReplay"), true, false); - isi.autosubscribe = getAttributeBool(domIdlStruct, QStringLiteral("autosubscribe"), true, true); - - QDomElement domColumn = domIdlStruct.firstChildElement(QStringLiteral("column")); - while (!domColumn.isNull()) - { - isi.columns.append(parseIdlStructColumn(domColumn)); - domColumn = domColumn.nextSiblingElement(domColumn.tagName()); - } - - - m_idlStructsToGenerate.insert(isi.name, isi); - - domIdlStruct = domIdlStruct.nextSiblingElement(domIdlStruct.tagName()); - } - - - INFO_LOG(m_logger) << "Read " << m_idlStructsToGenerate.size() << " idl struct(s)"; -} - -void XmlReader::parseFunctionTopics(const QDomElement &rootElement) -{ - if (rootElement.isNull()) - return; - - - QDomElement domFunctionTopic = rootElement.firstChildElement(QStringLiteral("functionTopic")); - while (!domFunctionTopic.isNull()) - { - FunctionTopic ft; - ft.name = getAttribute(domFunctionTopic, QStringLiteral("name")); - ft.requestTopicType = getAttribute(domFunctionTopic, QStringLiteral("requestTopicType")); - ft.replyTopicType = getAttribute(domFunctionTopic, QStringLiteral("replyTopicType")); - ft.requestTopicName = getAttribute(domFunctionTopic, QStringLiteral("requestTopicName")); - ft.replyTopicName = getAttribute(domFunctionTopic, QStringLiteral("replyTopicName")); - ft.routeInReplay = getAttributeBool(domFunctionTopic, QStringLiteral("routeInReplay"), true, false); - - m_functionTopics.append(ft); - - domFunctionTopic = domFunctionTopic.nextSiblingElement(domFunctionTopic.tagName()); - } - - for (const FunctionTopic &ft : qAsConst(m_functionTopics)) - { - const QVector sl1 = ft.requestTopicType.splitRef(QStringLiteral("::")); - if (sl1.size() == 2) - loadExternalTopicType(sl1.at(0).toString(), sl1.at(1).toString()); - const QVector sl2 = ft.replyTopicType.splitRef(QStringLiteral("::")); - if (sl2.size() == 2) - loadExternalTopicType(sl2.at(0).toString(), sl2.at(1).toString()); - } -} - -ColumnInformation XmlReader::parseTableColumn(const QDomElement &column, const QString &tableName) const -{ - ColumnInformation ci; - - ci.columnName = getAttribute(column, QStringLiteral("name")); - ci.datatype = getAttribute(column, QStringLiteral("datatype")); - ci.defaultValue = getAttribute(column, QStringLiteral("default"), true); - ci.comment = getAttribute(column, QStringLiteral("comment"), true); - ci.isPrimaryKey = getAttributeBool(column, QStringLiteral("isPrimaryKey"), true, false); - ci.isForeignKey = getAttributeBool(column, QStringLiteral("isForeignKey"), true, false); - ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false); - const QDomElement seq = column.firstChildElement(QStringLiteral("sequence")); - if (!seq.isNull()) - { - ci.sequenceStart = getAttributeInt(seq, QStringLiteral("start"), true, -1); - ci.sequenceIncrement = getAttributeInt(seq, QStringLiteral("increment"), true, -1); - ci.hasSequence = ci.sequenceStart >= 0 && ci.sequenceIncrement > 0; - } - - if (ci.columnName.toLower() != ci.columnName) - WARNING_LOG(m_logger) << "Column " << ci.columnName << " in Table " - << tableName << " has has upper-case chars which is not allowed!"; - - return ci; -} - -IdlColumnInformation XmlReader::parseIdlStructColumn(const QDomElement &column) const -{ - IdlColumnInformation ci; - - ci.name = getAttribute(column, QStringLiteral("name")); - ci.type = getAttribute(column, QStringLiteral("idlType")); - ci.comment = getAttribute(column, QStringLiteral("comment"), true); - ci.isKey = getAttributeBool(column, QStringLiteral("isKey"), true, false); - ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false); - ci.isTranslatable = getAttributeBool(column, QStringLiteral("isTranslatable"), true, false); - - return ci; -} - -QSharedPointer XmlReader::parseForeignKey(const QDomElement &foreignKey) const -{ - QSharedPointer fk(new ForeignKeyInformation); - - fk->name = getAttribute(foreignKey, QStringLiteral("name")); - - const QString deleteAction = getAttribute(foreignKey, QStringLiteral("referential_integrity"), true); - if (deleteAction == QLatin1String("DeleteNoAction")) - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::NoAction; - else if (deleteAction == QLatin1String("DeleteCascade")) - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade; - else if (deleteAction == QLatin1String("DeleteRestrict")) - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Restrict; - else if (deleteAction == QLatin1String("DeleteSetNull")) - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetNull; - else if (deleteAction == QLatin1String("DeleteSetDefault")) - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetDefault; - else - fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade; - - fk->source = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("source"))); - fk->destination = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("destination"))); - - return fk; -} - -ForeignKeyInformation::Endpoint XmlReader::parseForeignKeyEndpoint(const QString &foreignKeyName, - const QDomElement &endpoint) const -{ - if (endpoint.isNull()) - throw XmlReaderException(QLatin1String("No endpoint for foreign key ") + foreignKeyName); - - ForeignKeyInformation::Endpoint fkEndpoint; - - fkEndpoint.service = getAttribute(endpoint, QStringLiteral("service"), true); - fkEndpoint.serviceAbbr= getAttribute(endpoint, QStringLiteral("serviceAbbr"), true); - fkEndpoint.schema = getAttribute(endpoint, QStringLiteral("schema"), true); - fkEndpoint.tableName = getAttribute(endpoint, QStringLiteral("table")); - fkEndpoint.columnName = getAttribute(endpoint, QStringLiteral("column")); - - return fkEndpoint; -} - -ConstraintInformation XmlReader::parseConstraint(const QDomElement &constraint) const -{ - ConstraintInformation ret; - - ret.name = getAttribute(constraint, QStringLiteral("name"), true); - const QString type = getAttribute(constraint, QStringLiteral("type")).toLower(); - if (type == QLatin1String("unique")) - ret.type = ConstraintInformation::Type::unique; - else if (type == QLatin1String("index")) - ret.type = ConstraintInformation::Type::index; - - QDomElement domColumn = constraint.firstChildElement(QStringLiteral("column")); - if (domColumn.isNull()) - throw XmlReaderException(QLatin1String("No column for constraint") + ret.name); - while (!domColumn.isNull()) - { - ret.affectedColumns += getAttribute(domColumn, QStringLiteral("name")); - domColumn = domColumn.nextSiblingElement(domColumn.tagName()); - } - return ret; -} - -CustomServiceFunctionInformation XmlReader::parseCustomFunction(const QDomElement &customFunc, - CustomServiceFunctionInformation::Type funcType) const -{ - CustomServiceFunctionInformation ret; - ret.methodName = getAttribute(customFunc, QStringLiteral("name")); - ret.returnValue = getAttribute(customFunc, QStringLiteral("returns"), true); - ret.isConst = getAttributeBool(customFunc, QStringLiteral("isConst"), true, false); - if (ret.returnValue.isEmpty()) - ret.returnValue = QStringLiteral("bool"); - QMap> paramHelper; - QDomElement domParam = customFunc.firstChildElement(QStringLiteral("parameter")); - while (!domParam.isNull()) - { - int pos = getAttributeInt(domParam, QStringLiteral("pos")); - if (paramHelper.contains(pos)) - throw XmlReaderException(QLatin1String("Doubled position entry for custom function ") + ret.methodName); - QString paramName = getAttribute(domParam, QStringLiteral("name")); - const QString paramType = getAttribute(domParam, QStringLiteral("type")); - const QString paramDefault = getAttribute(domParam, QStringLiteral("default"), true); - if (!paramDefault.isEmpty()) - paramName.append(QLatin1String(" = ") + paramDefault); - paramHelper[pos] = qMakePair(paramName, paramType); - domParam = domParam.nextSiblingElement(domParam.tagName()); - - } - ret.paramNames.reserve(paramHelper.size()); - ret.paramTypes.reserve(paramHelper.size()); - ret.type = funcType; - QStringList signature; - for (auto it = paramHelper.constBegin(); it != paramHelper.constEnd(); ++it) - { - ret.paramNames += it.value().first; - ret.paramTypes += it.value().second; - QString tmp = it.value().second; - if (!it.value().second.endsWith(QLatin1Char('&'))) - tmp += QLatin1Char(' '); - tmp += it.value().first; - signature += tmp; - } - ret.signature += signature.join(QStringLiteral(", ")); - return ret; -} - -DbNotifcationInformation XmlReader::parseDbNotification(const QDomElement &customFunc) const -{ - DbNotifcationInformation ret(parseCustomFunction(customFunc, CustomServiceFunctionInformation::Type::DbNotify)); - - const QString attribute(getAttribute(customFunc, QStringLiteral("notificationTypes")).toLower()); - const QStringList nfTypes = attribute.split(QLatin1Char(',')); - static const QMap s_nfTypeMap = - { - { QLatin1String("beforeinsert"), DbNotifcationInformation::NotificationType::BeforeInsert }, - { QLatin1String("afterinsert"), DbNotifcationInformation::NotificationType::AfterInsert }, - { QLatin1String("beforeupdate"), DbNotifcationInformation::NotificationType::BeforeUpdate }, - { QLatin1String("afterupdate"), DbNotifcationInformation::NotificationType::AfterUpdate }, - { QLatin1String("beforedelete"), DbNotifcationInformation::NotificationType::BeforeDelete }, - { QLatin1String("afterdelete"), DbNotifcationInformation::NotificationType::AfterDelete } - }; - for (const QString &nfType : nfTypes) - { - const auto it = s_nfTypeMap.find(nfType); - if (it == s_nfTypeMap.end()) - throw XmlReaderException(QStringLiteral("Can not find notification type %2 in type map for function %1").arg(ret.methodName, nfType)); - ret.notificationType += it.value(); - } - if (ret.notificationType.isEmpty()) - throw XmlReaderException(QStringLiteral("Notification function '%1' without a valid notification type").arg(ret.methodName)); - if (ret.paramTypes.size() != 1) - throw XmlReaderException(QStringLiteral("Notification function '%1' must have exactly one parameter").arg(ret.methodName)); - - return ret; -} - -TopicTypeInformation XmlReader::loadExternalTopicType(const QString &externalServiceName, const QString &topicTypeName) -{ - DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << topicTypeName; - - if (!loadExternalService(externalServiceName)) - { - WARNING_LOG(m_logger) << "Could not find service " << externalServiceName - << " referenced by external topic " << topicTypeName; - return TopicTypeInformation(); - } - auto extXmlReader = m_externalXmlInformations.value(externalServiceName); - if (!extXmlReader) - { - ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'"; - return TopicTypeInformation(); - } - - TopicTypeInformation tti = extXmlReader->getIdlStructInformation().value(topicTypeName); - if (tti.isValid()) - { - tti.name = externalServiceName + QLatin1String("::") + tti.name; - m_externalTopicTypes += tti; - m_usedServices[externalServiceName] = true; - return tti; - } - WARNING_LOG(m_logger) << "Could not find topic " << topicTypeName - << " in external service definition for service " - << externalServiceName; - return TopicTypeInformation(); -} - -EnumInformation XmlReader::getExternalEnumeration(const QString &externalServiceName, const QString &enumName, bool verbose) -{ - DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << enumName; - - if (!loadExternalService(externalServiceName)) - { - WARNING_LOG(m_logger) << "Could not find service '" << externalServiceName << "'" - << " referenced by external enumeration " << enumName; - return EnumInformation(); - } - auto extXmlReader = m_externalXmlInformations.value(externalServiceName); - if (!extXmlReader) - { - ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'"; - return EnumInformation(); - } - - const QVector enums = extXmlReader->getEnumerations(); - for (const EnumInformation &ei : enums) - { - if (ei.enumName == enumName) - { - m_usedServices[externalServiceName] = true; - return ei; - } - } - if (verbose) - { - WARNING_LOG(m_logger) << "Could not find enumeration " << enumName - << " in external service definition for '" - << externalServiceName << "'"; - } - return EnumInformation(); -} - - -bool XmlReader::loadExternalService(const QString &serviceName) -{ - if (serviceName == getServiceNamespace()) - return false; - if (m_externalXmlInformations.contains(serviceName)) - return true; - - QString fileName = xmlFileForService(serviceName); - if (fileName.isEmpty()) - { - const QVector dirs = NGL::directories(NGL::FileType::ConfigFile); - for (const QString &dirname : dirs) - { - QDirIterator dirIt(dirname + QStringLiteral("/xml"), QDir::Files | QDir::NoDotAndDotDot | QDir::Readable); - while (dirIt.hasNext()) - { - dirIt.next(); - if (!dirIt.fileName().endsWith(QLatin1String(".xml"))) - continue; - - fileName = dirIt.filePath(); - const QString svcNamespace = serviceNamespaceFromFile(fileName); - if (svcNamespace == serviceName) - break; - } - } - } - if (fileName.isEmpty()) - return false; - - DEBUG_LOG(m_logger) << "xml file for " << serviceName << ": " << fileName; - auto extXmlReader = QSharedPointer(new XmlReader); - if (!extXmlReader->readServiceFile(fileName, QString(), this)) - { - WARNING_LOG(m_logger) << "Error reading service file " << fileName - << " for service " << serviceName; - return false; - } - m_externalXmlInformations[serviceName] = extXmlReader; - if (!m_usedServices.contains(serviceName)) - m_usedServices[serviceName] = false; - return true; -} - -void XmlReader::loadIdlInfofile(const QString &idlServiceInfoFile) -{ - if (idlServiceInfoFile.isEmpty()) - return; - QFile f(idlServiceInfoFile); - if (!f.open(QIODevice::ReadOnly)) - { - WARNING_LOG(m_logger) << "Could not read idl service info file " << idlServiceInfoFile; - return; - } - const QList files = f.readAll().split('\n'); - for (const QByteArray &filePath : files) - { - - if (filePath.startsWith("--") || filePath.trimmed().isEmpty()) - continue; - const QString fileName = QString::fromLocal8Bit(filePath.trimmed()); - const QVector parts = fileName.splitRef(QLatin1Char('/')); - getServiceNameInfoFromFilePath(parts); - const auto fn = parts.last().trimmed(); - if (fn.endsWith(QLatin1String(".xml"))) - { - const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).split(QLatin1Char('_')).at(0); - m_xmlFiles[svcAbbreveation.toString()] = fileName; - DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation.toString() << " = " << fileName; - } - else - { - const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).toString().toLower(); - if (svcAbbreveation.endsWith(QLatin1String("_enums"))) - continue; - m_idlFiles[svcAbbreveation] = fileName; - DEBUG_LOG(m_logger) << "idl: " << svcAbbreveation << " = " << fileName; - } - } -} - -QString XmlReader::getIdlFileForService(const QMap &data, - const QString &serviceName) const -{ - const QString serviceAbbr = serviceNameAbbreviation(serviceName); - return data.value(serviceAbbr); -} - - -QString XmlReader::serviceNameAbbreviation(const QString &serviceName) const -{ - return m_fullServiceName2Abbreviation.value(serviceName); -} - -void XmlReader::getServiceNameInfoFromFilePath(const QVector &parts) -{ - if (parts.size() < 2) - return; - - - const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first().toString(); - const QString serviceName = parts.at(parts.size() - 2).toString(); - DEBUG_LOG(m_logger) << "serviceName: " << serviceName << " = " << abbreviation; - m_fullServiceName2Abbreviation[serviceName] = abbreviation; -} - -ExerciseDependency XmlReader::string2ExerciseDependency(const QString &str) -{ - if (str.isEmpty()) - return ExerciseDependency::Undefined; - const QStringList sd = str.toLower().split(QLatin1Char(',')); - int ret = ExerciseDependency::Undefined; - for (const QString &s : sd) - { - if (s == QLatin1String("global")) - ret |= ExerciseDependency::Global; - else if (s == QLatin1String("exercise")) - ret |= ExerciseDependency::Exercise; - else if (s == QLatin1String("globalandexercise")) - ret |= ExerciseDependency::GlobalAndExercise; - } - return static_cast(ret); -} - -QString XmlReader::serviceNamespaceFromFile(const QString &fn) -{ - QFile f(fn); - if (!f.open(QIODevice::ReadOnly)) - return QString(); - QXmlStreamReader sr(&f); - while (!sr.atEnd()) - { - sr.readNext(); - if (sr.isStartElement() && sr.name() == QLatin1String("service")) - return (sr.attributes().value(QLatin1String("namespace"))).toString(); - } - return QString(); -} - diff --git a/Lts/Common/CodeGen/XmlReader.h b/Lts/Common/CodeGen/XmlReader.h deleted file mode 100755 index b825b4f5c..000000000 --- a/Lts/Common/CodeGen/XmlReader.h +++ /dev/null @@ -1,427 +0,0 @@ - - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include "codegen_export.h" - -namespace NGL -{ -class INGLLogger; -} -class QDomElement; - -namespace CodeGen -{ - -enum ExerciseDependency -{ - Undefined = 0, - Global = 0x1, - Exercise = 0x2, - GlobalAndExercise = 0x3 -}; - -struct EnumKeyValuePair -{ - QString key; - int value = 0; -}; - -struct EnumInformation -{ - QString enumName; - QString tableName; - QString serviceName; - QVector values; - - bool isValid() const { return !enumName.isEmpty(); } -}; - -struct ColumnInformation -{ - QString columnName; - QString datatype; - QString comment; - QString defaultValue; - bool isPrimaryKey = false; - bool isAssignmentForPrimaryTablePk = false; - bool isForeignKey = false; - bool generateSelectRemove = false; - bool canBeNull = true; - bool hasSequence = false; - bool isOptional = false; - int sequenceStart = 0; - int sequenceIncrement = 0; -}; - -struct IdlColumnInformation -{ - QString name; - QString type; - QString comment; - bool isKey = false; - bool isOptional = false; - bool isTranslatable = false; -}; - - -struct ForeignKeyInformation -{ - struct Endpoint - { - QString service; - QString serviceAbbr; - QString schema; - QString tableName; - QString columnName; - }; - - QString name; - Endpoint source; - Endpoint destination; - enum class OnDeleteAction - { - NoAction, - Cascade, - Restrict, - SetNull, - SetDefault - } deleteAction = OnDeleteAction::Cascade; -}; - -struct CustomServiceFunctionInformation -{ - QString methodName; - QString returnValue; - QString signature; - bool isConst = false; - QVector paramTypes; - QVector paramNames; - enum class Type - { - Db, - DbNotify, - Dtc - } type = Type::Db; -}; - -struct DbNotifcationInformation : public CustomServiceFunctionInformation -{ - DbNotifcationInformation() = default; - explicit DbNotifcationInformation(const CustomServiceFunctionInformation &d) - : CustomServiceFunctionInformation(d) - {} - enum class NotificationType - { - BeforeInsert, - AfterInsert, - BeforeUpdate, - AfterUpdate, - BeforeDelete, - AfterDelete - }; - QVector notificationType; -}; - -struct ConstraintInformation -{ - enum struct Type - { - unknown = 0x00, - unique = 0x01, - index = 0x02 - } type = Type::unknown; - QString name; - QStringList affectedColumns; -}; - -struct TableInformation -{ - QString name; - QString structName; - QString comment; - QVector plainColumns; - QVector > foreignKeys; - QVector constraints; - ExerciseDependency exerciseDependency = ExerciseDependency::Undefined; - - QSharedPointer fkInformationForColumn(const QString &tableName, - const QString &columnName) const - { - for (const QSharedPointer &fki : foreignKeys) - { - if (fki->source.columnName == columnName && - fki->source.tableName == tableName) - return fki; - } - return QSharedPointer(); - } -}; - -struct ViewInformation -{ - QString name; - QString comment; - QString structName; - QString baseTable; - QString baseSchema; - QString filter; - ExerciseDependency exerciseDependency = ExerciseDependency::Undefined; -}; - -struct ServiceStructInformation -{ - QString name; - QString primaryTable; - bool hasStaticContent = false; - - bool operator<(const ServiceStructInformation &o) const - { - if (hasStaticContent != o.hasStaticContent) - return hasStaticContent < o.hasStaticContent; - if (name != o.name) - return name < o.name; - return primaryTable < o.primaryTable; - } - QVector dbNotifications; -}; - -struct TopicTypeInformationBase -{ - TopicTypeInformationBase() = default; - virtual ~TopicTypeInformationBase() = default; - - QString name; - QString baseValueType; - QString comment; - bool isDataObject = false; - bool dtsAutogen = true; - bool dtcAutogen = true; - bool recordTopic = false; - bool routeToClient = true; - bool routeToServer = false; - bool routeInReplay = true; - bool autosubscribe = true; - - dds::core::xtypes::ExtensibilityKind extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE; - - virtual bool isValid() const { return !name.isEmpty(); } - - bool operator<(const TopicTypeInformationBase &o) const - { - if (name != o.name) - return name < o.name; - return baseValueType < o.baseValueType; - } -}; - -struct TopicTypeInformation : public TopicTypeInformationBase -{ - QVector columns; -}; - -struct FunctionTopic -{ - QString name; - QString requestTopicType; - QString replyTopicType; - QString requestTopicName; - QString replyTopicName; - bool routeInReplay = false; -}; - - -class CODEGEN_BASE_EXPORT XmlReader final -{ -public: - XmlReader(); - - bool readServiceFile(const QString &svcFile, - const QString &idlServiceInfoFile = QString(), - const XmlReader *parent = nullptr); - QString errorString() const - { - return m_errorString; - } - - QString getServiceNamespace() const { return m_svcNamespace; } - QString getServiceAbbr() const { return m_svcAbbr; } - QStringList getGroupNames() const { return m_groupNames; } - int getPrio() const { return m_prio;} - QStringList getProcNames() const { return m_svcProcNames; } - bool hasCustomRecordReplayPlugin() const { return m_customRecordReplay; } - bool getDevMode() const { return m_svcDevMode; } - - bool isServiceExerciseIndependent() const { return m_exerciseDependency == ExerciseDependency::Global; } - ExerciseDependency defaultExerciseDependency() const { return m_exerciseDependency; } - - QMap > getDbClass2TableMapping() const { return m_dbClassTables; } - - - QMap getStruct2BaseClassRelations() const { return m_struct2BaseClass; } - QVector getStructToGenerate() const { return m_structsToGenerate; } - - - QMap getIdlStructInformation() const { return m_idlStructsToGenerate; } - QVector getIdlStructToGenerate() const - { - QVector res; - res.reserve(m_idlStructsToGenerate.size()); - auto it = m_idlStructsToGenerate.constBegin(); - while (it != m_idlStructsToGenerate.constEnd()) - { - res.append(it.value()); - ++it; - } - return res; - } - - - QMap > getTables() const - { - return m_tables; - } - QMap > getViews() const - { - return m_views; - } - QSharedPointer getTable(const QString &table) const - { - return m_tables.value(table); - } - QVector getEnumerations() const - { - return m_enumerations; - } - QVector getIdlColumns(const QString &structName) const - { - return m_idlStructsToGenerate.value(structName).columns; - } - - QVector getFunctionTopics() const - { - return m_functionTopics; - } - QVector getExternalTopicTypes() const - { - return m_externalTopicTypes; - } - - QString idlFileForService(const QString &serviceName) const - { - return getIdlFileForService(m_idlFiles, serviceName); - } - - QString xmlFileForService(const QString &serviceName) const - { - return getIdlFileForService(m_xmlFiles, serviceName); - } - - QMap usedServices() const - { - return m_usedServices; - } - - QVector getCustomFunctions() const - { - return m_customFunctions; - } - - - TopicTypeInformation loadExternalTopicType(const QString &externalServiceName, const QString &topicTypeName); - EnumInformation getExternalEnumeration(const QString &externalServiceName, const QString &enumName, bool verbose); - - static ExerciseDependency string2ExerciseDependency(const QString &str); - - XmlReader(const XmlReader &) = delete; - XmlReader &operator=(const XmlReader &) = delete; - -private: - void parseDBSserviceStructs(const QDomElement &rootElement); - void parseDBTables(const QDomElement &rootElement); - void parseDBViews(const QDomElement &rootElement); - void parseEnumerations(const QDomElement &rootElement); - void parseTopicTypes(const QDomElement &rootElement); - void parseFunctionTopics(const QDomElement &rootElement); - void parseCustomDtcFunctions(const QDomElement &rootElement); - - static QString getAttribute(const QDomElement &element, - const QString &attributeName, - bool bOptional = false); - static bool getAttributeBool(const QDomElement &element, - const QString &attributeName, - bool bOptional, bool defaultValue); - static int getAttributeInt(const QDomElement &element, - const QString &attributeName, - bool bOptional = false, int defaultValue = 0); - - ColumnInformation parseTableColumn(const QDomElement &column, const QString &tableName) const; - IdlColumnInformation parseIdlStructColumn(const QDomElement &column) const; - - QSharedPointer parseForeignKey(const QDomElement &foreignKey) const; - ForeignKeyInformation::Endpoint parseForeignKeyEndpoint(const QString &foreignKeyName, - const QDomElement &endpoint) const; - ConstraintInformation parseConstraint(const QDomElement &constraint) const; - CustomServiceFunctionInformation parseCustomFunction(const QDomElement &customFunc, CustomServiceFunctionInformation::Type funcType) const; - DbNotifcationInformation parseDbNotification(const QDomElement &customFunc) const; - bool loadExternalService(const QString &serviceName); - void loadIdlInfofile(const QString &idlServiceInfoFile); - QString getIdlFileForService(const QMap &data, const QString &serviceName) const; - QString serviceNameAbbreviation(const QString &serviceName) const; - void getServiceNameInfoFromFilePath(const QVector &parts); - static QString serviceNamespaceFromFile(const QString &fn); - - - QString m_svcNamespace; - - QString m_svcAbbr; - - QStringList m_svcProcNames; - - bool m_customRecordReplay; - - - ExerciseDependency m_exerciseDependency{ExerciseDependency::Exercise}; - - - QMap > m_dbClassTables; - - - QMap m_struct2BaseClass; - QVector m_structsToGenerate; - - QMap m_idlStructsToGenerate; - - QVector m_functionTopics; - QVector m_externalTopicTypes; - QVector m_customFunctions; - - - QMap > m_tables; - QMap > m_views; - QVector m_enumerations; - QString m_errorString; - - - QMap > m_externalXmlInformations; - QMap m_idlFiles; - QMap m_xmlFiles; - QMap m_usedServices; - QMap m_fullServiceName2Abbreviation; - - QStringList m_groupNames; - int m_prio; - bool m_svcDevMode{false}; - - NGL::INGLLogger &m_logger; -}; -} diff --git a/Lts/Common/CodeGen/codegen_export.h b/Lts/Common/CodeGen/codegen_export.h deleted file mode 100755 index 698bad14f..000000000 --- a/Lts/Common/CodeGen/codegen_export.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include - -#ifndef CODEGEN_BASE_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define CODEGEN_BASE_EXPORT -# else -# if defined(MAKE_CODEGEN_BASE_LIB) -# define CODEGEN_BASE_EXPORT Q_DECL_EXPORT -# else -# define CODEGEN_BASE_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif diff --git a/Lts/Common/CodeGen/codegen_log4cxx.props b/Lts/Common/CodeGen/codegen_log4cxx.props deleted file mode 100755 index 889482757..000000000 --- a/Lts/Common/CodeGen/codegen_log4cxx.props +++ /dev/null @@ -1,101 +0,0 @@ -log4j.reset=true -log4j.rootLogger=WARN, stdout - -# CodeGeneration Tool -########################################################### - -log4j.logger.CodeGen.main=WARN -log4j.logger.CodeGen=INFO -log4j.logger.CodeGen.Settings=INFO -log4j.logger.CodeGen.FtsImpl=INFO -log4j.logger.CodeGen.FtcImpl=INFO - -# DTS/DTC, FTS/FTC -log4j.logger.Generic.DTS=INFO -log4j.logger.Generic.DTC=INFO -log4j.logger.Generic.FTS.Function=INFO -log4j.logger.Generic.FTC=INFO -log4j.logger.DTC.Subscriber=INFO -log4j.logger.DTC.SubscriberListener=INFO -log4j.logger.FTS=INFO -log4j.logger.FTC=WARN - -# connext (request/reply feature) -log4j.logger.connext.requester=WARN -log4j.logger.connext.replier=WARN - -# CodeGen -log4j.logger.CodeGen.XmlReader=WARN - -# SqlGenerator -log4j.logger.CodeGen.SqlGenerator=WARN -log4j.logger.CodeGen.StructBuilder=INFO - -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.MaxFileSize=1MB -log4j.appender.file.MaxBackupIndex=9 -log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_codeGen.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n - - -# Appnd_Console -########################################################### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n - - -# Appnd_Syslog -########################################################### -log4j.appender.syslog=org.apache.log4j.SyslogAppender -log4j.appender.syslog.Threshold=ERROR -log4j.appender.syslog.SysLogHost=localhost -log4j.appender.syslog.Facility=LOCAL7 -log4j.appender.syslog.layout=org.apache.log4j.PatternLayout -log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m -log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true -log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO -log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL - - -########################################################### -########################################################### -########################################################### -# 1) Available Levels in LOG4CXX Version 0.10.0: -# TRACE, -# DEBUG, -# INFO, -# WARN, -# ERROR -# FATAL -# -# d = date/time -# p = priority (Level) -# c = category/channel -# m = message -# F = file -# L = line -# n = platform dependent line separator character or characters. -# .... -# -# Examples for console: -# a) -# %d %p %c #%m# (%.50F:%L)%n -# date/time -# priority -# category/channel -# #message# -# (Maximal 50Character des Filename von rechts:Line) -# line separator -# -# b) -# %d (%.15F:%L) %-5p %c |#| %m%n -# date/time -# (Maximal 15Character des Filename von rechts:Line) -# priority, Right pad with spaces if the priority name is less than 5 characters long. -# category/channel -# message -# line separator -# -# Examples for files: diff --git a/Lts/Common/CodeGen/codegen_simple.py b/Lts/Common/CodeGen/codegen_simple.py deleted file mode 100755 index a6d08ee58..000000000 --- a/Lts/Common/CodeGen/codegen_simple.py +++ /dev/null @@ -1,477 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - - - - - - -from __future__ import print_function - -import xml.etree.ElementTree as ET -import os -import re -import string -import sys -import time - -class FtData(object): - def __init__( self ): - self.name = '' - self.namespace = '' - self.requestTopicType = '' - self.requestTopicName = '' - self.replyTopicType = '' - self.replyTopicName = '' - - def fqnVar( self, name ): - if name.find('::') < 0: - name = self.namespace + '::' + name - return name - def stripNamespace( self, str ): - return str.split('::')[-1] - - def getReplyTopicName( self ): - return 'LTS_' + self.stripNamespace( self.replyTopicName ).replace( '::', '_' ) - - def getReplyTopicNameType( self, withNamespace = True ): - var = self.replyTopicName - return self.fqnVar( var ) if withNamespace else self.stripNamespace( var ) - - def getRequestTopicName( self ): - return 'LTS_' + self.stripNamespace( self.requestTopicName ).replace( '::', '_' ) - - def getRequestTopicNameType( self, withNamespace = True ): - var = self.requestTopicName - return self.fqnVar( var ) if withNamespace else self.stripNamespace( var ) - - def getReplyTopicType( self ): - return self.fqnVar( self.replyTopicType ) - def getRequestTopicType( self ): - return self.fqnVar( self.requestTopicType ) - - def needReplyTopicTypedef( self ): - return self.getReplyTopicNameType() != self.getReplyTopicType() - def needRequestTopicTypedef( self ): - return self.getRequestTopicNameType() != self.getRequestTopicType() - -class GuiPluginRights(object): - def __init__( self ): - self.name = '' - self.offset = 0 - self.groups = [] -class GuiPluginRightsGroup(object): - def __init__( self ): - self.name = '' - self.desc = '' - self.values = [] - -class FileParser(object): - def __init__( self ): - self.filename = '' - self.service_abbr = '' - self.module_name = '' - self.struct_names = [] - self.ft_enums = [] - self.ft_replies = [] - self.ft_data = dict() - self.op_right_offset = 0 - self.op_rights = [] - - def build(self, path): - """ Parse the whole stuff ... """ - self.filename = path - self.read_file_xml(self.filename) - - def read_file_xml(self, filename): - """ Read the given xml service file""" - - """print >> sys.stdout, 'Reading xml file %s' % filename""" - xmlTree = ET.parse(filename) - root = xmlTree.getroot() - self.module_name = root.attrib['namespace'] - self.service_abbr = root.attrib['abbreviation'].lower() - - for child in root.iter('topicType'): - struct_name = child.attrib['name'] - if 'isDataObject' in child.attrib and int(child.attrib['isDataObject']) != 0: - self.struct_names.append(struct_name) - for child in root.iter('functionTopic'): - ft_name = child.attrib['name'] - self.ft_enums.append(ft_name) - - ft_data = FtData() - ft_data.name = ft_name - ft_data.namespace = self.module_name - ft_data.requestTopicType = child.attrib['requestTopicType'] - ft_data.requestTopicName = child.attrib['requestTopicName'] - ft_data.replyTopicType = child.attrib['replyTopicType'] - ft_data.replyTopicName = child.attrib['replyTopicName'] - self.ft_data[ft_name] = ft_data - - for child in root.iter('operatorRights'): - self.op_right_offset = int(child.attrib['offset']) - for pluginChild in child.iter('guiPlugin'): - r = GuiPluginRights() - r.name = pluginChild.attrib['name'] - r.offset = int(pluginChild.attrib['offset']) - r.groups = []; - for groups in pluginChild.iter('operatorRightGroup'): - g = GuiPluginRightsGroup(); - g.name = groups.attrib['name'] - g.desc = groups.attrib['description'] if 'description' in groups.attrib else '' - - for right in groups.iter('operatorRight'): - desc = right.attrib['description'] if 'description' in right.attrib else '' - g.values.append((right.attrib['right'], int(right.attrib['value']), desc)) - r.groups.append(g) - self.op_rights.append(r) - - -def outputHeader( outfile, parser, description, with_dds_include = True, with_pragma = True ): - outfile.write('\n') - outfile.write('/**\n') - outfile.write('* %s definitions\n' % description) - outfile.write('*/\n') - outfile.write('\n') - outfile.write('// warning: this file is generated with command\n') - outfile.write('// %s\n' % ( " ".join(sys.argv) )) - outfile.write('// at %s\n' % time.strftime( '%c' )) - outfile.write('// it will be overwritten by next run - do not modify!\n') - outfile.write('\n') - if with_pragma: - outfile.write('#pragma once\n') - outfile.write('\n') - if with_dds_include: - outfile.write('#include "%s.hpp"\n' % ( parser.service_abbr )) - -class DtsGenerator(object): - def generate( self, outfile, parser ): - outputHeader( outfile, parser, 'DTS' ) - outfile.write('\n') - outfile.write('/**\n') - outfile.write(' * Type definitions \n') - outfile.write(' * => DataWriterObjects (DWOs)\n') - outfile.write(' * => DataTopicServers (DTSs)\n') - outfile.write(' */\n') - outfile.write('\n') - outfile.write('namespace DDSGeneric\n') - outfile.write('{\n') - outfile.write('template \n') - outfile.write('class GenericDataWriter;\n') - outfile.write('}\n') - outfile.write('\n') - outfile.write('namespace DTS\n') - outfile.write('{\n') - outfile.write(' template class GenericDts;\n') - outfile.write(' template class GenericDtsImpl;\n') - outfile.write('\n') - - for struct_name in sorted(parser.struct_names): - - topic_name = "%s::%s" % ( parser.module_name, struct_name ) - dwo_name = "Dwo%s" % ( struct_name ) - outfile.write(' // Topic %s\n' % ( topic_name )) - outfile.write(' // Interface\n') - outfile.write(' using %s = GenericDts<%s>;\n' % ( struct_name, topic_name )) - outfile.write(' // Interface impl\n') - outfile.write(' using %s = DDSGeneric::GenericDataWriter<%s>;\n' % ( dwo_name, topic_name )) - outfile.write(' using %sImpl = GenericDtsImpl<%s>;\n' % ( struct_name, dwo_name )) - outfile.write('\n') - - outfile.write('} // namespace DTS\n') - outfile.write('\n') - -class DtcGenerator(object): - def generate( self, outfile, parser ): - outputHeader( outfile, parser, 'DTC' ) - outfile.write('\n') - outfile.write('/**\n') - outfile.write(' * Type definitions \n') - outfile.write(' * => DataTopicClients (DTCs)\n') - outfile.write(' */\n') - outfile.write('\n') - outfile.write('namespace DDSGeneric\n') - outfile.write('{\n') - outfile.write('template \n') - outfile.write('class GenericDataReader;\n') - outfile.write('template \n') - outfile.write('class GenericDataReaderContentFilter;\n') - outfile.write('class DynamicDataReader;\n') - outfile.write('}\n') - outfile.write('\n') - outfile.write('namespace DTC\n') - outfile.write('{\n') - outfile.write(' template class GenericDtc;\n') - outfile.write(' template class GenericDtcImpl;\n') - outfile.write(' template class GenericDtcSampleInfo;\n') - outfile.write(' template class GenericDtcImplSampleInfo;\n') - outfile.write('\n') - - for struct_name in sorted(parser.struct_names): - - topic_name = "%s::%s" % ( parser.module_name, struct_name ) - dro_name = "Dro%s" % ( struct_name ) - dro_name_cf = "Dro%sCF" % ( struct_name ) - - outfile.write(' // Topic %s\n' % ( topic_name )) - outfile.write(' using %s = DDSGeneric::GenericDataReader<%s>;\n' % ( dro_name, topic_name )) - outfile.write(' using %s = GenericDtc<%s>;\n' % ( struct_name, dro_name )) - outfile.write(' using %sImpl = GenericDtcImpl<%s>;\n' % ( struct_name, dro_name )) - - outfile.write(' using %s = DDSGeneric::GenericDataReaderContentFilter<%s>;\n' % ( dro_name_cf, topic_name )) - outfile.write(' using %sCF = GenericDtc<%s>;\n' % ( struct_name, dro_name_cf )) - outfile.write(' using %sCFImpl = GenericDtcImpl<%s>;\n' % ( struct_name, dro_name_cf )) - - outfile.write(' namespace SampleInfo {\n') - outfile.write(' using %s = GenericDtcSampleInfo<%s>;\n' % ( struct_name, dro_name )) - outfile.write(' using %sImpl = GenericDtcImplSampleInfo<%s>;\n' % ( struct_name, dro_name )) - outfile.write(' }\n') - outfile.write('\n') - - outfile.write('} // namespace DTC\n') - outfile.write('\n') - -def printFtTopicTypeNames( outfile, ft_data ): - outfile.write(' template<> struct TopicNames<%s>' % ft_data.name) - outfile.write(' {\n') - outfile.write(' static constexpr const char *functionTopicName() { return "%s"; }\n' % ft_data.name) - outfile.write(' static constexpr const char *replyTopicName() { return "%s"; }\n' % ft_data.getReplyTopicName()) - outfile.write(' static constexpr const char *replyTopicTypeName() { return "%s"; }\n' % ft_data.getReplyTopicNameType( True )) - outfile.write(' static constexpr const char *requestTopicName() { return "%s"; }\n' % ft_data.getRequestTopicName()) - outfile.write(' static constexpr const char *requestTopicTypeName() { return "%s"; }\n' % ft_data.getRequestTopicNameType( True )) - outfile.write(' };\n') - -def printFtTypedefs( outfile, ft_data ): - if ft_data.needRequestTopicTypedef(): - outfile.write(' using %s = %s;\n' % ( ft_data.getRequestTopicNameType( False ), ft_data.getRequestTopicType() )) - if ft_data.needReplyTopicTypedef(): - outfile.write(' using %s = %s;\n' % ( ft_data.getReplyTopicNameType( False ), ft_data.getReplyTopicType() )) - -class FtsGenerator(object): - def generate( self, outfile, parser): - outputHeader( outfile, parser, 'FTS' ) - outfile.write('\n') - outfile.write('namespace FTS\n') - outfile.write('{\n') - outfile.write('template class GenericFtsImpl;\n') - outfile.write('template struct TopicNames;\n') - outfile.write('}\n') - outfile.write('namespace %s\n' % ( parser.module_name )) - outfile.write('{\n') - outfile.write(' // TypeDefinitions for Replies that are not implemented => using Core::FunctionReply\n') - for ft_name in sorted(parser.ft_enums): - printFtTypedefs( outfile, parser.ft_data[ft_name] ) - - outfile.write('} // namespace %s\n' % ( parser.module_name )) - outfile.write('\n') - - outfile.write('\n') - outfile.write('namespace FTS\n') - outfile.write('{\n') - outfile.write(' // TypeDefinitions for GenericFtsImpl\n') - for ft_name in sorted(parser.ft_enums): - ft_data = parser.ft_data[ft_name] - - dummy = '%sTemplateHelperDummy' % ft_data.name; - outfile.write(' class %s;\n' % dummy) - outfile.write(' using %s = GenericFtsImpl<%s, %s, %s>;\n' % (ft_data.name, ft_data.getRequestTopicType(), ft_data.getReplyTopicNameType(), dummy)) - printFtTopicTypeNames( outfile, ft_data ) - outfile.write('\n') - - outfile.write('} // namespace FTS\n') - outfile.write('\n') - -class FtcGenerator(object): - def generate( self, outfile, parser ): - outputHeader( outfile, parser, 'FTC' ) - outfile.write('\n') - outfile.write('namespace FTC\n') - outfile.write('{\n') - outfile.write('template class GenericFtcImpl;\n') - outfile.write('template struct TopicNames;\n') - outfile.write('}\n') - outfile.write('namespace %s\n' % ( parser.module_name )) - outfile.write('{\n') - outfile.write(' // TypeDefinitions for Replies that are not implemented => using Core::FunctionReply\n') - for ft_name in sorted(parser.ft_enums): - printFtTypedefs( outfile, parser.ft_data[ft_name] ) - - outfile.write('} // namespace %s\n' % ( parser.module_name )) - outfile.write('\n') - - outfile.write('\n') - outfile.write('namespace FTC\n') - outfile.write('{\n') - outfile.write(' // TypeDefinitions for GenericFtcImpl\n') - for ft_name in sorted(parser.ft_enums): - ft_data = parser.ft_data[ft_name] - - dummy = '%sTemplateHelperDummy' % ft_data.name; - outfile.write(' class %s;\n' % dummy) - outfile.write(' using %s = GenericFtcImpl<%s, %s, %s>;\n' % (ft_data.name, ft_data.getRequestTopicType(), ft_data.getReplyTopicNameType(), dummy)) - printFtTopicTypeNames( outfile, ft_data ) - outfile.write('\n') - - outfile.write('} // namespace FTC\n') - outfile.write('\n') - -class OperatorRights(object): - def generate( self, outfile, parser ): - outputHeader( outfile, parser, 'Operator Rights', False ) - outfile.write('namespace %s \n' % parser.module_name) - outfile.write('{\n') - outfile.write(' static const int Offset = %s; \n' % parser.op_right_offset) - outfile.write('}\n\n') - - outfile.write('namespace OperatorRights\n') - outfile.write('{\n') - globalOffset = parser.op_right_offset << 24 - for r in parser.op_rights: - outfile.write('enum %s\n' % r.name) - outfile.write('{\n') - offsetPlugin = globalOffset + (r.offset << 16) - groupCount = -1 - for group in r.groups: - groupCount = groupCount + 1 - groupOffset = offsetPlugin + (groupCount << 8) - for k,v,d in group.values: - v += groupOffset - outfile.write(' %s = %s,\n' % (k, hex(v))) - outfile.write('};\n') - - outfile.write('\n') - outfile.write('enum Groups_%s\n' % parser.module_name) - outfile.write('{\n') - pattern = re.compile('[\W]+') - for r in parser.op_rights: - offsetPlugin = globalOffset + (r.offset << 16) - for group in r.groups: - groupOffset = offsetPlugin + (groupCount << 8) - name = pattern.sub('', group.name) - outfile.write(' Group%s = %s,\n' % (name, hex(groupOffset))) - outfile.write('};\n') - - outfile.write('\n') - outfile.write('enum Plugins_%s\n' % parser.module_name) - outfile.write('{\n') - for r in parser.op_rights: - offsetPlugin = globalOffset + (r.offset << 16) - name = pattern.sub('', r.name) - outfile.write(' Plugin%s = %s,\n' % (name, hex(offsetPlugin))) - outfile.write('};\n') - - outfile.write('} // namespace OperatorRights\n') - -class OperatorRightsCpp(object): - def generate( self, outfile, parser ): - outputHeader( outfile, parser, 'Operator Rights (source)', False, False ) - outfile.write('#include "%s_operator_rights.h"\n' % parser.service_abbr) - outfile.write('#include \n\n\n') - if len(parser.op_rights) == 0: - return - outfile.write('\nvoid doNothing()\n{\n const char * txt[] {\n') - - disamb = 'OperatorRights' - globalOffset = parser.op_right_offset << 24 - for r in parser.op_rights: - offsetPlugin = globalOffset + (r.offset << 16) - groupCount = -1 - for group in r.groups: - groupCount = groupCount + 1 - groupOffset = offsetPlugin + (groupCount << 8) - if group.name != '': - outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, group.name)) - if group.desc != '': - outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, group.desc)) - for aclName, aclId, aclDescr in group.values: - outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, aclDescr)) - outfile.write(' };\n Q_UNUSED(txt);\n}\n') - -class OperatorRightsSql(object): - def generate( self, outfile, parser ): - outfile.write('BEGIN;\n') - globalOffset = parser.op_right_offset << 24 - if globalOffset > 0: - outfile.write('--- Offset: %s = %s\n' % (globalOffset, hex(globalOffset))) - - groupId = globalOffset; - - for r in parser.op_rights: - offsetPlugin = globalOffset + (r.offset << 16) - groupCount = -1 - for group in r.groups: - groupCount = groupCount + 1 - groupOffset = offsetPlugin + (groupCount << 8) - - outfile.write('\n--- %s - %s = %s\n' % (group.name, hex(groupOffset), groupOffset)) - str = "INSERT INTO opm.opm_group (id, name, description, type_id, is_hidden) VALUES (%s, '%s', '%s', 4, false);\n" - outfile.write(str % (groupOffset, group.name, group.desc)) - for aclName, aclId, aclDescr in group.values: - aclId += groupOffset - outfile.write('--- Base: %s, %s - %s = %s\n' % (groupOffset, aclName, aclId, hex(aclId))) - str = "INSERT INTO opm.opm_access_control_level (id, name, description) VALUES (%s, '%s', '%s');\n"; - outfile.write(str % (aclId, aclName, aclDescr)); - str = "INSERT INTO opm.opm_group (id, name, description, type_id, is_hidden) VALUES (%s, '%s', '%s', 1, true);\n"; - outfile.write(str % (groupId, aclName, aclDescr)); - str = "INSERT INTO opm.opm_acl_allow (group_id, acl_id) VALUES (%s, %s); -- group_id is: %s = %s\n"; - outfile.write(str % (groupId, aclId, groupId, hex(groupId))); - str = "INSERT INTO opm.opm_group_group (group_id, is_member_of_group_id) VALUES (%s, %s); -- %s\n" - outfile.write(str % (groupOffset, groupId, group.name)) - groupId = groupId + 1 - outfile.write('\n') - outfile.write('END;\n') - -def generate( parser, generator, out_filename ): - try: - outfile = open(out_filename, 'w') - except: - print("Could not open %s for writing" % out_filename, file=sys.stderr) - sys.exit(4) - generator.generate(outfile, parser) - outfile.close() - -def usage( error ): - print(error, file=sys.stderr) - print("Usage: %s DTS|DTC|FTS|FTC|SQL " % sys.argv[0], file=sys.stderr) - print(" %s ALL " % sys.argv[0], file=sys.stderr) - print("i.e.: %s DTS exm_service.xml DtsTypeDefinitions.h" % sys.argv[0], file=sys.stderr) - print("call: '%s'" % " ".join(sys.argv), file=sys.stderr) - sys.exit(1) - -def main(): - if len(sys.argv) != 4: - usage("Invalid argument count") - - option_str = sys.argv[1] - xml_filename = sys.argv[2] - - parser = FileParser() - parser.build(xml_filename) - - if option_str == "DTS": - generate(parser, DtsGenerator(), sys.argv[3]) - elif option_str == "DTC": - generate(parser, DtcGenerator(), sys.argv[3]) - elif option_str == "FTS": - generate(parser, FtsGenerator(), sys.argv[3]) - elif option_str == "FTC": - generate(parser, FtcGenerator(), sys.argv[3]) - elif option_str == "SQL": - out_dir = sys.argv[3] - generate(parser, OperatorRightsSql(), os.path.join(out_dir, parser.service_abbr + '_operator_rights.sql')) - elif option_str == "ALL": - out_dir = sys.argv[3] - fn = parser.service_abbr + '_%s_types.h' - generate(parser, DtsGenerator(), fn % 'dts') - generate(parser, DtcGenerator(), fn % 'dtc') - generate(parser, FtsGenerator(), fn % 'fts') - generate(parser, FtcGenerator(), fn % 'ftc') - generate(parser, OperatorRights(), parser.service_abbr + '_operator_rights.h') - generate(parser, OperatorRightsCpp(), parser.service_abbr + '_operator_rights.cpp') - else: - usage("invalid option '%s'" % option_str) - sys.exit(0) - -if __name__ == "__main__": - main() diff --git a/Lts/Common/CodeGen/ea_export_script.txt b/Lts/Common/CodeGen/ea_export_script.txt deleted file mode 100755 index 2700b3ec5..000000000 --- a/Lts/Common/CodeGen/ea_export_script.txt +++ /dev/null @@ -1,1465 +0,0 @@ -// -// see http://www.sparxsystems.com/enterprise_architect_user_guide/12.0/automation_and_scripting/reference.html -// -// $Id$ -// $HeadURL$ -// $Author$ -// -// -> tabs to spaces -// sed -i "s/\t/ /g" ea_export_script.txt -// -> remove trailing spaces -// sed -i "s/[ ]*$//g" ea_export_script.txt -// - -!INC Local Scripts.EAConstants-JScript -!INC EAScriptLib.JScript-Database -!INC EAScriptLib.JScript-XML -!INC EAScriptLib.JScript-TaggedValue - -function warnOut( msg ) -{ - Session.Output( "WARN: " + msg ); -} - -function infoOut( msg ) -{ - Session.Output( "INFO: " + msg ); -} - -function TVGetElementTaggedValueEx( theElement /* : EA.Element */, taggedValueName /* : String */, defaultValue /* : String */ ) /* : variant */ -{ - var result = defaultValue; - - if ( theElement != null && taggedValueName.length > 0 ) - { - for ( var j = 0; j < theElement.TaggedValuesEx.Count; j++ ) - { - var taggedValue as EA.TaggedValue; - taggedValue = theElement.TaggedValuesEx.GetAt(j); - if (taggedValue.Name == taggedValueName) - { - result = taggedValue.Value; - break; - } - } - } - - return result; -} - - -function getFrameworkPackage( element_ ) -{ - if ( element_ == null ) - return null; - var element as EA.Element; - element = element_; - if ( element.Stereotype == "framework" ) - return element; - - var elementId = element.PackageID; - if ( element.ObjectType == otPackage ) - { - var package as EA.Package; - package = element; - var packageElement as EA.Element; - packageElement = package.Element; - if ( packageElement == null ) - return null; - if ( packageElement.Stereotype == "framework" ) - return package; - elementId = element.ParentID; - } - //Session.Output( "Name: " + element.Name + " PackageID: " + elementId ); - element = Repository.GetPackageByID( elementId ); - - return getFrameworkPackage( element ) -} - -function getFrameworkElement( element_ ) -{ - var element as EA.Element; - element = element_; - if ( element.Stereotype == "framework" ) - return element; - var package as EA.Package; - package = getFrameworkPackage( element_ ); - if ( package == null ) - return null; - return package.Element; -} - -function getConnectorFromElement( _element, connectorType ) -{ - var element as EA.Element; - element = _element; - - if ( element == null ) - return null; - - for( var j = 0; j < element.Connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = element.Connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.ClientID != element.ElementID ) - continue; - if( connector.Type != connectorType ) - continue; - return connector; - } - return null; -} - - -function getMethodFromElement( _element, methodString ) -{ - var element as EA.Element; - element = _element; - - if( element == null || methodString == null ) - return null; - - var methods as EA.Collection; - methods = element.MethodsEx; - - return getCollectionAttribute(methods, methodString); -} - -function getCollectionAttribute(_collections, methodName) -{ - var collections as EA.Collection; - - //Session.Output( "Find methodName:" + methodName ); - collections = _collections; - // method.GetByName() does not work on non-existing attributes - for( var i = 0; i < collections.Count; ++i ) - { - var method as EA.Method; - method = collections.GetAt( i ); - //Session.Output( "MethodName: " + method.Name ); - if( method && method.Name == methodName ) - return method; - } - return null; -} - -function getKeyColumnFromMethod( element, methodString ) -{ - var method as EA.Method; - method = getMethodFromElement( element, methodString ); - if( method == null ) - { - Session.Output( "Can not find PK/FK '" + methodString + "' in Table " + element.Name ); - return null; - } - if( method.Parameters.Count != 1 ) - return null; - - return method.Parameters.GetAt(0).Name; -} - -function getTaggedValueGroup( element, groupName ) -{ - var groupsTaggedValue = TVGetElementTaggedValue( element, groupName ); - if ( groupsTaggedValue != null ) - { - tmp1 = DBGetFieldValueString( "Notes", "t_propertytypes", "Property = \'" + groupName + "\' " ); - - var startIndex = tmp1.lastIndexOf( "=" ) + 1; - var endIndex = tmp1.length - 1; - - var groupNames = tmp1.substring(startIndex, endIndex).split( "," ); - - var groupVals = groupsTaggedValue.split( "," ); - var validGroups = ""; - for ( var i = 0; i < groupVals.length; i++ ) - { - if ( groupVals[i] == 1 ) - { - if ( validGroups.length > 0 ) - { - validGroups += ","; - } - validGroups += groupNames[i]; - } - } - if ( validGroups.length > 0 ) - { - return validGroups; - } - } - return null; -} - -function exportTaggedValueGroup( xml, xmlAttribName, element, groupName ) -{ - var validGroups = getTaggedValueGroup( element, groupName ); - if ( validGroups != null ) - { - //Session.Output('valid groups: ' + validGroups ); - xml.setAttribute( xmlAttribName, validGroups ); - } -} - -function exportFunctionDeclaration( xmlDoc, xmlParent, _method ) -{ - var method as EA.Method; - method = _method; - - var methodXml = xmlDoc.createNode( 1, method.Stereotype, '' ); - xmlParent.appendChild( methodXml ); - methodXml.setAttribute( 'name', method.Name ); - methodXml.setAttribute( 'returns', method.ReturnType ); - if (method.IsConst) - methodXml.setAttribute( 'isConst', 1 ); - for( var k = 0; k < method.Parameters.Count; k++ ) - { - var param as EA.Parameter; - param = method.Parameters.GetAt( k ); - var paramXml = xmlDoc.createNode( 1, 'parameter', '' ); - paramXml.setAttribute( 'pos', k + 1 ); - paramXml.setAttribute( 'name', param.Name ); - paramXml.setAttribute( 'type', param.Type ); - if (param.Default != "") - paramXml.setAttribute( 'default', param.Default ); - methodXml.appendChild( paramXml ); - } - return methodXml; -} - -function exportDbStructBase( xmlDoc, dbStructsXml, dbStructXml, connector, exportedTopicTypes ) -{ - // only inheritance - if( connector.Type != "Generalization" ) - return; - - var base as EA.Element; - base = GetElementByID( connector.SupplierID ); - if( base == null ) - { - Session.Output( "got unknown element id " + connector.SupplierID ); - return; - } - - // now generate the base='foo' - part for inheritance - dbStructXml.setAttribute( 'base', base.Name ); - - // check if the base struct is already exported - for( var i = 0; i < exportedTopicTypes.length; ++i ) - { - if( exportedTopicTypes[i] == connector.SupplierID ) - return; - } - exportedTopicTypes.push( connector.SupplierID ); - - // not yet exported -> export - var dbStructXml = xmlDoc.createNode( 1, 'table', '' ); - dbStructsXml.appendChild( dbStructXml ); - dbStructXml.setAttribute( 'name', base.Name ); - - var baseConnectors as EA.Collection; - baseConnectors = base.Connectors; - for( var j = 0; j < baseConnectors.Count; j++ ) - { - var connector as EA.Connector; - baseConnector = baseConnectors.GetAt( j ); - if( baseConnector == null ) - continue; - if( baseConnector.ClientID != base.ElementID ) - continue; - - // check if base of base is already exported - exportDbStructBase( xmlDoc, dbStructsXml, dbStructXml, baseConnector, exportedTopicTypes ); - } -} - -function exportDDSBase( xmlDoc, topicTypesXml, topicTypeXml, connector, exportedTopicTypes ) -{ - // only inheritance - if( connector == null || connector.Type != "Generalization" ) - return; - - var supplier as EA.Element; - supplier = GetElementByID( connector.SupplierID ); - if( supplier == null ) - { - Session.Output( "got unknown element id " + connector.SupplierID ); - return; - } - - var supplierPackage as EA.Package; - var clientPackage as EA.Package; - supplierPackage = getFrameworkElement( supplier ); - clientPackage = getFrameworkElement( GetElementByID( connector.ClientID ) ); - - // now generate the base='foo' - part for inheritance - if ( supplierPackage.Name != clientPackage.Name ) - { - topicTypeXml.setAttribute( 'baseValueType', supplierPackage.Name + "::" + supplier.Name ); - } else { - topicTypeXml.setAttribute( 'baseValueType', supplier.Name ); - } - - if ( supplier.Attributes.Count == 0 ) - { - // special case - an empty idl struct - modelled for consistency, - // but rtiddsgen does not like it and it's also useless in codebase - var grandparentConnector = getConnectorFromElement( supplier, "Generalization" ) - exportDDSBase( xmlDoc, topicTypesXml, topicTypeXml, grandparentConnector, exportedTopicTypes ) - } - exportedTopicTypes.push( connector.SupplierID ); -} - - - -function getDBServiceStructs( _package, xmlDoc, xmlRootNode ) -{ - var dbServiceStructsXml = xmlDoc.createNode( 1, 'dbServiceStructs', '' ); - xmlRootNode.appendChild( dbServiceStructsXml ); - - var package as EA.Package; - package = _package; - var elements as EA.Collection; - elements = package.Elements; - - var dbstructs = [] - - if( elements.Count == 0 ) { - infoOut("'Database Service Structs' is empty."); - } - for( var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - element = elements.GetAt( i ); - if( element.Stereotype == "implementationClass" ) - { - var dbClassXml = xmlDoc.createNode( 1, 'serviceStruct', '' ); - dbServiceStructsXml.appendChild( dbClassXml ); - dbClassXml.setAttribute( 'name', element.Name ); - var connectors as EA.Collection; - connectors = element.Connectors; - - var correspondingTableFound = false; - for( var j = 0; j < connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = connectors.GetAt( j ); - if( connector == null ) - continue; - - if( connector.Type != "Dependency" && connector.Type != "InformationFlow" ) - continue; - - var sourceId = (( connector.Type == "Dependency") ? connector.ClientID : connector.SupplierID ); - var destId = (( connector.Type == "Dependency") ? connector.SupplierID : connector.ClientID ); - - if( sourceId != element.ElementID ) - continue; - - var supplier as EA.Element; - supplier = GetElementByID( destId ); - if( supplier == null ) - { - Session.Output( "got unknown element id " + destId ); - continue; - } - - dbClassXml.setAttribute( 'table', supplier.Name ); - correspondingTableFound = true; - - dbstructs.push( destId ); - - if( connector.Type == "InformationFlow" ) - { - dbClassXml.setAttribute( 'static', 1 ); - } - } - if ( !correspondingTableFound ) - { - warnOut( "Found no corresponding table for Database Service Struct '" + element.Name + "'" ); - } - var methods as EA.Collection; - methods = element.MethodsEx; - for( var j = 0; j < methods.Count; j++ ) - { - var method as EA.Method; - method = methods.GetAt( j ); - if( method == null ) - continue; - if( method.Stereotype == 'function' || method.Stereotype == 'notification' ) - { - var methodXml = exportFunctionDeclaration( xmlDoc, dbClassXml, method ); - if( method.Stereotype == 'notification' ) - { - exportTaggedValueGroup( methodXml, 'notificationTypes', method, 'Legatus::DatabaseNotificationType' ); - } - } - } - } - } - - var exportedTopicTypes = []; - for( var i = 0; i < dbstructs.length; i++ ) - { - var element as EA.Element; - element = GetElementByID( dbstructs[i] ); - - if( element == null ) - continue; - - var dbStructXml = xmlDoc.createNode( 1, 'table', '' ); - dbServiceStructsXml.appendChild( dbStructXml ); - dbStructXml.setAttribute( 'name', element.Name ); - - exportedTopicTypes.push( element.ElementID ); - - var connectors as EA.Collection; - connectors = element.Connectors; - - for( var j = 0; j < connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.ClientID != element.ElementID ) - continue; - - exportDbStructBase( xmlDoc, dbServiceStructsXml, dbStructXml, connector, exportedTopicTypes ); - } - } -} - -function exportReferentialIntegrity( _methods, srcFk, xmlOut) -{ - var methods as EA.Collection; - methods = _methods; - - var method as EA.Method; - method = null; - for (m = 0; m < methods.Count(); ++m) - { - method = methods.GetAt(m); - if (method.Name == srcFk) - break; - method = null; - } - if (method == null) - return; - - for (var t = 0; t < method.TaggedValues.Count(); ++t) - { - var methodTag as EA.MethodTag; - methodTag = method.TaggedValues.GetAt(t); - if (methodTag.Name != 'property') - continue; - if (methodTag.Value == 'Delete Set Null=1;') - xmlOut.setAttribute( 'referential_integrity', 'DeleteSetNull') - else if (methodTag.Value == 'Delete Restrict=1;') - xmlOut.setAttribute( 'referential_integrity', 'DeleteRestrict') - else if (methodTag.Value == 'Delete Cascade=1;') - xmlOut.setAttribute( 'referential_integrity', 'DeleteCascade') - else if (methodTag.Value == 'Delete No Action=1;') - xmlOut.setAttribute( 'referential_integrity', 'DeleteNoAction') - else if (methodTag.Value == 'Delete Set Default=1;') - xmlOut.setAttribute( 'referential_integrity', 'DeleteSetDefault') - break; - } - -} -function getDBModel( _package, xmlDoc, xmlRootNode ) -{ - var tablesXml = xmlDoc.createNode( 1, 'tables', '' ); - xmlRootNode.appendChild( tablesXml ); - - var package as EA.Package; - package = _package; - var elements as EA.Collection; - elements = package.Elements; - - if( elements.Count == 0 ) { - infoOut("'Database Model' is empty."); - } - - for( var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - element = elements.GetAt( i ); - if( element.Stereotype == "table" ) - { - var tableXml = xmlDoc.createNode( 1, 'table', '' ); - tablesXml.appendChild( tableXml ); - tableXml.setAttribute( 'name', element.Name ); - if( element.Notes != "" ) - { - tableXml.setAttribute( 'comment', element.Notes ); - } - exportTaggedValueGroup( tableXml, 'exerciseDependency', element, 'Legatus::ExerciseDependency' ); - - var attributes as EA.Collection; - attributes = element.Attributes; - for( var j = 0; j < attributes.Count; j++ ) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( j ); - if( attribute == null ) - continue; - - var columnXml = xmlDoc.createNode( 1, 'column', '' ); - tableXml.appendChild( columnXml ); - - columnXml.setAttribute( 'name', attribute.Name ); - columnXml.setAttribute( 'datatype', attribute.Type ); - - if( attribute.Default != "" ) - if (attribute.Default == "Optional" | attribute.Default == "optional") - columnXml.setAttribute( 'isOptional', 1 ); - else - columnXml.setAttribute( 'default', attribute.Default ); - if( attribute.IsOrdered ) - columnXml.setAttribute( 'isPrimaryKey', 1 ); - if( attribute.IsCollection ) - columnXml.setAttribute( 'isForeignKey', 1 ); - - var attrTag as EA.AttributeTag; - attrTag = attribute.TaggedValues.GetByName( "AutoNum" ); - if( attrTag != null && attrTag.Value == "True" ) - { - var sequenceXml = xmlDoc.createNode( 1, 'sequence', '' ); - columnXml.appendChild( sequenceXml ); - - attrTag = attribute.TaggedValues.GetByName( "StartNum" ); - sequenceXml.setAttribute( 'start', (( attrTag == null ) ? "1" : attrTag.Value ) ); - - attrTag = attribute.TaggedValues.GetByName( "Increment" ); - sequenceXml.setAttribute( 'increment', (( attrTag == null ) ? "1" : attrTag.Value ) ); - } - if ( attrTag == null && attribute.IsOrdered ) - { - warnOut( 'Column ' + element.Name + '.' + attribute.Name + ' is a primary key, but has no sequence (AutoNum) value set!') - } - - if( attribute.Notes != "" ) - { - columnXml.setAttribute( 'comment', attribute.Notes ); - } - } - - var methods as EA.Collection; - methods = element.MethodsEx; - for( var j = 0; j < methods.Count; j++ ) - { - var method as EA.Method; - method = methods.GetAt( j ); - if( method == null ) - continue; - if( method.Stereotype == 'index' || method.Stereotype == 'unique' ) - { - var methodXml = xmlDoc.createNode( 1, 'constraint', '' ); - tableXml.appendChild( methodXml ); - methodXml.setAttribute( 'name', method.Name ); - methodXml.setAttribute( 'type', method.Stereotype ); - for( var k = 0; k < method.Parameters.Count; k++ ) - { - var param as EA.Parameter; - param = method.Parameters.GetAt( k ); - var paramXml = xmlDoc.createNode( 1, 'column', '' ); - paramXml.setAttribute( 'name', param.Name ); - methodXml.appendChild( paramXml ); - } - } - } - - var connectors as EA.Collection; - connectors = element.Connectors; - for( var j = 0; j < connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.ClientID != element.ElementID ) - continue; - if( connector.Direction != "Source -> Destination" ) - continue; - if( connector.Type != "Association" ) - continue; - - var supplier as EA.Element; - supplier = GetElementByID( connector.SupplierID ); - if( supplier == null ) - { - Session.Output( "got unknown element id " + connector.SupplierID ); - continue; - } - - // FKINFO=SRC=FK_pdm_equipment_system_event_filter_pdm_hgr_mine_search_filter_type:DST=PK_pdm_hgr_mine_search_filter_type:; - s = connector.StyleEx; - srcFk = s.split( "SRC=" )[1].split(":")[0]; - dstFk = s.split( "DST=" )[1].split(":")[0]; - // Session.Output("srcFk: " + srcFk); - // Session.Output("dstFk: " + dstFk); - - var foreignKeyXml = xmlDoc.createNode( 1, 'foreignKey', '' ); - tableXml.appendChild( foreignKeyXml ); - foreignKeyXml.setAttribute( 'name', srcFk ) - - // Default: onDeleteCascade = true - exportReferentialIntegrity(methods, srcFk, foreignKeyXml); - - var sourceInfo = xmlDoc.createNode( 1, 'source', '' ) - //Session.Output( "Source: " + sourceInfo ); - foreignKeyXml.appendChild( sourceInfo ); - sourceInfo.setAttribute( 'table', element.Name ) - //Session.Output( "table: " + element.Name ); - sourceInfo.setAttribute( 'column', getKeyColumnFromMethod(element, srcFk) ) - - var destInfo = xmlDoc.createNode( 1, 'destination', '' ) - foreignKeyXml.appendChild( destInfo ); - exportExternalFk(xmlDoc, destInfo, element, supplier); - destInfo.setAttribute( 'table', supplier.Name ) - destInfo.setAttribute( 'column', getKeyColumnFromMethod(supplier, dstFk) ) - //Session.Output( "Destination: " + destInfo ); - //Session.Output( "table: " + supplier.Name ); - - } - } - } -} - -function getDBViews( _package, xmlDoc, xmlRootNode ) -{ - var viewsXml = xmlDoc.createNode( 1, 'views', '' ); - xmlRootNode.appendChild( viewsXml ); - - var package as EA.Package; - package = _package; - var elements as EA.Collection; - elements = package.Elements; - - for( var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - element = elements.GetAt( i ); - if( element.Stereotype == "view" ) - { - var viewXml = xmlDoc.createNode( 1, 'view', '' ); - viewsXml.appendChild( viewXml ); - viewXml.setAttribute( 'name', element.Name ); - if( element.Notes != "" ) - { - viewXml.setAttribute( 'comment', element.Notes ); - } - exportTaggedValueGroup( viewXml, 'exerciseDependency', element, 'Legatus::ExerciseDependency' ); - - var attributes as EA.Collection; - attributes = element.Attributes; - for( var j = 0; j < attributes.Count; j++ ) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( j ); - if( attribute == null ) - continue; - if( attribute.Stereotype != "filter") - continue; - viewXml.setAttribute( "filter", attribute.Name ) - } - - var connectors as EA.Collection; - connectors = element.Connectors; - for( var j = 0; j < connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.ClientID != element.ElementID ) - continue; - if( connector.Direction != "Source -> Destination" ) - continue; - if( connector.Type != "Dependency" ) - continue; - - var supplier as EA.Element; - supplier = GetElementByID( connector.SupplierID ); - if( supplier == null ) - { - Session.Output( "got unknown element id " + connector.SupplierID ); - continue; - } - viewXml.setAttribute( "base", supplier.Name ) - } - } - } -} - -function getEnumerations( _package, xmlDoc, xmlRootNode ) -{ - var rootXml = xmlDoc.createNode( 1, 'enumerations', '' ); - xmlRootNode.appendChild( rootXml ); - - var package as EA.Package; - package = _package; - var elements as EA.Collection; - elements = package.Elements; - - if( elements.Count == 0 ) { - warnOut("'Enumerations' is empty. At least one placeholder enum is needed for generating C++ code."); - } - - for( var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - element = elements.GetAt( i ); - //Session.Output("Enum element: " + element.Name); - if( element.Stereotype == "enumeration" ) - { - var elementXml = xmlDoc.createNode( 1, 'enumeration', '' ); - rootXml.appendChild( elementXml ); - elementXml.setAttribute( 'name', element.Name ); - - var attributes as EA.Collection; - attributes = element.AttributesEx; - for( var j = 0; j < attributes.Count; j++ ) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( j ); - if( attribute == null ) - continue; - - var attr = xmlDoc.createNode( 1, 'attribute', '' ); - elementXml.appendChild( attr ); - - attr.setAttribute( 'key', attribute.Name ); - attr.setAttribute( 'value', attribute.Default ); - } - - var tableCount = 0; - var connectors as EA.Collection; - connectors = element.Connectors; - for( var j = 0; j < connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.ClientID != element.ElementID ) - continue; - if( connector.Direction != "Source -> Destination" ) - continue; - if( connector.Type != "Dependency" ) - continue; - var supplier as EA.Element; - supplier = GetElementByID( connector.SupplierID ); - if( supplier == null ) - { - Session.Output( "got unknown element id " + connector.SupplierID ); - continue; - } - if( supplier.Stereotype != "table" ) - continue; - - elementXml.setAttribute( 'table', supplier.Name ); - tableCount += 1; - } - if ( tableCount < 1 ) - { - Session.Output( "Got no information about associated database table for enumeration " + element.Name + " - not creating database entries for this enum" ); - } - else if ( tableCount > 1 ) - { - Session.Output( "Got more than one associated database table for enumeration " + element.Name + "!" ); - } - } - } -} - -function exportDtcCustomFunctions( xmlDoc, xmlParent, _element ) -{ - var element as EA.Element; - element = _element; - - var dtcFunctionsXml = xmlDoc.createNode( 1, 'customDtcFunctions', '' ); - xmlParent.appendChild( dtcFunctionsXml ); - - var dtcFunctionXml = xmlDoc.createNode( 1, 'customDtcFunction', '' ); - dtcFunctionsXml.appendChild( dtcFunctionXml ); - - var methods as EA.Collection; - methods = element.MethodsEx; - for( var j = 0; j < methods.Count; j++ ) - { - var method as EA.Method; - method = methods.GetAt( j ); - if( method == null ) - continue; - if( method.Stereotype == 'function' ) - { - // export DTC functions - exportFunctionDeclaration( xmlDoc, dtcFunctionXml, method ); - } else { - warnOut('Found method ' + method.Name + ' with unhandled Sterotype "' + method.Stereotype + '"'); - } - } -} - -function exportIdlFunctionTopic( xmlDoc, functionTopicsXml, _element ) -{ - var element as EA.Element; - element = _element; - - // we need two connectors: - // - Reply ( FunctionTopic -> idlStruct ) - // - Request ( idlStruct -> FunctionTopic ) - var replyConnector as EA.Connector; - var requestConnector as EA.Connector; - var replyElement as EA.Element; - var requestElement as EA.Element; - - for( var j = 0; j < element.Connectors.Count; j++ ) - { - var connector as EA.Connector; - connector = element.Connectors.GetAt( j ); - if( connector == null ) - continue; - if( connector.Type != "Dependency" ) - continue; - // Reply ( FunctionTopic -> idlStruct ) - if( connector.ClientID == element.ElementID ) - { - replyConnector = connector; - replyElement = GetElementByID( connector.SupplierID ) - } - else if( connector.SupplierID == element.ElementID ) - { - requestConnector = connector; - requestElement = GetElementByID( connector.ClientID ) - } - } - - if ( requestElement == null ) - { - Session.Output( "Could not find request TopicType for FunctionTopic " + element.Name ); - return; - } - if ( requestElement == null ) - { - Session.Output( "Could not find reply TopicType for FunctionTopic " + element.Name ); - return; - } - // Session.Output( "RequestTopicType: " + requestElement.Name + ", ReplyTopicType: " + replyElement.Name ); - var functionTopicXml = xmlDoc.createNode( 1, 'functionTopic', '' ); - functionTopicsXml.appendChild( functionTopicXml ); - functionTopicXml.setAttribute( 'name', element.Name ); - functionTopicXml.setAttribute( 'requestTopicType', requestElement.Name ); - functionTopicXml.setAttribute( 'replyTopicType', replyElement.Name ); - functionTopicXml.setAttribute( 'requestTopicName', element.Name + "Request" ); - functionTopicXml.setAttribute( 'replyTopicName', element.Name + "Reply" ); - // def value = False - var routeInReplay = TVGetElementTaggedValue(element, "Legatus::RouteTopicInReplay"); - functionTopicXml.setAttribute( 'routeInReplay', routeInReplay == 'true' ? 1 : 0 ); - - var replyPackage as EA.Package; - var requestPackage as EA.Package; - var clientPackage as EA.Package; - requestPackage = getFrameworkElement( GetElementByID( requestConnector.ClientID ) ); - replyPackage = getFrameworkElement( GetElementByID( replyConnector.SupplierID ) ); - currentPackage = getFrameworkElement( element ); - - if ( requestPackage.Name != currentPackage.Name ) - { - functionTopicXml.setAttribute( 'requestTopicType', requestPackage.Name + "::" + requestElement.Name ); - } - if ( replyPackage.Name != currentPackage.Name ) - { - functionTopicXml.setAttribute( 'replyTopicType', replyPackage.Name + "::" + replyElement.Name ); - } - -} - -function exportExternalFk( xmlDoc, fkXml, source, target ) -{ - var sourcePackage as EA.Package; - sourcePackage = getFrameworkElement( source ); - - var targetPackage as EA.Package; - targetPackage = getFrameworkElement( target ); - - if (targetPackage.Name != sourcePackage.Name) - { - fkXml.setAttribute( 'service', targetPackage.Name ); - fkXml.setAttribute( 'serviceAbbr', targetPackage.Alias.toLowerCase() ); - } - - //Session.Output("sourcePackage: " + sourcePackage.Alias); - //Session.Output("targetPackage: " + targetPackage.Alias); - - // try to get out the exercise/global dependency state - var edTgtTable = getTaggedValueGroup( target, "Legatus::ExerciseDependency" ); - var edSrcTable = getTaggedValueGroup( source, "Legatus::ExerciseDependency" ); - var edTgtPackage = getTaggedValueGroup( targetPackage, "Legatus::ExerciseDependency" ); - var edSrcPackage = getTaggedValueGroup( sourcePackage, "Legatus::ExerciseDependency" ); - // the four properties above can be inconsistent - we assume the best ... - - var tgtFqnName = targetPackage.Name + '.' + target.Name; - var srcFqnName = sourcePackage.Name + '.' + source.Name; - // table always wins, if availble - if (edTgtTable && edTgtTable != '') - edTgtPackage = edTgtTable; - if (edSrcTable && edSrcTable != '') - edSrcPackage = edSrcTable; - var isTgtExerciseDependent = (edTgtPackage.indexOf('Exercise') >= 0); - var isTgtGlobal = (edTgtPackage.indexOf('Global') >= 0); - var isSrcExerciseDependent = (edSrcPackage.indexOf('Exercise') >= 0); - var isSrcGlobal = (edSrcPackage.indexOf('Global') >= 0); - // since global and exercise can both be active we can get into trouble here... - if ( !isTgtExerciseDependent && !isTgtGlobal) - { - warnOut('Legatus::ExerciseDependency property for ' + tgtFqnName + - ' is neither set for Framework nor Table - this is not allowed!'); - return; - } - if ( !isSrcExerciseDependent && !isSrcGlobal) - { - warnOut('Legatus::ExerciseDependency property for ' + srcFqnName + - ' is neither set for Framework nor Table - this is not allowed!'); - return; - } - //Session.Output('Src: ' + srcFqnName + ' Global: ' + isSrcGlobal + ', Exercise: ' + isSrcExerciseDependent); - //Session.Output('Tgt: ' + tgtFqnName + ' Global: ' + isTgtGlobal + ', Exercise: ' + isTgtExerciseDependent); - // both are exercise dependent - do not export schema (so sqlgenerator generates fk to local table) - if (isTgtExerciseDependent && isSrcExerciseDependent) - return; - // both are global - do not export schema (so sqlgenerator generates fk to local table) - // but source must not be exerciseDependent because then we need the fqn - // when src is exercise and tgt is global - if (isTgtGlobal && isSrcGlobal && !isSrcExerciseDependent) - { - if (targetPackage.Name != sourcePackage.Name) - fkXml.setAttribute( 'schema', targetPackage.Alias.toLowerCase() ); - return; - } - if (isSrcGlobal && isTgtExerciseDependent) - { - warnOut('Legatus::ExerciseDependency property for ' + srcFqnName + - ' is global but destination (' + tgtFqnName + - ' is exercise dependent - this is not allowed / impossible'); - return; - } - fkXml.setAttribute( 'schema', targetPackage.Alias.toLowerCase() ); -} - -function getDDSModel( _package, xmlDoc, xmlRootNode ) -{ - var topicTypesXml = xmlDoc.createNode( 1, 'topicTypes', '' ); - xmlRootNode.appendChild( topicTypesXml ); - var functionTopicsXml = xmlDoc.createNode( 1, 'functionTopics', '' ); - xmlRootNode.appendChild( functionTopicsXml ); - - var package as EA.Package; - package = _package; - var basePackage as EA.Package; - basePackage = getFrameworkElement( package ); - var exportedTopicTypes = []; - - if( package.Elements.Count == 0 && package.Packages.Count == 0 ) { - infoOut("'DDS Model' is empty."); - } - - exportedTopicTypes = exportDDSModelPackage(package, - basePackage, - xmlDoc, - xmlRootNode, - topicTypesXml, - functionTopicsXml, - exportedTopicTypes ); -} - -function exportDDSModelPackage( _package, basePackage, xmlDoc, xmlRootNode, topicTypesXml, functionTopicsXml, exportedTopicTypes ) -{ - var package as EA.Package; - package = _package; - - // export all elements in this package - var elements as EA.Collection; - elements = package.Elements; - for( var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - element = elements.GetAt( i ); - - //Session.Output("element.Stereotype = " + element.Stereotype + " Name = " + element.Name ); - if ( element.Stereotype == 'implementationClass' && element.Name == 'DTC') - { - exportDtcCustomFunctions( xmlDoc, xmlRootNode, element ); - } - else if ( element.Stereotype == 'FunctionTopic' ) - { - exportIdlFunctionTopic( xmlDoc, functionTopicsXml, element ); - } - else if ( element.Stereotype == 'idlStruct' || element.Stereotype == 'valueType' ) - { - // do not export empty structs (see exportDDSBase() ) - if ( element.Attributes.Count == 0 ) - { - infoOut('Not exporting dds struct ' + element.Name + ' - no attributes (not allowed by rtiddsgen)'); - continue; - } - - if (element.Stereotype == 'valueType') - { - warnOut('Stereotype "valueType" is deprecated, please replace with "idlStruct" in struct ' + element.Name); - } - - var topicTypeXml = xmlDoc.createNode( 1, 'topicType', '' ); - topicTypesXml.appendChild( topicTypeXml ); - topicTypeXml.setAttribute( 'name', element.Name ); - - var ddsExtensibility = TVGetElementTaggedValue(element, "IDL::Extensibility"); - if ( ddsExtensibility ) - topicTypeXml.setAttribute( 'extensibility', ddsExtensibility); - - var ddsIsDataObject = TVGetElementTaggedValueEx(element, "IDL::IsDataObject") == 'true'; - if ( ddsIsDataObject ) { - topicTypeXml.setAttribute( 'isDataObject', 1 ); - - // def value = True - var autosubscribe = TVGetElementTaggedValueEx(element, "Legatus::AutoSubscribe"); - topicTypeXml.setAttribute( 'autosubscribe', autosubscribe == 'false' ? 0 : 1 ); - - // def value = True - var routeToClient = TVGetElementTaggedValueEx(element, "Legatus::RouteTopicToClient"); - topicTypeXml.setAttribute( 'routeToClient', routeToClient == 'false' ? 0 : 1 ); - - // def value = False - var routeToServer = TVGetElementTaggedValueEx(element, "Legatus::RouteTopicToServer"); - topicTypeXml.setAttribute( 'routeToServer', routeToServer == 'true' ? 1 : 0 ); - - // def value = False - var routeInReplay = TVGetElementTaggedValueEx(element, "Legatus::RouteTopicInReplay"); - topicTypeXml.setAttribute( 'routeInReplay', routeInReplay == 'true' ? 1 : 0 ); - } - - var dtsAutogen = TVGetElementTaggedValue(element, "Legatus::AutogenDTS"); - if ( dtsAutogen == 'false' || dtsAutogen == 0 ) - topicTypeXml.setAttribute( 'autogenDTS', 0 ); - - var dtcAutogen = TVGetElementTaggedValue(element, "Legatus::AutogenDTC"); - if ( dtcAutogen == 'false' || dtcAutogen == 0 ) - topicTypeXml.setAttribute( 'autogenDTC', 0 ); - - // Attributes - var attributes as EA.Collection; - attributes = element.AttributesEx; // we need to investigate all, but only export our own - for( var j = 0; j < attributes.Count; j++ ) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( j ); - if( attribute == null ) - continue; - - if( ddsIsDataObject && attribute.Name == 'sampleTime' && attribute.Notes != 'no_record' ) - topicTypeXml.setAttribute( 'recordTopic', 1 ); - // only export our own attributes - isDerived does not work as expected - if( attribute.ParentID != element.ElementID ) - continue; - - var columnXml = xmlDoc.createNode( 1, 'column', '' ); - topicTypeXml.appendChild( columnXml ); - - columnXml.setAttribute( 'name', attribute.Name ); - columnXml.setAttribute( 'idlType', attribute.Type ); - //infoOut("attribute.Name: " + element.Name + ":" + attribute.Name); - if ( attribute.ClassifierID != 0 ) - { - var attributeTypePackage as EA.Package; - attributeTypePackage = getFrameworkElement( GetElementByID( attribute.ClassifierID ) ); - if ( attributeTypePackage != null && attributeTypePackage.Name != basePackage.Name ) - { - columnXml.setAttribute( 'idlType', attributeTypePackage.Name + "::" + attribute.Type ); - } - } - - //Session.Output("StereoType:" + attribute.Stereotype); - if( attribute.Stereotype == "key" ) - columnXml.setAttribute( 'isKey', 1 ); - if( attribute.Stereotype == "Optional" | attribute.Stereotype == "optional" ) - columnXml.setAttribute( 'isOptional', 1 ); - if ( attribute.Stereotype == "translatable" ) - columnXml.setAttribute( 'isTranslatable', 1); - - if( attribute.Notes != "" ) - columnXml.setAttribute( 'comment', attribute.Notes ); - } - - var supplierCon = getConnectorFromElement( element, "Generalization" ) - exportDDSBase( xmlDoc, topicTypesXml, topicTypeXml, supplierCon, exportedTopicTypes ); - } - } - // now handle subpackages - var packages as EA.Collection; - packages = package.Packages; - for( var i = 0; i < packages.Count; ++i ) - { - exportedTopicTypes = exportDDSModelPackage(packages.GetAt(i), - basePackage, - xmlDoc, - xmlRootNode, - topicTypesXml, - functionTopicsXml, - exportedTopicTypes ); - } - return exportedTopicTypes -} - -function ExportServiceInformation( _package, xmlDoc, xmlRootNode ) -{ - var servicePackage as EA.Package; - var serviceCollection as EA.Collection; - var hasDbServiceStructs = false; - var hasDataBaseModel = false; - var hasEnumerations = false; - var hasDdsModel = false; - - servicePackage = _package; - serviceCollection = servicePackage.Packages; - for( var i = 0; i < serviceCollection.Count; i++ ) - { - var obj = serviceCollection.GetAt( i ); - if( obj.ObjectType == otPackage ) - { - var package as EA.Package - package = obj - if( package.Name == "Database Service Structs") { - infoOut('Handling Database Service Structs ...'); - getDBServiceStructs( package, xmlDoc, xmlRootNode ) - hasDbServiceStructs = true; - infoOut('... ready'); - } else - if( package.Name == "Database Model") { - infoOut('Handling Database Model ...'); - getDBModel( package, xmlDoc, xmlRootNode ) - getDBViews( package, xmlDoc, xmlRootNode ) - hasDataBaseModel = true; - infoOut('... ready'); - } else - if( package.Name == "Enumerations") { - infoOut('Handling Enumerations ...'); - getEnumerations( package, xmlDoc, xmlRootNode ) - hasEnumerations = true; - infoOut('... ready'); - } else - if( package.Name == "DDS Model") { - infoOut('Handling DDS Model ...'); - getDDSModel( package, xmlDoc, xmlRootNode) - hasDdsModel = true; - infoOut('... ready'); - } - } - } - - if( !hasDbServiceStructs) { - warnOut("'Database Service Structs' not found!"); - } - if( !hasDataBaseModel) { - warnOut("'Database Model' not found!"); - } - if( !hasEnumerations) { - warnOut("'Enumerations' not found!"); - } - if( !hasDdsModel) { - warnOut("'DDS Model' not found!"); - } -} - -function ExportOperatorRights( _frameworkElement, _package, xmlDoc, xmlRootNode ) -{ - infoOut('Handling Operator Rights ...'); - var frameworkElement as EA.Element; - frameworkElement = _frameworkElement; - var package as EA.Package; // 'Plugin' - package = _package; - - var svcRightsOfs = TVGetElementTaggedValue( frameworkElement, "Legatus::OperatorRightsServiceOffset" ); - if( svcRightsOfs == null || svcRightsOfs == "" ) - { - warnOut( 'Legatus::OperatorRightsServiceOffset is not defined for framework ' + frameworkElement.Name ); - return; - } - var rightsRootNode = xmlDoc.createNode( 1, 'operatorRights', '' ); - xmlRootNode.appendChild( rightsRootNode ); - rightsRootNode.setAttribute( 'offset', svcRightsOfs); - if (!package) - return; - - var pluginPackages as EA.Collection; - pluginPackages = package.Packages; - for( var i = 0; i < pluginPackages.Count; i++ ) - { - var pluginPackage as EA.Package; - pluginPackage = pluginPackages.GetAt( i ); - var plgRightsOfs = TVGetElementTaggedValue( pluginPackage.Element, "Legatus::OperatorRightsGuiPluginOffset" ); - if( plgRightsOfs == null || plgRightsOfs == "" ) - { - warnOut( 'Legatus::OperatorRightsGuiPluginOffset is not defined for Gui Plugin1 ' + pluginPackage.Name ); - continue; - } - var pluginRightsNode = xmlDoc.createNode( 1, 'guiPlugin', '' ); - rightsRootNode.appendChild( pluginRightsNode ); - pluginRightsNode.setAttribute( 'name', pluginPackage.Name ); - pluginRightsNode.setAttribute( 'offset', plgRightsOfs ); - - //warnOut('name: ' + pluginPackage.Name) - var elements = pluginPackage.Elements; - for (var j = 0; j < elements.Count; j++) - { - var element as EA.Element; - var element = elements.GetAt( j ); - if (element.Stereotype != 'enumeration') - continue; - - var attributes as EA.Collection; - attributes = element.Attributes; - - if (attributes.Count == 0) - continue; - - var group = xmlDoc.createNode( 1, 'operatorRightGroup', '' ); - pluginRightsNode.appendChild( group ); - group.setAttribute( 'name', element.Name ) - if( element.Notes != "" ) - group.setAttribute( 'description', element.Notes ) - - - for (var k = 0; k < attributes.Count; k++) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( k ); - - var attributeNode = xmlDoc.createNode( 1, 'operatorRight', '' ); - group.appendChild( attributeNode ); - attributeNode.setAttribute( 'right', attribute.Name ); - attributeNode.setAttribute( 'value', attribute.Default ); - if( attribute.Notes != "" ) - attributeNode.setAttribute( 'description', attribute.Notes ); - } - } - } -} - -function DoSomething() -{ - // Show the script output window - Repository.EnsureOutputVisible( "Script" ); - - // Get the set of selected items as a collection - var selPkg = Repository.GetTreeSelectedPackage(); - - var frameworkPackage as EA.Package; - frameworkPackage = getFrameworkPackage( selPkg ); - if( frameworkPackage != null ) - { - Session.Output( "Export Service information data for " + frameworkPackage.Name ); - Session.Output( "===============================" ); - var xmlDoc = new ActiveXObject( 'Msxml2.DOMDocument' ); - var xmlRootNode = xmlDoc.createNode( 1, 'service', '' ); - xmlDoc.appendChild( xmlRootNode ); - var xmlSvnNode = xmlDoc.createNode( 1, 'rcs', '' ); - xmlRootNode.appendChild( xmlSvnNode ); - var xmlSvnNodeId = xmlDoc.createNode( 1, 'Id', '' ); - xmlSvnNode.appendChild( xmlSvnNodeId ); - xmlSvnNodeId.setAttribute( 'Id', '$Id$' ) - var xmlSvnNodeUrl = xmlDoc.createNode( 1, 'HeadURL', '' ); - xmlSvnNode.appendChild( xmlSvnNodeUrl ); - xmlSvnNodeUrl.setAttribute( 'HeadURL', '$HeadURL$' ) - var xmlSvnNodeAuthor = xmlDoc.createNode( 1, 'Author', '' ); - xmlSvnNode.appendChild( xmlSvnNodeAuthor ); - xmlSvnNodeAuthor.setAttribute( 'Author', '$Author$' ) - - var frameworkElement as EA.Element; - frameworkElement = frameworkPackage.Element; - - var frameworkCollection as EA.Collection; - frameworkCollection = frameworkPackage.Packages; - - // now search 'Service' Package inside frameworkCollection - var opRightsExported = false; - for( var i = 0; i < frameworkCollection.Count; i++ ) - { - var obj = frameworkCollection.GetAt( i ); - if( obj.ObjectType == otPackage ) - { - var package as EA.Package; - package = obj; - if( package.Name == "Service" || package.Name == "Proxy" ) { - ExportServiceInformation( package, xmlDoc, xmlRootNode ); - } - if( package.Name == "OperatorRights" ) { - ExportOperatorRights( frameworkElement, package, xmlDoc, xmlRootNode ); - opRightsExported = true; - } - } - } - - if (!opRightsExported) { - ExportOperatorRights( frameworkElement, null, xmlDoc, xmlRootNode ); - } - - xmlRootNode.setAttribute( 'namespace', frameworkPackage.Name ); - xmlRootNode.setAttribute( 'abbreviation', frameworkPackage.Alias.toUpperCase() ); - var procNames = TVGetElementTaggedValue( frameworkElement, "Legatus::ProcNames" ); - if( procNames == null || procNames == "" ) - procNames = frameworkPackage.Name; - xmlRootNode.setAttribute( 'procNames', procNames ); - var customRecordReplay = TVGetElementTaggedValue( frameworkElement, "Legatus::CustomRecordReplayPlugin" ); - if( customRecordReplay == 'true' ) - xmlRootNode.setAttribute( 'customRecordReplay', 1 ); - - exportTaggedValueGroup( xmlRootNode, 'exerciseDependency', frameworkElement, 'Legatus::ExerciseDependency' ); - exportTaggedValueGroup( xmlRootNode, 'groups', frameworkElement, 'Legatus::Groups' ); - - var writer = new ActiveXObject( "MSXML2.MXXMLWriter" ); - writer.indent = true; - writer.encoding = "UTF-8"; // for files only - writer.omitXMLDeclaration = false; - writer.standalone = true; - var reader = new ActiveXObject( "MSXML2.SAXXMLReader" ); - reader.contentHandler = writer; - reader.parse(xmlDoc); - - var fso = new ActiveXObject("Scripting.FileSystemObject"); - var outputFile = "C:\\TDS\\" + frameworkElement.Alias.toLowerCase() + "_service.xml"; - Session.Output("OutputFile: " + outputFile ); - var textStream = fso.CreateTextFile(outputFile, true); - // Writer always uses utf-16 as output ecoding - whyever - // also replace tab with 2 spaces - textStream.Write(writer.output.replace("encoding=\"UTF-16\"", "encoding=\"UTF-8\"").replace(/\t/g, " ")); - textStream.Write("\n"); - textStream.Close(); - - //Session.Prompt( "Wrote framework '" + frameworkPackage.Name + "' to " + outputFile , promptOk ); - } else { - Session.Prompt( "This script requires the current context item to be a framework package.\n" + - "Please select a framework package and try again.", promptOK ); - } -} - -DoSomething(); - - -function getPackageByName( element_, requestedName ) -{ - var element as EA.Element; - element = element_; - if ( element.Name == requestedName ) - return element; - - var elementId = element.PackageID; - if ( element.ObjectType == otPackage ) - { - var package as EA.Package; - package = element; - var packageElement as EA.Element; - packageElement = package.Element; - if ( packageElement == null ) - return null; - if ( packageElement.Name == requestedName ) - return package; - elementId = element.ParentID; - } - element = Repository.GetPackageByID( elementId ); - - return getPackageByName( element, requestedName ) -} - -function isFrameworkPackage( element ) -{ - if( element.ObjectType != otPackage ) - return false; - var package as EA.Package; - package = element; - return ( package.StereotypeEx == 'framework' ) -} - -function getOperatorRightsEnum( element ) -{ - var package as EA.Package; - package = element; - var elements = package.Elements; - - for (var i = 0; i < elements.Count; i++ ) - { - var element as EA.Element; - var element = elements.GetAt( i ); - if ( element.Stereotype == 'enumeration' && element.Name == 'OperatorRights' ) - return element; - } - return null; -} - -function ExportOperatorRightsTest() -{ - // Show the script output window - Repository.EnsureOutputVisible( "Script" ); - - Session.Output( "Export Operator Rights" ); - Session.Output( "======================" ); - // Get the set of selected items as a collection - var selPkg = Repository.GetTreeSelectedPackage(); - - var scPackage as EA.Package; - if( selPkg != null ) - { - scPackage = getPackageByName( selPkg, 'Software Components' ); - } - if ( scPackage == null ) - { - Session.Prompt( "This script requires the current context item to be inside the 'Software Components' package\n" + - "Please select this package and try again.", promptOK ); - return; - } - // now walk every leaf and search for an element named 'OperatorRights' - // with the stereotype 'enumeration' - // the path should be <> / Plugin / / <> OperatorRights - - var frameworkPackages as EA.Collection; - frameworkPackages = scPackage.Packages; - for( var i = 0; i < frameworkPackages.Count; i++ ) - { - var obj = frameworkPackages.GetAt( i ); - if ( !isFrameworkPackage( obj ) ) - continue; - var package as EA.Package; - package = obj; - var subPackages = package.Packages; - for( var j = 0; j < subPackages.Count; j++ ) - { - var subObj = subPackages.GetAt( j ); - if ( subObj.Name != 'Plugin' ) - continue; - var subPackage as EA.Package; - subPackage = subObj; - var pluginPackages = subPackage.Packages; - for ( var k = 0; k < pluginPackages.Count; k++ ) - { - var element as EA.Element; - element = getOperatorRightsEnum( pluginPackages.GetAt( k ) ); - if ( element == null ) - continue; - var attributes as EA.Collection; - attributes = element.Attributes; - for( var j = 0; j < attributes.Count; j++ ) - { - var attribute as EA.Attribute; - attribute = attributes.GetAt( j ); - Session.Output('found attribute: ' + attribute.Name + ' - ' + attribute.Default ); - } - } - } - } -} - -//ExportOperatorRightsTest(); - diff --git a/Lts/Common/CodeGen/main_grantlee.cpp b/Lts/Common/CodeGen/main_grantlee.cpp deleted file mode 100755 index 3ee571270..000000000 --- a/Lts/Common/CodeGen/main_grantlee.cpp +++ /dev/null @@ -1,172 +0,0 @@ - - - -#include "StructBuilder.h" -#include "XmlReader.h" -#include "DaoGeneratorGrantlee.h" -#include "DatabaseGeneratorGrantlee.h" -#include "CppTopicGeneratorGrantlee.h" -#include "SqlGenerator.h" - -#include -#include -#include -#include - - -class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser -{ -public: - CmdLineParser() - : LtsCommandLineParser(LtsCommandLineParser::LtsCommandLineParser::None) - { - addOption({ QStringList() << QStringLiteral("s") << QStringLiteral("servicefile"), - QStringLiteral("full path to the service xml-file"), - QStringLiteral("service xml-file"), - QStringLiteral("/home") - }); - - addOption({ QStringList() << QStringLiteral("a") << QStringLiteral("action"), - QStringLiteral("the action to execute"), - QStringLiteral("DATABASE|DATABASE_SERVER|IDL|DDS_TOPICS"), - QStringLiteral("/0") - }); - - addOption({ QStringList() << QStringLiteral("o") << QStringLiteral("outdir"), - QStringLiteral("the output directory where to put the generated files"), - QStringLiteral("output directory"), - QStringLiteral("/home") - }); - - addOption({ QStringList() << QStringLiteral("i") << QStringLiteral("idlinfo"), - QStringLiteral("the info file with all available idl service files"), - QStringLiteral("info file for idl"), - QStringLiteral("/0") - }); - } - - bool init() - { - if (!LtsCommandLineParser::init()) - return false; - - if (serviceFile().isEmpty() || action().isEmpty() || outDir().isEmpty()) - { - showHelp(); - return false; - } - return true; - } - - QString serviceFile() const { return value(QStringLiteral("s")); } - QString action() const { return value(QStringLiteral("a")); } - QString outDir() const { return value(QStringLiteral("o")); } - QString idlInfoFile() const { return value(QStringLiteral("i")); } -}; - -int main(int argc, char **argv) -{ - NGLCoreApplication app(argc, argv); - app.createLogger("CodeGen", "codegen_log4cxx.props", true ); - - NGL::INGLLogger &m_logger = app.baseLogger(); - - CmdLineParser options; - if (!options.init()) - return -1; - - QScopedPointer reader(new CodeGen::XmlReader); - - DEBUG_LOG(m_logger) << "ServiceFile: " << options.serviceFile(); - DEBUG_LOG(m_logger) << "IdlInfoFile: " << options.idlInfoFile(); - if (!reader->readServiceFile(options.serviceFile(), options.idlInfoFile())) - { - ERROR_LOG(m_logger) << "Error parsing service file '" << options.serviceFile() - << ": " << reader->errorString(); - return 2; - } - - QDir dir(options.outDir()); - DEBUG_LOG(m_logger) << "Directory: " << options.outDir(); - if (!dir.exists() && !dir.mkpath(options.outDir())) - { - ERROR_LOG(m_logger) << "Directory '" << options.outDir() - << "' does not exist and can not be created!"; - return 3; - } - - QScopedPointer structBuilder(new CodeGen::StructBuilder(reader.data())); - if (!structBuilder->buildStructures()) - { - ERROR_LOG(m_logger) << "Error building up structures"; - return 4; - } - - const QString action = options.action().toLower(); - - - QScopedPointer generator; - if (action == QStringLiteral("dao")) - generator.reset(new CodeGen::DaoGeneratorGrantlee); - else if (action == QStringLiteral("database")) - generator.reset(new CodeGen::DatabaseGeneratorGrantlee(false)); - else if (action == QStringLiteral("database_server")) - generator.reset(new CodeGen::DatabaseGeneratorGrantlee(true)); - else if (action == QStringLiteral("dds_topics")) - generator.reset(new CodeGen::CppTopicGeneratorGrantlee); - else if (action == QStringLiteral("sql")) - { - CodeGen::SqlGenerator sql(reader.data(), structBuilder.data()); - if (!sql.generateData(35)) - return 8; - - - if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Exercise)) - { - ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr(); - return 10; - } - - if (!sql.generateData(42)) - return 8; - - - if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Exercise)) - { - ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr(); - return 10; - } - - - sql.generateData(0); - if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Global)) - { - ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr(); - return 10; - } - return 0; - } - else - { - ERROR_LOG(m_logger) << "Invalid action '" << options.action() << "' given!"; - return 5; - } - - if (!generator->init(reader.data(), structBuilder.data())) - { - ERROR_LOG(m_logger) << "Error initializing generator!"; - return 6; - } - - - - - const auto posArgs = options.positionalArguments(); - for (const QString &file : posArgs) - { - - if (!generator->generateData(file, dir.absolutePath())) - return 7; - } - return 0; -} diff --git a/Lts/Common/CodeGen/main_grantlee_gui.cpp b/Lts/Common/CodeGen/main_grantlee_gui.cpp deleted file mode 100755 index 5c4574133..000000000 --- a/Lts/Common/CodeGen/main_grantlee_gui.cpp +++ /dev/null @@ -1,158 +0,0 @@ - - - -#include "main_grantlee_gui.h" -#include "ui_main_grantlee_gui.h" -#include "StructBuilder.h" -#include "XmlReader.h" -#include "CppTopicGeneratorGrantlee.h" -#include "DatabaseGeneratorGrantlee.h" - -#include -#include -#include -#include -#include - - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , m_xmlReader(nullptr) - , m_structBuilder(nullptr) - , m_generator(new CodeGen::CppTopicGeneratorGrantlee) - , m_engine(new Grantlee::Engine) - , m_ui(new Ui_MainGrantleeGui) -{ - m_idlServiceInfoFile = QStringLiteral("/home/chehrlic/TDS/TDS-build-product-clang/service_idl_files.txt"); - m_ui->setupUi(this); - connect(m_ui->actionExit, &QAction::triggered, - this, &MainWindow::close); - connect(m_ui->actionSelect_Data_Source, &QAction::triggered, - this, &MainWindow::onSelectDataSource); - connect(m_ui->actionOpen_Template, &QAction::triggered, - this, &MainWindow::onOpenTemplate); - connect(m_ui->actionSave_Template, &QAction::triggered, - this, &MainWindow::onSaveTemplate); - connect(m_ui->pbRefresh, &QPushButton::clicked, - this, &MainWindow::onRefreshOutput); - connect(m_ui->actionDatabaseGenerator, &QAction::triggered, - this, &MainWindow::onGeneratorDb); - connect(m_ui->actionDDS_Generator, &QAction::triggered, - this, &MainWindow::onGeneratorDds); -} - -MainWindow::~MainWindow() -{ - delete m_xmlReader; - delete m_structBuilder; - delete m_generator; - delete m_engine; - delete m_ui; -} - -static QLatin1String s_startDir("/home/chehrlic/TDS/TDS-source-product/Lts/"); - -void MainWindow::onSelectDataSource() -{ - const QString dataSource = QFileDialog::getOpenFileName(this, tr("Select data source"), s_startDir, tr("Service Files (*.xml)")); - if (dataSource.isEmpty()) - return; - delete m_xmlReader; - m_xmlReader = new CodeGen::XmlReader; - if (!m_xmlReader->readServiceFile(dataSource, m_idlServiceInfoFile)) - { - QMessageBox::warning(this, tr("Invalid data source"), tr("The selected data source is not valid: %1").arg(m_xmlReader->errorString())); - return; - } - delete m_structBuilder; - m_structBuilder = new CodeGen::StructBuilder(m_xmlReader); - if (!m_structBuilder->buildStructures()) - { - QMessageBox::warning(this, tr("Invalid data source"), tr("Error building structures from data")); - return; - } - m_ui->teOuput->clear(); - m_generator->init(m_xmlReader, m_structBuilder); - QMessageBox::information(this, tr("Data source"), tr("Successfully loaded data source")); -} - -void MainWindow::onOpenTemplate() -{ - const QString templateFile = QFileDialog::getOpenFileName(this, tr("Select data source"), s_startDir, tr("Templates (*.grantlee)")); - if (templateFile.isEmpty()) - return; - QFile f(templateFile); - if (!f.open(QIODevice::ReadOnly)) - { - QMessageBox::warning(this, tr("Error reading template file"), tr("Can not read file: %1").arg(f.errorString())); - return; - } - const QString content = QString::fromUtf8(f.readAll()); - m_ui->teInput->setText(content); - m_templateFile = templateFile; -} - -void MainWindow::onSaveTemplate() -{ - QFile f(m_templateFile); - if (!f.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(this, tr("Error saving template file"), tr("Can not save file: %1").arg(f.errorString())); - return; - } - f.write(m_ui->teInput->toPlainText().toUtf8()); - f.close(); - QMessageBox::information(this, tr("Saving template file"), tr("Successfully saved template to %1").arg(m_templateFile)); -} - -void MainWindow::onRefreshOutput() -{ - const QString in = m_ui->teInput->toPlainText(); - if (!m_generator->setTemplate(QString(), in)) - { - m_ui->teOuput->setText(m_generator->errorString()); - return; - } - m_ui->teOuput->setText(m_generator->render()); -} - -void MainWindow::onGeneratorDb() -{ - if (dynamic_cast(m_generator)) - return; - delete m_generator; - m_generator = new CodeGen::DatabaseGeneratorGrantlee(false); - if (m_xmlReader && m_structBuilder) - m_generator->init(m_xmlReader, m_structBuilder); -} - -void MainWindow::onGeneratorDds() -{ - if (dynamic_cast(m_generator)) - return; - delete m_generator; - m_generator = new CodeGen::CppTopicGeneratorGrantlee; - if (m_xmlReader && m_structBuilder) - m_generator->init(m_xmlReader, m_structBuilder); -} - - -int main(int argc, char **argv) -{ - NGLApplication app(argc, argv); - app.createLogger("CodeGen", "codegen_log4cxx.props"); - - NGL::INGLLogger &m_logger = app.baseLogger(); - STARTUP_LOG(m_logger); - - int iRet = 0; - { - QScopedPointer mw(new MainWindow); - mw->show(); - iRet = app.exec(); - } - - SHUTDOWN_LOG(m_logger); - return iRet; -} - diff --git a/Lts/Common/CodeGen/main_grantlee_gui.h b/Lts/Common/CodeGen/main_grantlee_gui.h deleted file mode 100755 index 02aa486d3..000000000 --- a/Lts/Common/CodeGen/main_grantlee_gui.h +++ /dev/null @@ -1,39 +0,0 @@ - - -#pragma once - -#include - -namespace CodeGen -{ -class XmlReader; -class StructBuilder; -class GeneratorGrantleeBase; -} -namespace Grantlee -{ -class Engine; -} -class Ui_MainGrantleeGui; -class MainWindow : public QMainWindow -{ - Q_OBJECT -public: - explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow(); -private Q_SLOTS: - void onSelectDataSource(); - void onOpenTemplate(); - void onSaveTemplate(); - void onRefreshOutput(); - void onGeneratorDb(); - void onGeneratorDds(); -private: - CodeGen::XmlReader *m_xmlReader; - CodeGen::StructBuilder *m_structBuilder; - CodeGen::GeneratorGrantleeBase *m_generator; - Grantlee::Engine *m_engine; - QString m_idlServiceInfoFile; - QString m_templateFile; - Ui_MainGrantleeGui *m_ui; -}; diff --git a/Lts/Common/CodeGen/main_grantlee_gui.ui b/Lts/Common/CodeGen/main_grantlee_gui.ui deleted file mode 100755 index c34b9fe73..000000000 --- a/Lts/Common/CodeGen/main_grantlee_gui.ui +++ /dev/null @@ -1,140 +0,0 @@ - - - MainGrantleeGui - - - - 0 - 0 - 838 - 532 - - - - MainWindow - - - - - - - Qt::Vertical - - - - - - - Input - - - - - - - - - - - - - - Output - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Refresh Output - - - Ctrl+R - - - - - - - - - - - - - - - - 0 - 0 - 838 - 22 - - - - - Fi&le - - - - - - - - - &Generator - - - - - - - - - - - &Exit - - - - - &Open Template - - - - - &Select Data Source - - - - - Sa&ve Template - - - - - &DatabaseGenerator - - - - - DDS &Generator - - - - - - diff --git a/Lts/Common/CodeGen/svc.idl.grantlee b/Lts/Common/CodeGen/svc.idl.grantlee deleted file mode 100755 index bdcf031d6..000000000 --- a/Lts/Common/CodeGen/svc.idl.grantlee +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for idl struct definitions - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Hanna E. Rathje, SLSE, 2015-08-05 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}.idl -#ifndef {{ namespace_abbr }}_idl_included -#define {{ namespace_abbr }}_idl_included - -//@copy #include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> // #define NDDSUSERDllExport for linux - -//@copy #include "{{ namespace_abbr }}_enums.hpp" -#include "{{ namespace_abbr }}_enums.idl" - -{% for ext_service in ext_services.member %} -//@copy #include <{{ ext_service.baseFilePath }}.hpp> -#include <{{ ext_service.baseFilePath }}.idl> -{% endfor %} - -module {{ namespace }} -{ - {% for idl_struct in idl_structs %} - - {% if idl_struct.comment %} // {{ idl_struct.comment }} {% endif %} - struct {{ idl_struct.name }} {% if idl_struct.baseValueType %}: {{ idl_struct.baseValueType }} {% endif %} - { {% for idl_member in idl_struct.member %} - {{ idl_member.type }} {{ idl_member.name }}; {{ idl_member.isKey }} {{ idl_member.isOptional }} {% endfor %} - }; //@Extensibility {{ idl_struct.extensibility }} - - {% endfor %} -}; // namespace {{ namespace }} - -#endif -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dao.cpp.grantlee b/Lts/Common/CodeGen/svc_dao.cpp.grantlee deleted file mode 100755 index 510fad917..000000000 --- a/Lts/Common/CodeGen/svc_dao.cpp.grantlee +++ /dev/null @@ -1,204 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id: svc_db_impl.cpp.grantlee 56901 2022-04-22 04:21:30Z karwat $ - * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee $ - * $Author: karwat $ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dao.cpp -#include "{{ namespace_abbr }}_dao.h" -{% if custom_db_procs %} -#include <{{ include_prefix }}/{{ namespace_abbr }}_db_impl_custom.h> -{% endif %} - -#include -#include -#include -#include -#include -#include -#include - -namespace {{ namespace }} -{ - -Dao::Dao(int exerciseId) - : m_query(nullptr) - , m_exerciseId(exerciseId) - , m_bEmitMessageOnError(true) - , m_bCanHandleSchemas(true) - , m_ownDatabaseConnection(true) - , m_logger(getLogger("Db")) -{} - -Dao::~Dao() -{ - m_query = nullptr; -{% if has_db_notifications %} - QSqlDriver *driver = m_dbConnection.driver(); - if (driver && m_dbConnection.isOpen()) - { -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} driver->unsubscribeFromNotification(QStringLiteral("{{ db_notify }}")); -{% endfor %}{% endfor %} } -{% endif %} - if (m_ownDatabaseConnection && m_dbConnection.isOpen()) - m_dbConnection.close(); -} - -bool Dao::init(const QString &dbUser, - const QString &dbPasswd, - const QString &dbDriver, - const QString &dbHost, - const QString &dbName) -{ - m_ownDatabaseConnection = true; - - m_dbConnection = DBConnection::open(m_logger, dbUser, dbPasswd, dbDriver, dbHost, dbName, schemaName()); - - return initInternal(); -} - -bool Dao::initInternal() -{ - QSqlDriver *driver = m_dbConnection.driver(); - if (!driver) - { - WARNING_LOG(m_logger) << "No SQL driver loaded - no database available!"; - return false; - } - const QString driverName = m_dbConnection.driverName(); - DEBUG_LOG(m_logger) << "Connected to database " << m_dbConnection.databaseName() << "@" << m_dbConnection.hostName() - << ", schema: " << schemaName() << ", used driver: " << driverName; - if (!m_dbConnection.isOpen()) - { - WARNING_LOG(m_logger) << "Database connection is no open - can not initialize database interface"; - return false; - } - if (driverName == QLatin1String("QSQLITE")) - m_bCanHandleSchemas = false; - -{% if has_db_notifications %} - if (!driver->hasFeature(QSqlDriver::EventNotifications)) - { - WARNING_LOG(m_logger) << "Can not subscribe to notifications - sqldriver does not support this feature!"; - } - else - { - connect(driver, QOverload::of(&QSqlDriver::notification), - this, &Dao::onNotification); -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}driver->subscribeToNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %} - } -{% endif %} - m_query = std::unique_ptr(new NSqlQuery(m_dbConnection, m_logger)); - return true; -} - -QString Dao::schemaName() const -{ - if (!m_bCanHandleSchemas) - return QString(); - if (m_exerciseId <= 0) - return QStringLiteral("{{ namespace_abbr }}"); - return DBCommon::getExerciseSchemaName(m_exerciseId); -} - -QString Dao::dbUser() const -{ - if (m_exerciseId > 0) - return QStringLiteral("ets_ex_00"); - else - return QStringLiteral("ets_") + schemaName(); -} - -void Dao::setEmitMessageOnError(bool bEmit) -{ - m_bEmitMessageOnError = bEmit; -} - -QString Dao::getTableName(const QString &tableName) const -{ - if (!m_bCanHandleSchemas) - return tableName; - if (m_exerciseId > 0) - return DBCommon::getExerciseTableName(tableName, m_exerciseId); - return QStringLiteral("{{ namespace_abbr }}.") + tableName; -} - -QSqlError Dao::getLastError() const -{ - return m_lastError; -} - -{% if has_db_notifications %} -void Dao::onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload) -{ - DEBUG_LOG(m_logger) << "Notification source = " << source; - if (source == QSqlDriver::SelfSource) - return; - // search coresponding signal -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} - if (name == QStringLiteral("{{ db_notify }}")) - { - emit signal{{ db_notify }}(payload.toInt()); - - return; - } - {% endfor %}{% endfor %} -} -{% endif %} - -{% for cpp_struct in cpp_structs %} -// -// --> Struct: Database::{{ cpp_struct.name }} -// - -bool Dao::select(QVector &dataVec, const QString &where) -{ - SqlQueryJanitor janitor(m_query); - const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data) - { - dataVec.push_back(data); - }; - return selectPrepareInternal{{ cpp_struct.name }}(where) && selectFetchInternal{{ cpp_struct.name }}(cb); -} - -bool Dao::selectPrepareInternal{{ cpp_struct.name }}(const QString &where) -{ - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.viewTableName }}"))); - const QString stmt(QStringLiteral("SELECT {% for row in cpp_struct.viewMember %}{{ row.dbColumnNameView }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " FROM ") + tableName + where); - return m_query->prepare(stmt); -} - -bool Dao::selectFetchInternal{{ cpp_struct.name }}(const std::function &cb) -{ - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - Database::{{ cpp_struct.name }} data; - while (m_query->next()) - { - int i = 0; - {% for row in cpp_struct.viewMember %} - data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %} - cb(data); - } - - return true; -} - -// <-- Database::{{ cpp_struct.name }} end -{% endfor %} - -/*static*/ QString Dao::qVariantToJsonString(const QVariant &value) -{ - return value.isNull() ? QStringLiteral("{}") : value.toString(); -} - -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dao.h.grantlee b/Lts/Common/CodeGen/svc_dao.h.grantlee deleted file mode 100755 index 49855abbd..000000000 --- a/Lts/Common/CodeGen/svc_dao.h.grantlee +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id: svc_db_impl.h.grantlee 53676 2021-06-10 10:21:06Z diers $ - * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/svc_db_impl.h.grantlee $ - * $Author: diers $ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dao.h -#pragma once - -#include -#include -#include -#include -#include -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp> -#include <{{ include_prefix }}/{{ namespace_abbr }}_db.h> -#include <{{ include_prefix }}/logger.h> - -class NSqlQuery; -namespace {{ namespace }} -{ -class {{ namespace_abbr_upper }}_COMMON_EXPORT Dao {% if not custom_db_procs %}final {%endif %}: public QObject -{ - Q_OBJECT -public: - explicit Dao(int exerciseId); - ~Dao(); - - bool init(const QString &dbUser, - const QString &dbPasswd, - const QString &dbDriver, - const QString &dbHost, - const QString &dbName); - - QString schemaName() const; - QString dbUser() const; - const QSqlDatabase &database() const { return m_dbConnection; } - void setEmitMessageOnError(bool bEmit); - QSqlError getLastError() const; - - {% for cpp_struct in cpp_structs %} - // --> Database::{{ cpp_struct.name }} -public: - bool select(QVector &dataVec, const QString &where = QString()); - -private: - bool selectPrepareInternal{{ cpp_struct.name }}(const QString &where); - bool selectFetchInternal{{ cpp_struct.name }}(const std::function &cb); - // <-- Database::{{ cpp_struct.name }} - {% endfor %} - - static QString qVariantToJsonString(const QVariant &value); - -protected: - QString getTableName(const QString &tableName) const; -{% if has_db_notifications %} -private Q_SLOTS: - void onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload); - -Q_SIGNALS: -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} void signal{{ db_notify }}(int id); -{% endfor %}{% endfor %}{% endif %} -protected: - QSqlDatabase m_dbConnection; - std::unique_ptr m_query; - QSqlError m_lastError; - int m_exerciseId; - bool m_bEmitMessageOnError; - bool m_bCanHandleSchemas; - bool m_ownDatabaseConnection; - LOGGER_T m_logger; - -private: - bool initInternal(); -};// class Dao -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_db.h.grantlee b/Lts/Common/CodeGen/svc_db.h.grantlee deleted file mode 100755 index 30ead9d31..000000000 --- a/Lts/Common/CodeGen/svc_db.h.grantlee +++ /dev/null @@ -1,145 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_db.h -#pragma once - -#include -#include -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_db_structs.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp> - -{% for ext_service in ext_services.member %} -#include <{{ ext_service.baseFilePath }}.hpp> -{% endfor %} - -namespace NGL -{ -class INGLLogger; -} -class QSqlDatabase; - -namespace {{ namespace }} -{ - -class {{ namespace_abbr_upper }}_COMMON_EXPORT DatabaseInterface : public QObject -{ - Q_OBJECT -public: - virtual ~DatabaseInterface() = default; - - /** - * Create a new instance for {{ namespace }}::DatabaseInterface. - * @param exerciseId if <= 0 -> global service - */ - static std::shared_ptr create(int exerciseId); - - /** - * @brief initialize the database interface (must be called after the db connection was opened!) - * @return true on succcess - */ - virtual bool init(const QSqlDatabase &dbConnection) = 0; - virtual bool init(const QString &dbUser, - const QString &dbPasswd, - const QString &dbDriver, - const QString &dbHost, - const QString &dbName) = 0; - - /** - * @brief return the current schema name depending if exercise id > 0 or not - */ - virtual QString schemaName() const = 0; - - /** - * @brief return the current database user dependening if exercise id > 0 or not - */ - virtual QString dbUser() const = 0; - - /** - * @brief return the database connection used in this DatabaseInterface - */ - virtual const QSqlDatabase &database() const = 0; - - /** - * emit an error message (ERROR_LOG) when a statement fails to execute (default: on) - */ - virtual void setEmitMessageOnError(bool bEmit) = 0; - - virtual QSqlError getLastError() const = 0; - - {% for cpp_struct in cpp_structs %} - // - // Database::{{ cpp_struct.name }} - // -public: - virtual bool insert(Database::{{ cpp_struct.name }} &data) = 0; - virtual bool upsert(Database::{{ cpp_struct.name }} &data) = 0; - virtual bool update(const Database::{{ cpp_struct.name }} &data) = 0; -{% if cpp_struct.insertStructures|length == 1 %} - virtual bool insert(std::vector &cont) = 0; - virtual bool upsert(std::vector &cont) = 0; - virtual bool update(const std::vector &cont) = 0; -{% endif %} - virtual bool select(QVector &dataVec) = 0; - virtual bool select(const std::function &cb) = 0; - {% for fk in cpp_struct.selectRemoveMember %} - virtual bool selectBy{{ fk.cppMemberNameCc }}(QVector &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) = 0; - virtual bool selectBy{{ fk.cppMemberNameCc }}(const std::function &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) = 0; - {% endfor %} - virtual bool select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data) = 0; - virtual bool remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}) = 0; - {% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %} - virtual bool remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }}) = 0; - {% endfor %} {% endif %} - virtual bool fixUpSequence{{ cpp_struct.name }}() = 0; - virtual bool truncate{{ cpp_struct.name }}() = 0; -{% if cpp_struct.dbNotifications %} -Q_SIGNALS: - {% for dbnotify in cpp_struct.dbNotifications %} - // database notification on insert/update/delete - void {{ dbnotify }}(const QVariant &payload); - {% endfor %} - {% endif %} - // - // Database::{{ cpp_struct.name }} end - // - {% endfor %} -{% if data_structs|length > 0 %} -public:{% for data_struct in data_structs %} - // read only struct Database::{{ data_struct.name }} - virtual bool select(QVector &dataVec) = 0; -{% endfor %} {% endif %} -{% if custom_db_procs|length > 0 %} -public:{% for db_proc in custom_db_procs %} - // custom proc, body must be implemented by user - virtual {{ db_proc.signature }} = 0; -{% endfor %} {% endif %} - -public: - - // vector of database struct to/from JSON-array - // helper for BaseDownloader - template - static QJsonArray toJson(const QVector &dataVec) - { - return Database::toJson(dataVec); - } - - template - static std::vector fromJson(const QJsonArray &arr) - { - return Database::fromJson(arr); - } -};// class DatabaseInterface - -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee b/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee deleted file mode 100755 index ee32ddbe2..000000000 --- a/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee +++ /dev/null @@ -1,732 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_db_impl.cpp -#include "{{ namespace_abbr }}_db_impl.h" -{% if custom_db_procs %} -#include <{{ include_prefix }}/{{ namespace_abbr }}_db_impl_custom.h> -{% endif %} - -#include -#include -#include -#include -#include -#include -#include - -namespace {{ namespace }} -{ - -QString qVariantToJsonString(const QVariant &value) -{ - return value.isNull() ? QStringLiteral("{}") : value.toString(); -} - -QString fixupEmptyJsonString(const QString &str) -{ - return str.isEmpty() ? QStringLiteral("{}") : str; -} - -// DatabaseInterface -std::shared_ptr DatabaseInterface::create(int exerciseId) -{ - {% if custom_db_procs %} - return std::make_shared(exerciseId); - {% else %} - return std::make_shared(exerciseId); - {% endif %} -} - -DatabaseInterfaceImpl::DatabaseInterfaceImpl(int exerciseId) - : m_query(nullptr) - , m_exerciseId(exerciseId) - , m_bEmitMessageOnError(true) - , m_bCanHandleSchemas(true) - , m_ownDatabaseConnection(true) - , m_logger(getLogger("Db")) -{} - -DatabaseInterfaceImpl::~DatabaseInterfaceImpl() -{ - m_query = nullptr; -{% if has_db_notifications %} - QSqlDriver *driver = m_dbConnection.driver(); - if (driver && m_dbConnection.isOpen()) - { -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} - driver->unsubscribeFromNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %} - } -{% endif %} - if (m_ownDatabaseConnection && m_dbConnection.isOpen()) - m_dbConnection.close(); -} - -bool DatabaseInterfaceImpl::init(const QSqlDatabase &dbConnection) -{ - m_dbConnection = dbConnection; - m_ownDatabaseConnection = false; - return initInternal(); -} - -bool DatabaseInterfaceImpl::init(const QString &dbUser, - const QString &dbPasswd, - const QString &dbDriver, - const QString &dbHost, - const QString &dbName) -{ - m_ownDatabaseConnection = true; - - m_dbConnection = DBConnection::open(m_logger, dbUser, dbPasswd, dbDriver, dbHost, dbName, schemaName()); - - return initInternal(); -} - -bool DatabaseInterfaceImpl::initInternal() -{ - QSqlDriver *driver = m_dbConnection.driver(); - if (!driver) - { - WARNING_LOG(m_logger) << "No SQL driver loaded - no database available!"; - return false; - } - const QString driverName = m_dbConnection.driverName(); - DEBUG_LOG(m_logger) << "Connected to database " << m_dbConnection.databaseName() << "@" << m_dbConnection.hostName() - << ", schema: " << schemaName() << ", used driver: " << driverName; - if (!m_dbConnection.isOpen()) - { - WARNING_LOG(m_logger) << "Database connection is no open - can not initialize database interface"; - return false; - } - if (driverName == QLatin1String("QSQLITE")) - m_bCanHandleSchemas = false; - -{% if has_db_notifications %} - if (!driver->hasFeature(QSqlDriver::EventNotifications)) - { - WARNING_LOG(m_logger) << "Can not subscribe to notifications - sqldriver does not support this feature!"; - } - else - { - connect(driver, QOverload::of(&QSqlDriver::notification), - this, &DatabaseInterfaceImpl::onNotification); -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} - driver->subscribeToNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %} - } -{% endif %} - m_query = std::unique_ptr(new NSqlQuery(m_dbConnection, m_logger)); - return true; -} - -QString DatabaseInterfaceImpl::schemaName() const -{ - if (!m_bCanHandleSchemas) - return QString(); - if (m_exerciseId <= 0) - return QStringLiteral("{{ namespace_abbr }}"); - return DBCommon::getExerciseSchemaName(m_exerciseId); -} - -QString DatabaseInterfaceImpl::dbUser() const -{ - if (m_exerciseId > 0) - return QStringLiteral("ets_ex_00"); - else - return QStringLiteral("ets_") + schemaName(); -} - -void DatabaseInterfaceImpl::setEmitMessageOnError(bool bEmit) -{ - m_bEmitMessageOnError = bEmit; -} - -QString DatabaseInterfaceImpl::getTableName(const QString &tableName) const -{ - if (!m_bCanHandleSchemas) - return tableName; - if (m_exerciseId > 0) - return DBCommon::getExerciseTableName(tableName, m_exerciseId); - return QStringLiteral("{{ namespace_abbr }}.") + tableName; -} - -QSqlError DatabaseInterfaceImpl::getLastError() const -{ - return m_lastError; -} - -{% if has_db_notifications %} -void DatabaseInterfaceImpl::onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload) -{ - DEBUG_LOG(m_logger) << "Notification source = " << source; - // no need to emit notification signals - if (source == QSqlDriver::SelfSource) - return; - // search coresponding signal -{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} - if (name == QLatin1String("{{ db_notify }}")) - { - emit {{ db_notify }}(payload); - }{% endfor %}{% endfor %} -} -{% endif %} - -{% for cpp_struct in cpp_structs %} -// -// Struct: Database::{{ cpp_struct.name }} -// -bool DatabaseInterfaceImpl::insert(Database::{{ cpp_struct.name }} &data) -{ - SqlQueryJanitor janitor(m_query); -{% if cpp_struct.insertStructures|length > 1 %} - DBTransaction transaction(m_dbConnection); -{% endif %} -{% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - - QString keyColumnInsertKey; - QString keyColumnInsertVal; - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - const bool bInsertWithKey = data.{{cpp_struct.primaryKey.cppMemberName}} != {{cpp_struct.primaryKey.cppDefault}}; - if (bInsertWithKey) - { - keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, "); - keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, "); - } - {% endif %} - const QString stmt(QStringLiteral("INSERT INTO ") + tableName + - QStringLiteral(" (") + keyColumnInsertKey + - QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " ) VALUES (") + keyColumnInsertVal + - QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )")); - - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if insertStruct.serialMember.cppMemberName %} - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}}) - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% else %} - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% endif %} - {% if insertStruct.baseId.cppMemberName %} - data.{{ insertStruct.baseId.cppMemberName }} = data.{{ insertStruct.serialMember.cppMemberName }}; - {% endif %} - {% endif %} - } -{% endfor %} -{% if cpp_struct.insertStructures|length > 1 %} - transaction.commit(); -{% endif %} - return true; -} - -bool DatabaseInterfaceImpl::upsert(Database::{{ cpp_struct.name }} &data) -{ - SqlQueryJanitor janitor(m_query); -{% if cpp_struct.insertStructures|length > 1 %} - DBTransaction transaction(m_dbConnection); -{% endif %} -{% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - - QString keyColumnInsertKey; - QString keyColumnInsertKeyConflict; - QString keyColumnInsertVal; - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - const bool bInsertWithKey = true; - keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, "); - keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, "); - {% endif %} - - keyColumnInsertKeyConflict = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }} "); - - const QString stmt(QStringLiteral("INSERT INTO ") + tableName + - QStringLiteral(" (") + keyColumnInsertKey + - QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " ) VALUES (") + keyColumnInsertVal + - QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )") + - QStringLiteral(" ON CONFLICT (") + keyColumnInsertKeyConflict + QStringLiteral(") DO UPDATE ") + - QStringLiteral(" SET" - " {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " WHERE excluded.{{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}")); - - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if insertStruct.serialMember.cppMemberName %} - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}}) - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% else %} - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% endif %} - {% if insertStruct.baseId.cppMemberName %} - data.{{ insertStruct.baseId.cppMemberName }} = data.{{ insertStruct.serialMember.cppMemberName }}; - {% endif %} - {% endif %} - } -{% endfor %} -{% if cpp_struct.insertStructures|length > 1 %} - transaction.commit(); -{% endif %} - return true; -} - -bool DatabaseInterfaceImpl::update(const Database::{{ cpp_struct.name }} &data) -{ - SqlQueryJanitor janitor(m_query); -{% if cpp_struct.insertStructures|length > 1 %} - DBTransaction transaction(m_dbConnection); -{% endif %} - {% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - const QString stmt(QStringLiteral("UPDATE ") + tableName + - QStringLiteral(" SET" - " {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " WHERE {{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}")); - - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - {% if insertStruct.serialMember.cppMemberName %} - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - } - {% endfor %} -{% if cpp_struct.insertStructures|length > 1 %} - transaction.commit(); -{% endif %} - return true; -} - -{% if cpp_struct.insertStructures|length == 1 %} -// current only implemented for 'plain' structures with only one affected table -bool DatabaseInterfaceImpl::insert(std::vector &cont) -{ - if (cont.empty()) - return true; - - {% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - - QString keyColumnInsertKey; - QString keyColumnInsertVal; - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - const bool bInsertWithKey = cont[0].{{cpp_struct.primaryKey.cppMemberName}} != {{cpp_struct.primaryKey.cppDefault}}; - // the first element determines the stmt to be used - if (bInsertWithKey) - { - keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, "); - keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, "); - } - {% endif %} - const QString stmt(QStringLiteral("INSERT INTO ") + tableName + - QStringLiteral(" (") + keyColumnInsertKey + - QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " ) VALUES (") + keyColumnInsertVal + - QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )")); - - { - SqlQueryJanitor janitor(m_query); - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - for (auto &data : cont) - { - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if insertStruct.serialMember.cppMemberName %} - if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}}) - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% endif %} - } - } - - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - fixUpSequence{{ cpp_struct.nameCc }}(); - {% endif %} - - } - {% endfor %} - return true; -} - -bool DatabaseInterfaceImpl::upsert(std::vector &cont) -{ - if (cont.empty()) - return true; - - {% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - - QString keyColumnInsertKey; - QString keyColumnInsertKeyConflict; - QString keyColumnInsertVal; - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - const bool bInsertWithKey = true; - keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, "); - keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, "); - {% endif %} - // the first element determines the stmt to be used - keyColumnInsertKeyConflict = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }} "); - - const QString stmt(QStringLiteral("INSERT INTO ") + tableName + - QStringLiteral(" (") + keyColumnInsertKey + - QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " ) VALUES (") + keyColumnInsertVal + - QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )") + - QStringLiteral(" ON CONFLICT (") + keyColumnInsertKeyConflict + QStringLiteral(") DO UPDATE ") + - QStringLiteral(" SET" - " {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " WHERE excluded.{{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}")); - - { - SqlQueryJanitor janitor(m_query); - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - for (auto &data : cont) - { - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - {% if insertStruct.serialMember.cppMemberName %} - if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}}) - data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }}; - {% endif %} - } - } - - {% if forloop.first and cpp_struct.primaryKey.dbHasSequence %} - if (bInsertWithKey) - fixUpSequence{{ cpp_struct.nameCc }}(); - {% endif %} - - } - {% endfor %} - return true; -} - -// current only implemented for 'plain' structures with only one affected table -bool DatabaseInterfaceImpl::update(const std::vector &cont) -{ - if (cont.empty()) - return true; - - {% for insertStruct in cpp_struct.insertStructures %} - { - const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}"))); - const QString stmt(QStringLiteral("UPDATE ") + tableName + - QStringLiteral(" SET" - " {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " WHERE {{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}")); - - { - SqlQueryJanitor janitor(m_query); - if (!m_query->prepare(stmt)) - { - m_lastError = m_query->lastError(); - return false; - } - - for (const auto &data : cont) - { - {% for row in insertStruct.member %} - m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }}); - {% endfor %} - {% if insertStruct.serialMember.cppMemberName %} - m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }}); - {% endif %} - - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - } - } - } - {% endfor %} - return true; -} -{% endif %} - -bool DatabaseInterfaceImpl::select(QVector &dataVec) -{ - SqlQueryJanitor janitor(m_query); - const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data) - { - dataVec.push_back(data); - }; - return selectPrepareInternal{{ cpp_struct.name }}(QString()) && selectFetchInternal{{ cpp_struct.name }}(cb); -} - -bool DatabaseInterfaceImpl::select(const std::function &cb) -{ - SqlQueryJanitor janitor(m_query); - return selectPrepareInternal{{ cpp_struct.name }}(QString()) && selectFetchInternal{{ cpp_struct.name }}(cb); -} - -{% for fk in cpp_struct.selectRemoveMember %} -bool DatabaseInterfaceImpl::selectBy{{ fk.cppMemberNameCc }}(QVector &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy) -{ - SqlQueryJanitor janitor(m_query); - if (!selectPrepareInternal{{ cpp_struct.name }}(QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.dbColumnName }}") + orderBy)) - { - ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text(); - m_lastError = m_query->lastError(); - return false; - } - m_query->bindValue(QStringLiteral(":{{ fk.dbColumnName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }}); - const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data) - { - dataVec.push_back(data); - }; - return selectFetchInternal{{ cpp_struct.name }}(cb); -} - -bool DatabaseInterfaceImpl::selectBy{{ fk.cppMemberNameCc }}(const std::function &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy) -{ - SqlQueryJanitor janitor(m_query); - if (!selectPrepareInternal{{ cpp_struct.name }}(QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.dbColumnName }}") + orderBy)) - { - ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text(); - m_lastError = m_query->lastError(); - return false; - } - m_query->bindValue(QStringLiteral(":{{ fk.dbColumnName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }}); - return selectFetchInternal{{ cpp_struct.name }}(cb); -} -{% endfor %} - -bool DatabaseInterfaceImpl::selectPrepareInternal{{ cpp_struct.name }}(const QString &where) -{ - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.viewTableName }}"))); - const QString stmt(QStringLiteral("SELECT {% for row in cpp_struct.viewMember %}{{ row.dbColumnNameView }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " FROM ") + tableName + where); - return m_query->prepare(stmt); -} - -bool DatabaseInterfaceImpl::selectFetchInternal{{ cpp_struct.name }}(const std::function &cb) -{ - if (!m_query->exec(m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - Database::{{ cpp_struct.name }} data; - while (m_query->next()) - { - int i = 0; - {% for row in cpp_struct.viewMember %} - data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %} - cb(data); - } - - return true; -} - -bool DatabaseInterfaceImpl::select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data) -{ - SqlQueryJanitor janitor(m_query); - int iCnt = 0; - const auto cb =[&data, &iCnt] (const Database::{{ cpp_struct.name }} &content) - { - data = content; - ++iCnt; - }; - const QString where = QStringLiteral(" WHERE {{ cpp_struct.primaryKey.dbColumnName }} = %1").arg({{ cpp_struct.primaryKey.cppMemberName }}); - if (!selectPrepareInternal{{ cpp_struct.name }}(where) || !selectFetchInternal{{ cpp_struct.name }}(cb)) - return false; - - return (iCnt == 1); -} - -bool DatabaseInterfaceImpl::remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}) -{ - SqlQueryJanitor janitor(m_query); - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}"))); - const QString stmt(QStringLiteral("DELETE FROM ") + tableName + - QStringLiteral(" WHERE {{ cpp_struct.primaryKey.dbColumnName }} = ") + - QString::number({{ cpp_struct.primaryKey.cppMemberName }})); - - if (!m_query->exec(stmt, m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - return true; -} - -{% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %} -bool DatabaseInterfaceImpl::remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }}) -{ - SqlQueryJanitor janitor(m_query); - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}"))); - const QString stmt(QStringLiteral("DELETE FROM ") + tableName + - QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.cppMemberName }}")); - - if (!m_query->prepare(stmt)) - { - ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text(); - m_lastError = m_query->lastError(); - return false; - } - - m_query->bindValue(QStringLiteral(":{{ fk.cppMemberName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }}); - - - return m_query->exec(m_bEmitMessageOnError); -} -{% endfor %} {% endif %} -bool DatabaseInterfaceImpl::fixUpSequence{{ cpp_struct.nameCc }}() -{ - SqlQueryJanitor janitor(m_query); - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}"))); - const QString stmt(QStringLiteral("SELECT setval('") + tableName + QStringLiteral("_{{ cpp_struct.primaryKey.dbColumnName }}_seq', COALESCE((SELECT MAX({{ cpp_struct.primaryKey.dbColumnName }})+1 FROM ") + - tableName + QStringLiteral("), 1),false)")); - - if (!m_query->exec(stmt, m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - return true; -} - -bool DatabaseInterfaceImpl::truncate{{ cpp_struct.nameCc }}() -{ - SqlQueryJanitor janitor(m_query); - const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}"))); - const QString stmt(QStringLiteral("TRUNCATE TABLE ") + tableName + QStringLiteral(" CASCADE")); - - if (!m_query->exec(stmt, m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - return true; -} -// Database::{{ cpp_struct.name }} end -{% endfor %} - -{% for ds in data_structs %} -bool DatabaseInterfaceImpl::select(QVector &dataVec) -{ - SqlQueryJanitor janitor(m_query); - const QString tableName(getTableName(QStringLiteral("{{ ds.viewTableName }}"))); - const QString stmt(QStringLiteral("SELECT {% for row in ds.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}" - " FROM ") + tableName); - - if (!m_query->exec(stmt, m_bEmitMessageOnError)) - { - m_lastError = m_query->lastError(); - return false; - } - - Database::{{ ds.name }} data; - while (m_query->next()) - { - int i = 0; - {% for row in ds.member %} - data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %} - dataVec.append(data); - } - - return true; -} -{% endfor %} -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_db_impl.h.grantlee b/Lts/Common/CodeGen/svc_db_impl.h.grantlee deleted file mode 100755 index 558c05dfc..000000000 --- a/Lts/Common/CodeGen/svc_db_impl.h.grantlee +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_db_impl.h -#pragma once - -#include -#include -#include -#include -#include -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp> -#include <{{ include_prefix }}/{{ namespace_abbr }}_db.h> -#include <{{ include_prefix }}/logger.h> - -class NSqlQuery; -namespace {{ namespace }} -{ -class {{ namespace_abbr_upper }}_COMMON_EXPORT DatabaseInterfaceImpl {% if not custom_db_procs %}final {%endif %}: public DatabaseInterface -{ - Q_OBJECT -public: - explicit DatabaseInterfaceImpl(int exerciseId); - ~DatabaseInterfaceImpl(); - - bool init(const QSqlDatabase &dbConnection) override final; - bool init(const QString &dbUser, - const QString &dbPasswd, - const QString &dbDriver, - const QString &dbHost, - const QString &dbName) override final; - - QString schemaName() const override final; - QString dbUser() const override final; - const QSqlDatabase &database() const override final { return m_dbConnection; } - void setEmitMessageOnError(bool bEmit) override final; - QSqlError getLastError() const override final; - - {% for cpp_struct in cpp_structs %} - - // Database::{{ cpp_struct.name }} - bool insert(Database::{{ cpp_struct.name }} &data) override final; - bool upsert(Database::{{ cpp_struct.name }} &data) override final; - bool update(const Database::{{ cpp_struct.name }} &data) override final; -{% if cpp_struct.insertStructures|length == 1 %} - bool insert(std::vector &cont) override final; - bool upsert(std::vector &cont) override final; - bool update(const std::vector &cont) override final; -{% endif %} - bool select(QVector &dataVec) override final; - bool select(const std::function &cb) override final; - {% for fk in cpp_struct.selectRemoveMember %} - bool selectBy{{ fk.cppMemberNameCc }}(QVector &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) override final; - bool selectBy{{ fk.cppMemberNameCc }}(const std::function &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) override final; - {% endfor %} -protected: - bool selectPrepareInternal{{ cpp_struct.name }}(const QString &where); - bool selectFetchInternal{{ cpp_struct.name }}(const std::function &cb); -public: - bool select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data) override final; - bool remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}) override final; - {% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %} - bool remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }}) override final; - {% endfor %} {% endif %} - bool fixUpSequence{{ cpp_struct.name }}() override final; - bool truncate{{ cpp_struct.name }}() override final; - // Database::{{ cpp_struct.name }} - {% endfor %} - - {% for data_struct in data_structs %} - // read only struct{{ data_struct.name }} - bool select(QVector &dataVec) override final; - {% endfor %} - -protected: - QString getTableName(const QString &tableName) const; -{% if has_db_notifications %} -private Q_SLOTS: - void onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload); -{% endif %} -protected: - QSqlDatabase m_dbConnection; - std::unique_ptr m_query; - QSqlError m_lastError; - int m_exerciseId; - bool m_bEmitMessageOnError; - bool m_bCanHandleSchemas; - bool m_ownDatabaseConnection; - LOGGER_T m_logger; -private: - bool initInternal(); -};// class DatabaseInterfaceImpl -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee b/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee deleted file mode 100755 index baa4ee475..000000000 --- a/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_db_structs.cpp -#include "{{ namespace_abbr }}_db_structs.h" -#include "{{ namespace_abbr }}_export.h" - -namespace {{ namespace }} -{ -namespace Database -{ -QString jsonValueToJsonString(const QJsonValue &value) -{ - return value.isNull() ? QStringLiteral("{}") : value.toString(); -} - -QString qVariantToJsonString(const QVariant &value) -{ - return value.isNull() ? QStringLiteral("{}") : value.toString(); -} - -QString fixupEmptyJsonString(const QString &str) -{ - return str.isEmpty() ? QStringLiteral("{}") : str; -} - - -// QPointF -QJsonValue toJson(const QPointF &element) -{ - QJsonObject obj; - obj.insert(QStringLiteral("x"), QJsonValue(element.x())); - obj.insert(QStringLiteral("y"), QJsonValue(element.y())); - return QJsonValue(obj); -} -template<> -QPointF fromJson(const QJsonValue &val) -{ - QJsonObject obj = val.toObject(); - return QPointF(obj.value(QStringLiteral("x")).toDouble(), obj.value(QStringLiteral("y")).toDouble()); -} -{% if need_point3d %} -// QPoint3D -QJsonValue toJson(const LtsUtils::QPoint3D &element) -{ - QJsonObject obj; - obj.insert(QStringLiteral("x"), QJsonValue(element.x())); - obj.insert(QStringLiteral("y"), QJsonValue(element.y())); - obj.insert(QStringLiteral("z"), QJsonValue(element.z())); - return QJsonValue(obj); -} -template<> -LtsUtils::QPoint3D fromJson(const QJsonValue &val) -{ - QJsonObject obj = val.toObject(); - return LtsUtils::QPoint3D(obj.value(QStringLiteral("x")).toDouble(), obj.value(QStringLiteral("y")).toDouble(), obj.value(QStringLiteral("z")).toDouble()); -} -{% endif %} - -{% for cpp_struct in cpp_structs %} -// -// Struct: {{ cpp_struct.name }} -// -QJsonObject toJson(const {{ cpp_struct.name }} &element) -{ - QJsonObject obj; - {% for row in cpp_struct.jsonMember %} - {{ row.dbToJsonElement }}; - {% endfor %} - return obj; -} - -template<> -{{ cpp_struct.name }} fromJson(const QJsonValue &value) -{ - const QJsonObject objElement(value.toObject()); - {{ cpp_struct.name }} element; - - {% for row in cpp_struct.jsonMember %} - { {{ row.jsonElementToDb }}; } {% endfor %} - - return element; -} -// {{ cpp_struct.name }} end -{% endfor %} - -} // namespace Database -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_db_structs.h.grantlee b/Lts/Common/CodeGen/svc_db_structs.h.grantlee deleted file mode 100755 index 1782c0bd8..000000000 --- a/Lts/Common/CodeGen/svc_db_structs.h.grantlee +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for 'real' structures including insert/update/delete functions for db - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_db_structs.h -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -{% if need_point3d %} -#include -{% endif %} -{% if need_pgarray %} -#include -{% endif %} - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp> - -{% for ext_service in ext_services.member %} -#include <{{ ext_service.baseFilePath }}.hpp> -{% endfor %} - -namespace {{ namespace }} -{ -namespace Database -{ -{% for cpp_struct in cpp_structs %} - // {{ cpp_struct.name }} - struct {{ cpp_struct.name }} - { - {% for m in cpp_struct.cppMember %}{{ m.cppType }} {{ m.cppMemberName }}{% if m.cppDefault %} = {{ m.cppDefault }}{% endif %}; - {% endfor %} - };{% endfor %} - -{% for data_struct in data_structs %} - // read only struct {{ data_struct.name }} - struct {{ data_struct.name }} - { - {% for m in data_struct.cppMember %}{{ m.cppType }} {{ m.cppMemberName }}{% if m.cppDefault %} = {{ m.cppDefault }}{% endif %}; - {% endfor %} - };{% endfor %} - - // converting DbStructs to/from JSON - template - T fromJson(const QJsonValue &value); -{% for cpp_struct in cpp_structs %} - // {{ cpp_struct.name }} - template <> {{ namespace_abbr_upper }}_{{ library_target }}_EXPORT {{ cpp_struct.name }} fromJson(const QJsonValue &value); - {{ namespace_abbr_upper }}_{{ library_target }}_EXPORT QJsonObject toJson(const {{ cpp_struct.name }} &element);{% endfor %} - - // vector of database struct to/from JSON-array - template - QJsonArray toJson(const QVector &dataVec) - { - QJsonArray arr; - for (const T &data : dataVec) - arr.append(QJsonValue(toJson(data))); - - return arr; - } - - template - std::vector fromJson(const QJsonArray &arr) - { - std::vector dataVec; - dataVec.reserve(arr.size()); - for (const QJsonValue &val : arr) - dataVec.push_back(fromJson(val)); - - return dataVec; - } -};// namespace Database - -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dtc.h.grantlee b/Lts/Common/CodeGen/svc_dtc.h.grantlee deleted file mode 100755 index 4e4f87c7b..000000000 --- a/Lts/Common/CodeGen/svc_dtc.h.grantlee +++ /dev/null @@ -1,88 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Client Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Hanna E. Rathje, SLSE, 2016-01-04 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dtc.h -#pragma once - -#include -#include -#include -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp> - -namespace DDSConnector -{ -class DdsConnector; -} - -namespace {{ namespace }} -{ -namespace Interface -{ -class {{ namespace_abbr_upper }}_COMMON_EXPORT Dtc : public QObject -{ - Q_OBJECT -protected: - Dtc() = default; -public: - virtual ~Dtc() = default; - - /** - * Create a new instance for {{ namespace }}::Interface::Dtc. - */ - static std::shared_ptr create(const std::shared_ptr &ddsConnector); - - /** - * A unique identifier for this DTC - */ - static inline const char *identifier() { return "{{ namespace }}.Interface.Dtc"; } - - /** - * initialize this Dts instance with all data topic readers - */ - virtual bool enableAll(bool enable = true) = 0; - /** - * only initialize the specific reader (can be called instead enableAll()) - */ -{% for dt in dataTopics.member %} {% if dt.dtcImpl %} - virtual bool enable{{ dt.name }}(bool enable = true) = 0; - virtual bool is{{ dt.name }}Enabled() const = 0; - virtual bool waitForHistoricalData{{ dt.name }}(int timeoutMSec = -1) = 0; - {% endif %} {% endfor %} - - {% for cp in custom_dtc_procs %} - // custom procs, body must be implemented by user - virtual {{ cp.signature }} = 0; - {% endfor %} - - {% for dt in dataTopics.member %} -public: - // - // Data Topic '{{ dt.name }}' - // {% if dt.key.name %} - virtual QMap<{{ dt.key.cppType }}, {{ namespace }}::{{ dt.type }}> {{ dt.nameFlc }}s() = 0; - virtual bool {{ dt.nameFlc }}s(const std::function &cb) = 0; - // when the cb returns true, the loop is stopped (useful when searching for a specific topic) - virtual bool {{ dt.nameFlc }}s(const std::function &cb) = 0; - virtual {{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}) = 0; - {% else %} - virtual {{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}() = 0; - {% endif %} -Q_SIGNALS: - void {{ dt.nameFlc }}Changed(const {{ namespace }}::{{ dt.type }} &data, DataModification modType); - - {% endfor %} - -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee b/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee deleted file mode 100755 index 4a7caff62..000000000 --- a/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee +++ /dev/null @@ -1,304 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Client Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-04-07 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dtc_impl.cpp - -#include "{{ namespace_abbr }}_dtc_impl.h" -{% if custom_dtc_procs %} -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_impl_custom.h> -{% endif %} -#include -#include -#include -#include -#include -#include - -namespace {{ namespace }} -{ -namespace Interface -{ -// initializer function -std::shared_ptr Dtc::create(const std::shared_ptr &ddsConnector) -{ - // share only for main thread - if (qApp->thread() != QThread::currentThread()) - {% if custom_dtc_procs %} - return std::make_shared(ddsConnector); - {% else %} - return std::make_shared(ddsConnector); - {% endif %} - - static std::unordered_map> s_availableConnectors; - - auto it = s_availableConnectors.find(ddsConnector.get()); - if (it != s_availableConnectors.end()) - { - auto ret = it->second.lock(); - if (ret) - return ret; - s_availableConnectors.erase(it); - } - {% if custom_dtc_procs %} - auto ret = std::make_shared(ddsConnector); - {% else %} - auto ret = std::make_shared(ddsConnector); - {% endif %} - s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret)); - return std::move(ret); // c++11 defect -} - -// DtcImpl -DtcImpl::DtcImpl(const std::shared_ptr &ddsConnector) - : Dtc() - , m_ddsConnector(ddsConnector) - , m_logger(getLogger("Dtc")) -{} - -DtcImpl::~DtcImpl() -{ - enableAll(false); -} - -bool DtcImpl::enableAll(bool enable) -{ {% for dt in dataTopics.member %} {% if dt.dtcImpl %} {% if dt.autosubscribe %} - if (!enable{{ dt.name }}(enable)) - { - ERROR_LOG(m_logger) << "Error initializing data topic {{ dt.name }}"; - return false; - } - {% endif %} {% endif %} {% endfor %} - return true; -} - -{% for dt in dataTopics.member %} {% if dt.dtcImpl %} {% if dt.key.name %} -template <> -struct TopicConstraints<{{ dt.type }}> -{ - using KEY = {{ dt.key.cppType }}; -}; -{% endif %} {% endif %} {% endfor %} - - -template -bool DtcImpl::checkEnabled(const DTC &dtc) const -{ - if (!dtc) - { - using TOPIC = typename DtcConstraints::TOPIC ; - WARNING_LOG(m_logger) << "Error retrieving data topic " - << DDS::topic_type_name::value() << " - not enabled"; - Q_ASSERT(false); - return false; - } - return true; -} - -template -bool DtcImpl::waitForHistoricalData(DTC &dtc, int timeoutMSec) -{ - if (!checkEnabled(dtc)) - return false; - return dtc->waitForHistoricalData(timeoutMSec); -} - -template ::KEY> -K getKey(const T &val); - -template -QMap DtcImpl::getAll(const DTC &dtc) const -{ - if (!checkEnabled(dtc)) - return {}; - - QMap retMap; - for (const auto &sample : dtc->readAlive()) - retMap.insert(getKey(sample.data()), sample); - - return retMap; -} - -template -T DtcImpl::getOne(const DTC &dtc, const C &cont, const K &key) const -{ - if (!checkEnabled(dtc)) - return {}; - const auto it = cont.find(key); - return it == cont.end() ? T() : it.value(); -} - -template -bool DtcImpl::iterateAll(const DTC &dtc, const C &cont, const std::function &cb) const -{ - if (!checkEnabled(dtc)) - return false; - - for (const auto &sample : cont) - cb(sample); - - return true; -} - -template -bool DtcImpl::iterateUntil(const DTC &dtc, const C &cont, const std::function &cb) const -{ - if (!checkEnabled(dtc)) - return false; - - for (const auto &sample : cont) - { - if (cb(sample)) - return true; - } - return false; -} - -{% for dt in dataTopics.member %} {% if dt.dtcImpl %} -// -// Data Topic '{{ dt.name }}' -// -bool DtcImpl::enable{{ dt.name }}(bool enable) -{ - if (enable) - { - if (m_dtc{{ dt.name }}) - { - INFO_LOG(m_logger) << "{{ dt.name }} already enabled!"; - return true; - } - m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC()); - assert(m_dtc{{ dt.name }}.get()); - m_dtc{{ dt.name }}->setSubscriberCallback([this](const {{ namespace }}::{{ dt.name }} &data, DataModification modType) - { - {{ dt.nameFlc }}ChangedInternal(data, modType); - }); - } - else - { - m_dtc{{ dt.name }} = nullptr; - } - - return true; -} - -bool DtcImpl::waitForHistoricalData{{ dt.name }}(int timeoutMSec) -{ - - if (!is{{ dt.name }}Enabled()) - { - enable{{ dt.name }}(); - DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ; - } - - if (!waitForHistoricalData(m_dtc{{ dt.name }}, timeoutMSec)) - return false; -{% if dt.key.name %} - m_{{ dt.nameFlc }}Cont = getAll(m_dtc{{ dt.name }}); -{% else %} - auto samples = m_dtc{{ dt.name }}->readAlive(); - if (samples.length() == 0) - return false; - m_{{ dt.nameFlc }} = samples[samples.length() - 1].data(); -{% endif %} - return true; -} - -{% if dt.key.name %} -template <> -{{ dt.key.cppType }} getKey(const {{ dt.type }} &val) -{ - return val.{{ dt.key.name }}(); -} - -QMap<{{ dt.key.cppType }},{{ namespace }}::{{ dt.type }}> DtcImpl::{{ dt.nameFlc }}s() -{ - if (!is{{ dt.name }}Enabled()) - { - enable{{ dt.name }}(); - DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ; - waitForHistoricalData{{ dt.name }}(10); - } - return m_{{ dt.nameFlc }}Cont; -} - -bool DtcImpl::{{ dt.nameFlc }}s(const std::function &cb) -{ - if (!is{{ dt.name }}Enabled()) - { - enable{{ dt.name }}(); - DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ; - waitForHistoricalData{{ dt.name }}(10); - } - return iterateAll(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, cb); -} - -bool DtcImpl::{{ dt.nameFlc }}s(const std::function &cb) -{ - if (!is{{ dt.name }}Enabled()) - { - enable{{ dt.name }}(); - DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ; - waitForHistoricalData{{ dt.name }}(10); - } - return iterateUntil(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, cb); -} - -{{ namespace }}::{{ dt.type }} DtcImpl::{{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}) -{ - if (!is{{ dt.name }}Enabled()) - { - enable{{ dt.name }}(); - DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ; - waitForHistoricalData{{ dt.name }}(10); - } - return getOne(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, {{ dt.key.nameFlc }}); -} - -void DtcImpl::{{ dt.nameFlc }}ChangedInternal(const {{ namespace }}::{{ dt.type }} &data, DataModification modType) -{ - switch (modType) - { - case DataModification::eAdded: - case DataModification::eModified: - m_{{ dt.nameFlc }}Cont[getKey(data)] = data; - break; - case DataModification::eRemoved: - m_{{ dt.nameFlc }}Cont.remove(getKey(data)); - break; - } - emit {{ dt.nameFlc }}Changed(data, modType); -} - -{% else %} -{{ namespace }}::{{ dt.type }} DtcImpl::{{ dt.nameFlc }}() -{ - return m_{{ dt.nameFlc }}; -} -void DtcImpl::{{ dt.nameFlc }}ChangedInternal(const {{ namespace }}::{{ dt.type }} &data, DataModification modType) -{ - switch (modType) - { - case DataModification::eAdded: - case DataModification::eModified: - m_{{ dt.nameFlc }} = data; - break; - case DataModification::eRemoved: - m_{{ dt.nameFlc }} = {{ namespace }}::{{ dt.type }}(); - break; - } - emit {{ dt.nameFlc }}Changed(data, modType); -} - -{% endif %} {% endif %} {% endfor %} - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee b/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee deleted file mode 100755 index 73d6588ad..000000000 --- a/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee +++ /dev/null @@ -1,99 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Client Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-04-07 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dtc_impl.h -#pragma once - -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h> -#include <{{ include_prefix }}/logger.h> - -#include - -#if !defined(MAKE_SVC{{ namespace_abbr_upper }}CLIENT_LIB) -#error "You are not allowed to include this file in your code!" -#endif - -namespace {{ namespace }} -{ -namespace Interface -{ -template -struct DtcConstraints -{ - using DTC = typename UQ_DTC::element_type; - using TOPIC = typename DTC::TopicType; -}; - -template -struct TopicConstraints -{}; - -class DtcImpl {% if not custom_dtc_procs %}final {%endif %}: public Dtc -{ - Q_OBJECT -public: - explicit DtcImpl(const std::shared_ptr &ddsConnector); - ~DtcImpl(); - - bool enableAll(bool enable = true) override; - {% for dt in dataTopics.member %} {% if dt.dtcImpl %} - // - // Data Topic '{{ dt.name }}' - // -public: - bool enable{{ dt.name }}(bool enable = true) override final; - bool is{{ dt.name }}Enabled() const override final { return m_dtc{{ dt.name }} != nullptr; } - bool waitForHistoricalData{{ dt.name }}(int timeoutMSec = -1) override final; -{% if dt.key.name %} - QMap<{{ dt.key.cppType }}, {{ namespace }}::{{ dt.type }}> {{ dt.nameFlc }}s() override final; - bool {{ dt.nameFlc }}s(const std::function &cb) override final; - bool {{ dt.nameFlc }}s(const std::function &cb) override final; - {{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}) override final; -{% else %} - {{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}() override final; -{% endif %} {% endif %} {% endfor %} - -private: - template - bool checkEnabled(const DTC &dtc) const; - template - bool waitForHistoricalData(DTC &dtc, int timeoutMSec); - template ::TOPIC, - typename K = typename TopicConstraints::KEY> - QMap getAll(const DTC &dtc) const; - template ::TOPIC, - typename K = typename TopicConstraints::KEY> - T getOne(const DTC &dtc, const C &cont, const K &key) const; - template ::TOPIC> - bool iterateAll(const DTC &dtc, const C &cont, const std::function &cb) const; - template ::TOPIC> - bool iterateUntil(const DTC &dtc, const C &cont, const std::function &cb) const; - -protected: - std::shared_ptr m_ddsConnector; - -{% for dt in dataTopics.member %} -{% if dt.dtcImpl %} - void {{ dt.nameFlc }}ChangedInternal(const {{ namespace }}::{{ dt.type }} &data, DataModification modType); - std::unique_ptr m_dtc{{ dt.name }}; {% if dt.key.name %} - QMap<{{ dt.key.cppType }}, {{ namespace }}::{{ dt.type }}> m_{{ dt.nameFlc }}Cont; {% else %} - {{ namespace }}::{{ dt.type }} m_{{ dt.nameFlc }}; -{% endif %} {% endif %} {% endfor %} - - LOGGER_T m_logger; -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dts.h.grantlee b/Lts/Common/CodeGen/svc_dts.h.grantlee deleted file mode 100755 index 224a680ea..000000000 --- a/Lts/Common/CodeGen/svc_dts.h.grantlee +++ /dev/null @@ -1,69 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Server Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Hanna E. Rathje, SLSE, 2016-01-05 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dts.h -#pragma once - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp> - - -namespace DDSConnector -{ -class DdsConnector; -} - -namespace {{ namespace }} -{ -namespace Interface -{ -class Dts -{ -protected: - Dts() = default; -public: - virtual ~Dts() = default; - - /** - * Create a new instance for {{ namespace }}::Interface::Dts. - * The caller takes ownership of the returned instance - */ - {{ namespace_abbr_upper }}_SERVER_EXPORT static std::shared_ptr create(const std::shared_ptr &ddsConnector); - - /** - * A unique identifier for this DTS - */ - {{ namespace_abbr_upper }}_SERVER_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Dts"; } - - /** - * initialize this Dts instance with all data topic writers - */ - virtual bool enableAll(bool enable = true) = 0; - /** - * only initialize the specific writer (can be called instead enableAll()) - */ - {% for dt in dataTopics.member %} {% if dt.dtsImpl %} - virtual bool enable{{ dt.name }}(bool enable = true) = 0; - virtual bool is{{ dt.name }}Enabled() const = 0; - {% endif %} {% endfor %} - -{% for dt in dataTopics.member %} - // - // Data Topic '{{ dt.name }}' - // - virtual bool publish(const {{ namespace }}::{{ dt.type }} &data) = 0; - {% if dt.key.name %} - virtual bool remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists = true) = 0; - {% endif %} - {% endfor %} -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee b/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee deleted file mode 100755 index 251c037f7..000000000 --- a/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee +++ /dev/null @@ -1,100 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Server Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-04-07 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dts_impl.cpp -#include "{{ namespace_abbr }}_dts_impl.h" -#include - -namespace {{ namespace }} -{ -namespace Interface -{ -// initializer function -std::shared_ptr Dts::create(const std::shared_ptr &ddsConnector) -{ - return std::make_shared(ddsConnector); -} - -// DtsImpl -DtsImpl::DtsImpl(const std::shared_ptr &ddsConnector) - : Dts() - , m_ddsConnector(ddsConnector) - , m_logger(getLogger("Dts")) -{} - -DtsImpl::~DtsImpl() -{ - enableAll(false); -} - -bool DtsImpl::enableAll(bool enable) -{ - {% for dt in dataTopics.member %} {% if dt.dtsImpl %} - if (!enable{{ dt.name }}(enable)) - { - WARNING_LOG(m_logger) << "Error initializing data topic {{ dt.name }}"; - return false; - } - {% endif %} {% endfor %} - return true; -} - -{% for dt in dataTopics.member %} {% if dt.dtsImpl %} -// -// Data Topic '{{ dt.name }}' -// -bool DtsImpl::enable{{ dt.name }}(bool enable) -{ - if (enable) - { - if (m_dts{{ dt.name }}) - { - INFO_LOG(m_logger) << "{{ ft.name }} already enabled!"; - return true; - } - m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS()); - assert(m_dts{{ dt.name }}.get()); - } - else - { - m_dts{{ dt.name }} = nullptr; - } - return true; -} - -bool DtsImpl::publish(const {{ namespace }}::{{ dt.type }} &data) -{ - if (!m_dts{{ dt.name }}) - { - WARNING_LOG(m_logger) << "Error publishing data topic {{ dt.name }} - not enabled"; - Q_ASSERT(false); - return false; - } - return m_dts{{ dt.name }}->write(data); -} - -{% if dt.key.name %} -bool DtsImpl::remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists) -{ - if (!m_dts{{ dt.name }}) - { - WARNING_LOG(m_logger) << "Error removing data topic {{ dt.name }} - not enabled"; - Q_ASSERT(false); - return false; - } - {{ namespace }}::{{ dt.name }} data; - data.{{ dt.key.name }}({{ dt.key.name }}); - return m_dts{{ dt.name }}->remove(data, errorMsgIfSampleDoesNotExists); -} -{% endif %} -{% endif %} {% endfor %} - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_dts_impl.h.grantlee b/Lts/Common/CodeGen/svc_dts_impl.h.grantlee deleted file mode 100755 index 06852a4c7..000000000 --- a/Lts/Common/CodeGen/svc_dts_impl.h.grantlee +++ /dev/null @@ -1,58 +0,0 @@ -/** - * {{ namespace_abbr_upper }} Data Topic Server Interface - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-04-07 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_dts_impl.h -#pragma once - -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_dts.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_dts_types.h> -#include <{{ include_prefix }}/logger.h> - -#include - -#if !defined(MAKE_SVC{{ namespace_abbr_upper }}SERVER_LIB) -#error "You are not allowed to include this file in your code!" -#endif - -namespace {{ namespace }} -{ -namespace Interface -{ -class DtsImpl final : public Dts -{ -public: - explicit DtsImpl(const std::shared_ptr &ddsConnector); - ~DtsImpl(); - - bool enableAll(bool enable = true) override; - - {% for dt in dataTopics.member %} {% if dt.dtsImpl %} - // - // Data Topic '{{ dt.name }}' - // - bool enable{{ dt.name }}(bool enable = true) override; - bool is{{ dt.name }}Enabled() const override final { return m_dts{{ dt.name }} != nullptr; } - bool publish(const {{ namespace }}::{{ dt.type }} &data) override; - {% if dt.key.name %} - bool remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists = true) override; - {% endif %} {% endif %} {% endfor %} - -protected: - std::shared_ptr m_ddsConnector; - {% for dt in dataTopics.member %} {% if dt.dtsImpl %} - std::unique_ptr m_dts{{ dt.name }}; - {% endif %} {% endfor %} - LOGGER_T m_logger; -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_enums.idl.grantlee b/Lts/Common/CodeGen/svc_enums.idl.grantlee deleted file mode 100755 index 2ea8976a5..000000000 --- a/Lts/Common/CodeGen/svc_enums.idl.grantlee +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @RDE_HEADER@ - * Template for idl struct definitions - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Hanna E. Rathje, SLSE, 2015-08-19 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_enums.idl -#ifndef {{ namespace_abbr }}_enums_idl_included -#define {{ namespace_abbr }}_enums_idl_included - -//@copy #include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> // #define NDDSUSERDllExport for linux - - -module {{ namespace }} -{ - {% for idl_enum in idl_enums %} - enum {{ idl_enum.name }} - { - {% for idl_member in idl_enum.member %} - {{ idl_member.key }} = {{ idl_member.value }}{% if not forloop.last %},{% endif %} {% endfor %} - }; - - {% endfor %} - -}; // module {{ namespace }} - -#endif -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_ftc.h.grantlee b/Lts/Common/CodeGen/svc_ftc.h.grantlee deleted file mode 100755 index 20c31ae53..000000000 --- a/Lts/Common/CodeGen/svc_ftc.h.grantlee +++ /dev/null @@ -1,78 +0,0 @@ -/** - * {{ namespace }} Function Topic Client interface - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_ftc.h -#pragma once - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp> -#include - - -namespace DDSConnector -{ -class DdsConnector; -} - -namespace {{ namespace }} -{ -namespace Interface -{ -class Ftc -{ -protected: - Ftc() = default; -public: - virtual ~Ftc() = default; - - /** - * Create a new instance for {{ namespace }}::Interface::Ftc. - * The caller takes ownership of the returned instance! - */ - {{ namespace_abbr_upper }}_CLIENT_EXPORT static std::shared_ptr create(const std::shared_ptr &ddsConnector, bool forceCreate = false); - - /** - * A unique identifier for this FTC - */ - {{ namespace_abbr_upper }}_CLIENT_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Ftc"; } - - /** - * initialize this Ftc instance with all function topic clients - */ - virtual bool enableAll(bool enable = true) = 0; - /** - * only initialize the specific function topic client (can be called instead enableAll()) - */ - {% for ft in functionTopics.member %} - virtual bool enable{{ ft.name }}(bool enable = true) = 0; - virtual bool is{{ ft.name }}Enabled() const = 0; - {% endfor %} - -{% for ft in functionTopics.member %} - // - // Function Topic '{{ ft.name }}' - // - class {{ ft.replyHandler }} - { - public: - {{ ft.replyHandler }}() = default; - virtual ~{{ ft.replyHandler }}() = default; - virtual void {{ ft.onReply }}(const {{ ft.replyTypeFro }} &reply) = 0; - }; - virtual int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler) = 0; - virtual int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function &cb) = 0; - virtual {% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} {{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false) = 0; // await - virtual int64_t last{{ ft.request }}UniqueId() const = 0; - - {% endfor %} -}; - - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee b/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee deleted file mode 100755 index c90871ccd..000000000 --- a/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee +++ /dev/null @@ -1,254 +0,0 @@ -/** - * {{ namespace }} Function Topic Client implementation - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_ftc_impl.cpp -#include "{{ namespace_abbr }}_ftc_impl.h" - -#include -#include - -#include -#include - -#include -#include - - -//using namespace std::literals::chrono_literals; - - -namespace {{ namespace }} -{ -namespace Interface -{ -// initializer function -Q_DECL_EXPORT std::shared_ptr Ftc::create(const std::shared_ptr &ddsConnector, bool forceCreate) -{ - // share only for main thread - if (qApp->thread() != QThread::currentThread() || forceCreate) - return std::make_shared(ddsConnector); - - static std::unordered_map> s_availableConnectors; - - auto it = s_availableConnectors.find(ddsConnector.get()); - if (it != s_availableConnectors.end()) - { - auto ret = it->second.lock(); - if (ret) - return ret; - s_availableConnectors.erase(it); - } - - auto ret = std::make_shared(ddsConnector); - s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret)); - return std::move(ret); // c++11 defect -} - -// FtcImpl -FtcImpl::FtcImpl(const std::shared_ptr &ddsConnector) - : Ftc() - , m_ddsConnector(ddsConnector) - , m_logger(getLogger("Ftc")) -{} - -bool FtcImpl::enableAll(bool enable) -{ {% for ft in functionTopics.member %} - if (!enable{{ ft.name }}(enable)) - { - ERROR_LOG(m_logger) << "Error initializing function topic {{ ft.name }}"; - return false; - } - {% endfor %} - return true; -} - -FtcImpl::~FtcImpl() -{ - enableAll(false); -} - -{% for ft in functionTopics.member %} -// Function Topic '{{ ft.name }}' -bool FtcImpl::enable{{ ft.name }}(bool enable) -{ - if (enable) - { - if (m_ftc{{ ft.name }}) - { - INFO_LOG(m_logger) << "{{ ft.name }} already enabled!"; - return true; - } - m_ftc{{ ft.name }}.reset( - m_ddsConnector->createFTC(this)); - assert(m_ftc{{ ft.name }}.get()); - } - else - { - m_ftc{{ ft.name }} = nullptr; - } - - return true; -} -{% endfor %} - -template -static T buildErrorReason(int reason, int requestId) -{ - std::string errString; - std::vector errArgs; - switch (reason) - { - case 1: - errString = tr_lts("dds", "Service could not handle request"); - break; - case 3: - errString = tr_lts("dds", "Timeout while waiting for answer from server"); - break; - default: - errString = tr_lts("dds", "Unknown error reason: %1"); - errArgs.push_back(std::to_string(reason)); - break; - } - - T reply; - reply.id(requestId); - reply.errorString(errString); - reply.errorParams(errArgs); - return reply; -} - -{% for ft in functionTopics.member %} -// -// {{ ft.name }} -// -int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler) -{ - return {{ ft.requestFlc }}(request, [ handler ](const {{ ft.replyTypeFro }} &reply) { if (handler) handler->{{ ft.onReply }}(reply); }); -} - -int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function &cb) -{ - if (!m_ftc{{ ft.name }}) - { - WARNING_LOG(m_logger) << "Error requesting function topic {{ ft.name }} - not enabled"; - Q_ASSERT(false); - return 0; - } - - static const char * const slotReply = SLOT({{ ft.onReply }}({{ namespace }}::{{ ft.reply }},int,bool)); - static const char * const slotError = SLOT({{ ft.onError }}(int,int,int)); - int uniqueId = 0; - if (!m_ftc{{ ft.name }}->requestDataClassic(request, slotReply, slotError, uniqueId)) - return 0; - - m_{{ ft.replyHandlerFlc }}[uniqueId] = cb; - // first 16 hex bytes from Sha3_512 hash for '{{ ft.name }}' converted to an int64 - m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }}; - return m_last{{ ft.request }}UniqueId; -} - -// NOTE: do not use if you call an FTC which returns an id while also containing an error as "error doesn't mean that the service might not reply with an id - so always take the id" -{% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false) -{ - if (!m_ftc{{ ft.name }}) - { - Q_ASSERT(false); - throw std::runtime_error("Error requesting function topic {{ ft.name }} - not enabled"); - } - - auto *requestWatcher = new Request2(); - auto future = m_ftc{{ ft.name }}->requestDataModern(request, requestWatcher); - - m_currentlyOpen_{{ ft.requestFlc }}Requests++; - //fprintf(stderr, "{{ ft.requestFlc }}: waiting for reply... (currently open requests: %d)\n", m_currentlyOpen_{{ ft.requestFlc }}Requests); - LtsUtils::await(requestWatcher, &Request2::finished, true); // blocks until request has reply or failed - m_currentlyOpen_{{ ft.requestFlc }}Requests--; - //fprintf(stderr, "{{ ft.requestFlc }}: request done!\n"); - - // the generic ftc impl's 'requestDataModern' + 'exec()' guarantees that calling 'future.get()' will not result in a 'wait()' call by 'get()' - but we should still check! - switch (future.wait_for(std::chrono::seconds(1))) - { - case std::future_status::deferred: - { - Q_ASSERT(false); - // fprintf(stderr, "{{ ft.requestFlc }}: deferred...\n"); - break; - } - case std::future_status::timeout: - { - Q_ASSERT(false); - // fprintf(stderr, "{{ ft.requestFlc }}: future timeout!\n"); - throw std::runtime_error("Future timeout!"); - } - case std::future_status::ready: - { - // fprintf(stderr, "{{ ft.requestFlc }}: ready\n"); - break; - } - } - -{% if ft.isCoreFt %} - const auto _reply = future.get(); // throws if exception set - - Core::FunctionReplyObject fro( - {% for rp in ft.replyParams %} _reply.{{ rp.nameGetter }}(){% if not forloop.last %},{% endif %} {% endfor %} - ); - fro.setUniqueId(m_last{{ ft.request }}UniqueId); - - if (fro.error()) - { - ERROR_LOG(m_logger) << "FTC call failed: " << fro.translated(); - throw std::runtime_error(fro.translated().toStdString().c_str()); - } - - const auto reply = fro.id(); -{% else %} - const auto reply = future.get(); // throws if exception set -{% endif %} - - return reply; -} - -void FtcImpl::{{ ft.onReply }}(const {{ namespace }}::{{ ft.reply }} &reply, int uniqueId, bool bRequestFinished) -{ - auto it = m_{{ ft.replyHandlerFlc }}.find(uniqueId); - if (it == m_{{ ft.replyHandlerFlc }}.end()) - { - WARNING_LOG(m_logger) << "Error replying function topic {{ ft.name }} - no replyHandler"; - Q_ASSERT(false); - return; - } - - auto cb = it.value(); - if (bRequestFinished) - m_{{ ft.replyHandlerFlc }}.erase(it); - if (!cb) - return; - - m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }}; -{% if ft.isCoreFt %} - Core::FunctionReplyObject fro( - {% for rp in ft.replyParams %} - reply.{{ rp.nameGetter }}(){% if not forloop.last %},{% endif %} {% endfor %} - ); - fro.setUniqueId(m_last{{ ft.request }}UniqueId); - cb(fro); -{% else %} - cb(reply); -{% endif %} -} - -void FtcImpl::{{ ft.onError }}(int reason, int uniqueId, int requestId) -{ - {{ ft.onReply }}(buildErrorReason<{{ namespace }}::{{ ft.reply }}>(reason, requestId), uniqueId, true); -} -{% endfor %} - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee b/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee deleted file mode 100755 index ce1afe2cf..000000000 --- a/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee +++ /dev/null @@ -1,81 +0,0 @@ -/** - * {{ namespace }} Function Topic Client implementation - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_ftc_impl.h -#pragma once -#include <{{ include_prefix }}/{{ namespace_abbr }}_ftc.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_ftc_types.h> -#include <{{ include_prefix }}/logger.h> - -#include -#include -#include -#include - -#include - - -#if !defined(MAKE_SVC{{ namespace_abbr_upper }}CLIENT_LIB) -#error "You are not allowed to include this file in your code!" -#endif - -namespace {{ namespace }} -{ -namespace Interface -{ -class FtcImpl final : public QObject, public Ftc -{ - Q_OBJECT -public: - explicit FtcImpl(const std::shared_ptr &ddsConnector); - ~FtcImpl(); - - bool enableAll(bool enable = true) override; -{% for ft in functionTopics.member %} - bool enable{{ ft.name }}(bool enable = true) override; - bool is{{ ft.name }}Enabled() const override { return m_ftc{{ ft.name }} != nullptr; } -{% endfor %} - -{% for ft in functionTopics.member %} -// -// Function Topic '{{ ft.name }}' -// - -/// Classic request: -public: - int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler) override; // returns the uniqueId used for this request - int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function &cb) override; // returns the uniqueId used for this request - int64_t last{{ ft.request }}UniqueId() const override { return m_last{{ ft.request }}UniqueId; } - -private Q_SLOTS: - void {{ ft.onReply }}(const {{ namespace }}::{{ ft.reply }} &reply, int uniqueId, bool bRequestFinished); - void {{ ft.onError }}(int reason, int uniqueId, int requestId); - -private: - QHash> m_{{ ft.replyHandlerFlc }}; - int64_t m_last{{ ft.request }}UniqueId = -1; - -/// Modern request: -public: - /* Executes the request and blocks (enters Qt's event loop) until the reply has arrived. - * NOTE: If reply has a FunctionReplyWithState, this function only returns the first reply and returns. - {% if ft.isCoreFt %} * Returns the id of the FunctionReply.{% endif %}*/ - {% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} {{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false) override; - -private: - int m_currentlyOpen_{{ ft.requestFlc }}Requests { 0 }; - std::unique_ptr m_ftc{{ ft.name }}; -{% endfor %} - -private: - std::shared_ptr m_ddsConnector; - LOGGER_T m_logger; -}; -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_fts.h.grantlee b/Lts/Common/CodeGen/svc_fts.h.grantlee deleted file mode 100755 index aa5752fe7..000000000 --- a/Lts/Common/CodeGen/svc_fts.h.grantlee +++ /dev/null @@ -1,90 +0,0 @@ -/** - * {{ namespace }} Function Topic Server interface - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_fts.h -#pragma once - -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp> -#include - -namespace DDSConnector -{ -class DdsConnector; -} - -namespace {{ namespace }} -{ -namespace Interface -{ -class Fts -{ -protected: - Fts() = default; -public: - virtual ~Fts() = default; - - /** - * Create a new instance for {{ namespace }}::Interface::Dts. - * The caller takes ownership of the returned instance - */ - {{ namespace_abbr_upper }}_SERVER_EXPORT static std::shared_ptr create(const std::shared_ptr &ddsConnector); - - /** - * A unique identifier for this FTC - */ - {{ namespace_abbr_upper }}_SERVER_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Fts"; } - - /** - * initialize this Fts instance with all function topic servers - */ - virtual bool enableAll(bool enable = true) = 0; - /** - * only initialize the specific function topic writer (can be called instead enableAll()) - */ - {% for ft in functionTopics.member %} - virtual bool enable{{ ft.name }}(bool enable = true) = 0; - virtual bool is{{ ft.name }}Enabled() const = 0; - {% endfor %} - -{% for ft in functionTopics.member %} - // - // Function Topic '{{ ft.name }}' - // - class {{ ft.requestHandler }} - { - public: - explicit {{ ft.requestHandler }}(const std::shared_ptr &fts) - : m_fts(fts) - { - if (fts) - fts->set{{ ft.requestHandler }}(this); - } - virtual ~{{ ft.requestHandler }}() - { - auto sp = m_fts.lock(); - if (sp) - sp->set{{ ft.requestHandler }}(nullptr); - } - virtual void {{ ft.onRequest }}(const rti::core::SampleIdentity &cookie, - const {{ ft.requestType }} &request) = 0; - private: - std::weak_ptr m_fts; - }; - virtual void set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler) = 0; - virtual bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply) = 0; - {% if ft.isCoreFt %} - virtual bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject) = 0; - {% endif %} - -{% endfor %} -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee b/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee deleted file mode 100755 index 884d063c9..000000000 --- a/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee +++ /dev/null @@ -1,114 +0,0 @@ -/** - * {{ namespace }} Function Topic Server implementation - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_fts_impl.cpp -#include "{{ namespace_abbr }}_fts_impl.h" -#include - -namespace {{ namespace }} -{ -namespace Interface -{ -// initializer function -std::shared_ptr Fts::create(const std::shared_ptr &ddsConnector) -{ - return std::make_shared(ddsConnector); -} - -// FtsImpl -FtsImpl::FtsImpl(const std::shared_ptr &ddsConnector) - : Fts() - , m_ddsConnector(ddsConnector) - , m_logger(getLogger("Fts")) -{} - -FtsImpl::~FtsImpl() -{ - enableAll(false); -} - -bool FtsImpl::enableAll(bool enable) -{ - DEBUG_LOG(m_logger) << "FtsImpl::enableAll(" << (enable ? "true" : "false") << ")"; - {% for ft in functionTopics.member %} - if (!enable{{ ft.name }}(enable)) - { - ERROR_LOG(m_logger) << "Error initializing function topic {{ ft.name }}"; - return false; - } - {% endfor %} - return true; -} - -{% for ft in functionTopics.member %} -// -// Function Topic '{{ ft.name }}' -// -bool FtsImpl::enable{{ ft.name }}(bool enable) -{ - if (enable) - { - if (m_fts{{ ft.name }}) - { - INFO_LOG(m_logger) << "{{ ft.name }} already enabled!"; - return true; - } - m_fts{{ ft.name }}.reset(m_ddsConnector->createFTS()); - assert(m_fts{{ ft.name }}.get()); - m_fts{{ ft.name }}->setSubscriberCallback([this] (const rti::core::SampleIdentity &identity, const {{ namespace }}::{{ ft.request }} &request) { this->{{ ft.onRequest }}(identity, request); }); - } - else - { - m_fts{{ ft.name }}.reset(); - } - return true; -} - -bool FtsImpl::{{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply) -{ - if (!m_fts{{ ft.name }}) - { - WARNING_LOG(m_logger) << "Error publishing function topic {{ dt.name }} - not enabled"; - Q_ASSERT(false); - return false; - } - - return m_fts{{ ft.name }}->sendReply(cookie, reply); -} -{% if ft.isCoreFt %} -bool FtsImpl::{{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject) -{ - return {{ ft.replyFlc }}(cookie, {{ ft.replyType }}(replyObject.id(), replyObject.str(), replyObject.args(), replyObject.additionalInfo())); -} -{% endif %} - - -void FtsImpl::set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler) -{ - m_{{ ft.requestHandlerFlc }} = handler; -} - -void FtsImpl::{{ ft.onRequest }}(const rti::core::SampleIdentity &cookie, - const {{ namespace }}::{{ ft.request }} &request) -{ - DEBUG_LOG(m_logger) << "{{ ft.onRequest }}"; - if (!m_{{ ft.requestHandlerFlc }}) - { - WARNING_LOG(m_logger) << "Incoming {{ ft.request }} without registered handler!"; - Q_ASSERT(false); - return; - } - - m_{{ ft.requestHandlerFlc }}->{{ ft.onRequest }}(cookie, request); -} - -{% endfor %} - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_fts_impl.h.grantlee b/Lts/Common/CodeGen/svc_fts_impl.h.grantlee deleted file mode 100755 index 0b0adcb13..000000000 --- a/Lts/Common/CodeGen/svc_fts_impl.h.grantlee +++ /dev/null @@ -1,66 +0,0 @@ -/** - * {{ namespace }} Function Topic Server implementation - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_fts_impl.h -#pragma once -#include <{{ include_prefix }}/{{ namespace_abbr }}_fts.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_fts_types.h> -#include <{{ include_prefix }}/logger.h> - -#include -#include - -#if !defined(MAKE_SVC{{ namespace_abbr_upper }}SERVER_LIB) -#error "You are not allowed to include this file in your code!" -#endif - -namespace {{ namespace }} -{ -namespace Interface -{ -class FtsImpl final : public QObject, public Fts -{ - Q_OBJECT -public: - explicit FtsImpl(const std::shared_ptr &ddsConnector); - ~FtsImpl(); - - bool enableAll(bool enable = true) override; - -{% for ft in functionTopics.member %} - // - // {{ ft.name }} - // -public: - bool enable{{ ft.name }}(bool enable = true) override; - bool is{{ ft.name }}Enabled() const override { return m_fts{{ ft.name }} != nullptr && m_{{ ft.requestHandlerFlc }} != nullptr; } - bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply) override; - {% if ft.isCoreFt %} - bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject) override; - {% endif %} - - void set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler) override; - -private Q_SLOTS: - void {{ ft.onRequest }}(const rti::core::SampleIdentity &cookie, - const {{ namespace }}::{{ ft.request }} &request); -private: - std::unique_ptr m_fts{{ ft.name }}; - {{ ft.requestHandler }} *m_{{ ft.requestHandlerFlc }} = nullptr; - -{% endfor %} - -private: - - std::shared_ptr m_ddsConnector; - LOGGER_T m_logger; -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_json.cpp.grantlee b/Lts/Common/CodeGen/svc_json.cpp.grantlee deleted file mode 100755 index 7b13e8f3f..000000000 --- a/Lts/Common/CodeGen/svc_json.cpp.grantlee +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Json <-> DDS conversion routines - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2017-11-30 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_json.cpp - -#include "{{ namespace_abbr }}_json.h" -#include -#include -#include -#include -#include - -namespace {{ namespace }} -{ -template<> -int fromJson(const QJsonValue &val, type) -{ - return val.toInt(); -} - -template<> -double fromJson(const QJsonValue &val, type) -{ - return val.toDouble(); -} - -template<> -qint64 fromJson(const QJsonValue &val, type) -{ - return val.toString().toLongLong(); -} - -QJsonValue toJson(int vec) -{ - return QJsonValue(vec); -} -QJsonValue toJson(double vec) -{ - return QJsonValue(vec); -} -QJsonValue toJson(qint64 vec) -{ - return QJsonValue(QString::number(vec)); -} - -// std::string -QJsonValue toJson(const std::string &element) -{ - return QJsonValue(QString::fromStdString(element)); -} -template<> -std::string fromJson(const QJsonValue &val, type) -{ - return val.toString().toStdString(); -} - -// std::vector -template -QJsonArray toJson(const std::vector &vec) -{ - QJsonArray arr; - for (const auto &element : vec) - arr.push_back(toJson(element)); - return arr; -} - -template -std::vector fromJson(const QJsonValue &val, type>) -{ - std::vector ret; - if (!val.isArray()) - return ret; - const QJsonArray array(val.toArray()); - ret.reserve(array.size()); - for(int i = 0; i < array.size(); ++i) - ret.push_back(fromJson(array.at(i), type{})); - return ret; -} - -// dds::core:vector (bytestream) -inline QJsonValue toJson(const std::vector &element) -{ - return QJsonValue(QString::fromLatin1(QByteArray::fromRawData(reinterpret_cast(&element.at(0)), - static_cast(element.size())).toBase64())); -} -template<> -std::vector fromJson(const QJsonValue &val, type>) -{ - const QByteArray ba(QByteArray::fromBase64(val.toString().toLatin1())); - return LtsUtils::convert>(ba); -} - -{% for dt in jsonTopics %} -// {{ dt.name }} -QJsonObject toJson(const {{ dt.name }} &element) -{ - QJsonObject obj; - {% for row in dt.cppMember %} - {{ row.ddsToJsonElement }}; - {% endfor %} - return obj; -} - -template<> -{{ dt.name }} fromJson(const QJsonValue &value, type<{{ dt.name }}>) -{ - const QJsonObject objElement(value.toObject()); - {{ dt.name }} element; - - {% for row in dt.cppMember %} - { {{ row.jsonElementToDds }}; } {% endfor %} - - return element; -} - -// -// extensions to support sequence of seqence of Non-POD : -// - -QJsonValue toJson(const std::vector <{{ dt.name }} > &element) -{ - - if (element.size() == 0) - return QJsonValue(); - QJsonArray array; - for(size_t i = 0; i < element.size(); ++i) - array.push_back(toJson(element.at(i))); - - return array; -} - -template<> -std::vector<{{ dt.name }}> fromJson(const QJsonValue &value, type< std::vector <{{ dt.name }} > >) -{ - std::vector < {{ dt.name }} > ret; - if (value.isNull()) - return ret; - if (!value.isArray()) - return ret; - - const QJsonArray array = value.toArray(); - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type<{{ dt.name }}>{})); - return ret; -} - - - -{% endfor %} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_json.h.grantlee b/Lts/Common/CodeGen/svc_json.h.grantlee deleted file mode 100755 index b59958565..000000000 --- a/Lts/Common/CodeGen/svc_json.h.grantlee +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Json <-> DDS conversion routines - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2017-11-30 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_json.h -#pragma once - -#include "{{ namespace_abbr }}.hpp" -#include "{{ namespace_abbr }}_export.h" - -#include -#include - -namespace {{ namespace }} -{ -template -struct type{}; - -template -T fromJson(const QJsonValue &val, type); - -template -T fromJson(const QJsonValue &val) -{ - return fromJson(val, type{}); -} - -{% for dt in jsonTopics %} -// {{ dt.name }} -{{ namespace_abbr_upper }}_COMMON_EXPORT QJsonObject toJson(const {{ dt.name }} &element); -template<> {{ namespace_abbr_upper }}_COMMON_EXPORT {{ dt.name }} fromJson(const QJsonValue &val, type<{{ dt.name }}>); -{{ namespace_abbr_upper }}_COMMON_EXPORT QJsonValue toJson(const std::vector< {{ dt.name }} > &element); -template<> {{ namespace_abbr_upper }}_COMMON_EXPORT std::vector<{{ dt.name }}> fromJson(const QJsonValue &value, type< std::vector< {{ dt.name }} > >); - -{% endfor %} - - -// -// extensions to support sequence of seqence of Non-POD : -// - - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee b/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee deleted file mode 100755 index 42cb88a65..000000000 --- a/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee +++ /dev/null @@ -1,876 +0,0 @@ -/** - * RecordReplay Plugin - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-07-21 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_record_replay_plugin.cpp - -#include "{{ namespace_abbr }}_record_replay_plugin.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace {{ namespace }} -{ - -template -struct type{}; - -// conversion DDS -> QVariant, encapsulate complex members into json -template -QJsonValue toJson(const T &val); - -template -QVariant toQVariant(const T &val) -{ - return QVariant::fromValue(val); -} - -template<> -QJsonValue toJson(const int &vec) -{ - return QJsonValue(vec); -} -template<> -QJsonValue toJson(const double &vec) -{ - return QJsonValue(vec); -} -template<> -QJsonValue toJson(const qint64 &val) -{ - return QJsonValue(QString::number(val)); -} - -template<> -QJsonValue toJson(const std::vector &val) -{ - if (val.size() == 0) - return QJsonValue(); - QJsonArray array; - for(size_t i = 0; i < val.size(); ++i) - array.push_back(toJson(val.at(i))); - - return QJsonValue(array); -} - - -template -QVariant toQVariant(const dds::core::safe_enum &val) -{ - // do not use T here - otherwise the implicit ctor dds::core::safe_enum() is used which leads to infinite recursion - return toQVariant(val.underlying()); -} - -template<> -QJsonValue toJson(const std::string &element) -{ - return QJsonValue(QString::fromStdString(element)); -} - -template<> -QVariant toQVariant(const std::string &val) -{ - return QString::fromStdString(val); -} - -template -QVariant toQVariant(const std::vector &data) -{ - if (data.size() == 0) - return QString(); - QJsonArray array; - for(size_t i = 0; i < data.size(); ++i) - array.push_back(toJson(data.at(i))); - - return QString::fromUtf8(QJsonDocument(array).toJson()); -} - -template<> -QVariant toQVariant(const std::vector &data) -{ - if (data.size() == 0) - return QVariant(); - return QVariant(LtsUtils::convert(data)); -} - -template -static QVariant toQVariant(const dds::core::optional &val) -{ - if (val.is_set()) - return toQVariant(val.get()); - return QVariant(); -} - -// conversion QVariant -> DDS, decode complex members from json -template -T fromJson(const QJsonValue &val, type); - -template<> -int fromJson(const QJsonValue &val, type) -{ - return val.toInt(); -} -template<> -double fromJson(const QJsonValue &val, type) -{ - return val.toDouble(); -} -template<> -qint64 fromJson(const QJsonValue &val, type) -{ - return val.toString().toLongLong(); -} -/* -template<> -std::vector fromJson(const QJsonValue &val, type>) -{ - std::vector ret; - if (val.isNull()) - return ret; - const QJsonArray array(QJsonDocument::fromJson(val.toString().toUtf8()).array()); - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type{} )); - return ret; -} -*/ - -template -std::vector fromJson(const QJsonValue &jsonData, type>) -{ - std::vector ret; - if (jsonData.isNull()) - return ret; - const QJsonArray array(jsonData.toArray()); - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type{})); - return ret; -} - -template -std::vector fromJson(const QVariant &jsonData, type>) -{ - std::vector ret; - if (jsonData.isNull()) - return ret; - const QJsonArray array(QJsonDocument::fromJson(jsonData.toString().toUtf8()).array()); // warning: ugly and didn't work in the fromJson version above - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type{})); - return ret; -} - -template -T fromQVariant(const QVariant &val, type); - -template<> -bool fromQVariant(const QVariant &val, type) -{ - return val.toBool(); -} - -template<> -short fromQVariant(const QVariant &val, type) -{ - return static_cast(val.toInt()); -} - -template<> -int fromQVariant(const QVariant &val, type) -{ - return val.toInt(); -} - -template<> -long long fromQVariant(const QVariant &val, type) -{ - return val.toLongLong(); -} - -template<> -double fromQVariant(const QVariant &val, type) -{ - return val.toDouble(); -} - -template<> -std::string fromJson(const QJsonValue &val, type) -{ - return val.toString().toStdString(); -} - -template<> -std::string fromQVariant(const QVariant &val, type) -{ - return val.toString().toStdString(); -} - -template -dds::core::safe_enum fromQVariant(const QVariant &val, type>) -{ - using inner = typename dds::core::safe_enum::inner_enum; - return static_cast(val.toInt()); -} - -template -std::vector fromQVariant(const QVariant &val, type> t) -{ - return fromJson(val, t); -} - -template<> -std::vector fromQVariant(const QVariant &in, type>) -{ - return LtsUtils::convert>(in.toByteArray()); -} - -template -static dds::core::optional fromQVariant(const QVariant &val, type>) -{ - if (val.isNull()) - return dds::core::optional(); - return fromQVariant(val, type{}); -} - -template -T fromJson(const QJsonValue &val) -{ - return fromJson(val, type{}); -} - -template -T fromQVariant(const QVariant &val) -{ - return fromQVariant(val, type{}); -} - -{% for dt in serializeTopics.member %} -// -// Begin {{ dt.typeFq }} -// -template<> -{{ dt.typeFq }} fromJson(const QJsonValue &val, type<{{ dt.typeFq }}>) -{ - const QJsonObject objElement(val.toObject()); - {{ dt.typeFq }} element; - {% for row in dt.recTable.cppMember %} - { {{ row.jsonElementToDds }}; } {% endfor %} - - return element; -} -template<> -QJsonValue toJson(const {{ dt.typeFq }} &element) -{ - QJsonObject obj; - {% for row in dt.recTable.cppMember %} - {{ row.ddsToJsonElement }}; {% endfor %} - - return obj; -} - -template <> -QVariant toQVariant(const {{ dt.typeFq }} &val) -{ - return QString::fromUtf8(QJsonDocument(toJson(val).toObject()).toJson()); -} - -template<> -{{ dt.typeFq }} fromQVariant(const QVariant &val, type<{{ dt.typeFq }}>) -{ - return fromJson(QJsonDocument::fromJson(val.toString().toUtf8()).object(), type<{{ dt.typeFq }}>{}); -} - -// -// extensions to support sequence of seqence of Non-POD : -// - -template<> -QJsonValue toJson(const std::vector < {{ dt.typeFq }} > &data) -{ - if (data.size() == 0) - return QJsonValue(); - QJsonArray array; - for(size_t i = 0; i < data.size(); ++i) - array.push_back(toJson(data.at(i))); - - return array; -} - -template<> -std::vector < {{ dt.typeFq }} > fromJson(const QJsonValue &val) -{ - std::vector < {{ dt.typeFq }} > ret; - if (val.isNull()) - return ret; - if (!val.isArray()) - return ret; - const QJsonArray array = val.toArray(); - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type<{{ dt.typeFq }}>{})); - return ret; -} -// -// : extensions to support sequence of seqence of Non-POD -// - - -// -// End {{ dt.typeFq }} -// -{% endfor %} - - - - -RecordReplayPlugin::RecordReplayPlugin() - : m_exerciseId(0) - , m_bCanHandleSchemasRead(true) - , m_bCanHandleSchemasWrite(true) - , m_logger(getLogger("RecordReplay")) -{} - -RecordReplayPlugin::~RecordReplayPlugin() = default; - -QString RecordReplayPlugin::getTableName(const QString &tableName, bool bCanHandleSchemas) const -{ - if (!bCanHandleSchemas) - return tableName; - return DBCommon::getExerciseTableName(tableName, m_exerciseId); -} - -bool RecordReplayPlugin::initDtc(const std::shared_ptr &ddsConnector, - int exerciseId) -{ - m_ddsConnector = ddsConnector; - m_exerciseId = exerciseId; - if (!m_ddsConnector) - return false; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC()); -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::initDts(const std::shared_ptr &ddsConnector, - int exerciseId, - bool bCanHandleSchemas, - NSqlQuery &query) -{ - Q_UNUSED(query); - m_ddsConnector = ddsConnector; - m_exerciseId = exerciseId; - m_bCanHandleSchemasRead = bCanHandleSchemas; - m_bCanHandleSchemasWrite = bCanHandleSchemas; - if (!m_ddsConnector) - return false; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS()); -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite) -{ - m_exerciseId = exerciseId; - m_bCanHandleSchemasRead = bCanHandleSchemasRead; - m_bCanHandleSchemasWrite = bCanHandleSchemasWrite; - return true; -} - -bool RecordReplayPlugin::copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) -{ - {% for dt in dataTopics.member %} {% if dt.recordTopic %} - // {{ dt.name }} - if (!copyInitialData{{ dt.name }}(queryFrom, queryTo, fromTime)) return false; - if (!copyData{{ dt.name }}(queryFrom, queryTo, fromTime, toTime)) return false; - {% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::ddsTopicsToDatabase(NSqlQuery &q, QHash &minRecordSampleTimes) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!write{{ dt.name }}(q, minRecordSampleTimes)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::setReplayStartTime(NSqlQuery &q, qint64 startTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!read{{ dt.name }}At(q, startTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!read{{ dt.name }}(q, fromTime, toTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::publishData(qint64 desiredTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!publish{{ dt.name }}(desiredTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -void RecordReplayPlugin::clearData() -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - clearData{{ dt.name }}(); -{% endif %} {% endfor %} -} - -std::map> RecordReplayPlugin::getSnapshotTimes(NSqlQuery &q, - qint64 toTime, - qint64 interval, - const QHash &minRecordedSampleTimes) -{ - std::map> ret; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - { - const QString &topicName(QStringLiteral("{{ dt.name }}")); - std::vector vec; - auto it = minRecordedSampleTimes.find(topicName); - if (it != minRecordedSampleTimes.end()) - { - qint64 nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, it.value()); // is 0 or -1 if no samples are available - nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging - - if (nextSampleTime != m_snapshot{{ dt.name }}MapTime) - { - // the current snapshot data does not match the starting time -> reread m_{{ dt.nameFlc }}Keys - readLastSnapshotKeysFor{{ dt.name }}(q, nextSampleTime); - //DEBUG_LOG(m_logger) << "fromTime: " << fromTime << ", m_snapshotParticipantMapTime: " << m_snapshotParticipantMapTime - // << " - rereading snapshot times! Map now contains " << m_snapshot{{ dt.name }}Map.size() << " entries"; - } - - while (nextSampleTime > 0 && nextSampleTime <= toTime) - { - const qint64 intervalStart = nextSampleTime; - const qint64 intervalEnd = intervalStart + interval; - updateSnapshotKeys{{ dt.name }}(q, intervalStart, intervalEnd); - - vec.emplace_back(RecSnapshot(intervalEnd, m_snapshot{{ dt.name }}Map)); - - nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, intervalEnd); - nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging - } - } - if (!vec.empty()) - ret.insert({topicName, std::move(vec)}); - } -{% endif %} {% endfor %} - - return ret; -} - -QVector RecordReplayPlugin::getRecordTopicNames() const -{ - return {{% for dt in dataTopics.member %} {% if dt.recordTopic %} - QStringLiteral("{{ dt.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %} - }; -} - -QVector RecordReplayPlugin::getRecordTableNames() const -{ - return {{% for dt in dataTopics.member %} {% if dt.recordTopic %} - QStringLiteral("{{ dt.recTable.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %} - }; -} - -qint64 RecordReplayPlugin::getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const -{ - QString tableName; - {% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (topicName == QLatin1String("{{ dt.name }}")) - tableName = QStringLiteral("{{ dt.recTable.name }}"); - {% endif %} {% endfor %} - if (tableName.isEmpty()) - return defaultValue; - const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + tableName); - if (!q.execAndFetch(stmt)) - return defaultValue; - const QVariant val = q.value(0); - return val.isNull() ? defaultValue : val.toLongLong(); -} - -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - -bool RecordReplayPlugin::read{{ dt.name }}At(NSqlQuery &q, qint64 startTime) -{ - // read snapshot data for {{ dt.name }} - if (!readLastSnapshotFor{{ dt.name }}(q, startTime)) return false; - // read other samples between snapshot time and startTime - if (!read{{ dt.name }}(q, m_snapshot{{ dt.name }}MapTime, startTime)) return false; - return true; -} - -bool RecordReplayPlugin::write{{ dt.name }}(NSqlQuery &q, QHash &minRecordSampleTimes) -{ - using DATA = {{ namespace }}::{{ dt.name }}; - - if (!m_dtc{{ dt.name }}) - return false; - - qint64 minRecordSampleTime = std::numeric_limits::max(); - - if (m_dtc{{ dt.name }}->hasSamples()) - { - TRACE_LOG(m_logger) << "m_dtc{{ dt.name }}->hasSamples() == true "; - - const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES " - " (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});")); - - if (!q.prepare(stmt)) - return false; - - dds::sub::LoanedSamples samples = m_dtc{{ dt.name }}->takeAlive(); - TRACE_LOG(m_logger) << "samples count: " << samples.length(); - for (const auto &sample : samples) - { - const DATA &data = sample.data(); - INFO_LOG(m_logger) << "m_dtc{{ dt.name }} {{ dt.recTable.ddsKey.cppMemberName }}: " << data.{{ dt.recTable.ddsKey.cppMemberName }}(); - - {% for row in dt.recTable.cppMember %} - q.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.ddsToQVariant }}); - {% endfor %} - q.bindValue(QStringLiteral(":dds_is_alive"), true); - - if (!q.exec()) - return false; - - minRecordSampleTime = std::min(minRecordSampleTime, data.sampleTime()); - } - } - - dds::sub::LoanedSamples disposedSamples = m_dtc{{ dt.name }}->takeDisposed(); - if (disposedSamples.length() != 0) - { - const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, sample_time, {{ dt.recTable.ddsKey.dbColumnName }}) VALUES " - " (:dds_is_alive,:sample_time,:{{ dt.recTable.ddsKey.dbColumnName }});")); - - if (!q.prepare(stmt)) - return false; - - DEBUG_LOG(m_logger) << "disposedSamples count: " << disposedSamples.length(); - auto dr = m_dtc{{ dt.name }}->dataReader()->dataReader(); - {{ dt.name }} data; - for (const auto &sample : disposedSamples) - { - const qint64 sampleTime = sample.info().source_timestamp().to_millisecs(); - dr.key_value(data, sample.info().instance_handle()); - q.bindValue(QStringLiteral(":dds_is_alive"), false); - q.bindValue(QStringLiteral(":sample_time"), sampleTime); - q.bindValue(QStringLiteral(":{{ dt.recTable.ddsKey.cppMemberName }}"), {{ dt.recTable.ddsKey.ddsToQVariant }}); - - if (!q.exec()) - return false; - - minRecordSampleTime = std::min(minRecordSampleTime, sampleTime); - } - } - - if (minRecordSampleTime != std::numeric_limits::max()) - minRecordSampleTimes[QStringLiteral("{{ dt.name }}")] = minRecordSampleTime; - - return true; -} - -// read the last snapshot which is <= startTime, set m_snapshot{{ dt.name }}MapTime to the snapshot time -bool RecordReplayPlugin::readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime) -{ - // rebuild m_{{ dt.nameFlc }}Map content for given startTime - clearData{{ dt.name }}(); - - const QString stmt(QStringLiteral("SELECT id, time_stamp FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(startTime)); - - if (!q.exec(stmt)) - { - ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}"; - return false; - } - if (!q.next() || q.value(0).isNull()) - { - INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}"; - return true; - } - const qint64 idSnapshot = q.value(0).toLongLong(); - const qint64 timeSnapshot = q.value(1).toLongLong(); - - const QString stmt2(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')')); - - if (!q.exec(stmt2)) - return false; - - {{ namespace }}::{{ dt.name }} data; - while (q.next()) - { - int i = 0; - - const bool bIsAlive = q.value(i++).toBool(); - {% for row in dt.recTable.viewMember %} - {{ row.qVariantToDds }}; - {% endfor %} - - if (bIsAlive) - m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data); - else - m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data); - m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}()); - } - m_snapshot{{ dt.name }}MapTime = timeSnapshot; - - return true; -} - - -bool RecordReplayPlugin::read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';')); - - if (!q.exec(stmt)) - return false; - - {{ namespace }}::{{ dt.name }} data; - while (q.next()) - { - int i = 0; - const bool bIsAlive = q.value(i++).toBool(); - - {% for row in dt.recTable.viewMember %} - {{ row.qVariantToDds }}; - {% endfor %} - - if (bIsAlive) - m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data); - else - m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data); - m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}()); - } - return true; -} - -bool RecordReplayPlugin::updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT {{ dt.recTable.ddsKey.dbColumnName }}, {{ dt.recTable.primaryKey.dbColumnName }}, dds_is_alive FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + - QStringLiteral(" ORDER BY sample_time ")); - - DEBUG_LOG(m_logger) << "stmt: " << stmt; - - if (!q.exec(stmt)) - return false; - - while (q.next()) - { - const qint64 key = q.value(0){{ dt.recTable.ddsKey.cppFromQvPost }}; - const qint64 pk = q.value(1){{ dt.recTable.primaryKey.cppFromQvPost }}; - const bool isAlive = q.value(2).toBool(); - if (isAlive) - { - DEBUG_LOG(m_logger) << "INSERT {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive; - m_snapshot{{ dt.name }}Map[key] = pk; - m_{{ dt.nameFlc }}Keys.insert(key); - } - else - { - DEBUG_LOG(m_logger) << "REMOVE {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive; - m_snapshot{{ dt.name }}Map.erase(key); - m_{{ dt.nameFlc }}Keys.erase(key); - } - } - - // new validity time for snapshot data - m_snapshot{{ dt.name }}MapTime = toTime; - - DEBUG_LOG(m_logger) << "updateSnapshotKeys{{ dt.name }}(" << fromTime << ", " << toTime << ") = " - << m_snapshot{{ dt.name }}Map.size(); - - return true; -} - -bool RecordReplayPlugin::readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime) -{ - const QString stmt(QStringLiteral("SELECT rec_id_array FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(fromTime)); - - // no previous snapshot found? - if (!q.exec(stmt)) - return false; - if (!q.next() || q.value(0).isNull()) - { - DEBUG_LOG(m_logger) << "no snapshot entry for {{ dt.name }} found"; - return true; - } - - m_snapshot{{ dt.name }}Map = LtsUtils::BigintArray2D::fromPgString(q.value(0).toString())(); - - DEBUG_LOG(m_logger) << "Map size {{ dt.name }}: " << m_snapshot{{ dt.name }}Map.size(); - - return true; -} - - -qint64 RecordReplayPlugin::getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime) -{ - const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime)); - qint64 ret = -1; - if (q.execAndFetch(stmt) && !q.value(0).isNull()) - ret = q.value(0).toLongLong(); - - DEBUG_LOG(m_logger) << "next min sampleTime for {{ dt.name }} is " << ret << " (> " << fromTime << ")"; - - return ret; -} - - -bool RecordReplayPlugin::publish{{ dt.name }}(qint64 desiredTime) -{ - if (!m_dts{{ dt.name }}) - return false; - { - auto it = m_{{ dt.nameFlc }}Map.begin(); - while (it != m_{{ dt.nameFlc }}Map.end() && it.key() <= desiredTime) - { - m_dts{{ dt.name }}->write(it.value()); - it = m_{{ dt.nameFlc }}Map.erase(it); - } - } - { - auto it = m_{{ dt.nameFlc }}DisposedMap.begin(); - while (it != m_{{ dt.nameFlc }}DisposedMap.end() && it.key() <= desiredTime) - { - m_dts{{ dt.name }}->remove(it.value()); - it = m_{{ dt.nameFlc }}DisposedMap.erase(it); - } - } - - return true; -} - -void RecordReplayPlugin::clearData{{ dt.name }}() -{ - if (m_dts{{ dt.name }}) - { - {{ dt.name }} tmpData; - for (auto key : m_{{ dt.nameFlc }}Keys) - { - tmpData.{{ dt.key.name }}(key); - m_dts{{ dt.name }}->remove(tmpData); - } - } - m_{{ dt.nameFlc }}Keys.clear(); - m_{{ dt.nameFlc }}Map.clear(); - m_{{ dt.nameFlc }}DisposedMap.clear(); -} - -bool RecordReplayPlugin::copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime) -{ - const QString stmt(QStringLiteral("SELECT id FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(startTime)); - - if (!queryFrom.exec(stmt)) - { - ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}"; - return false; - } - if (!queryFrom.next() || queryFrom.value(0).isNull()) - { - INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}"; - return true; - } - const qint64 idSnapshot = queryFrom.value(0).toLongLong(); - - const QString stmtSelect(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')')); - - if (!queryFrom.exec(stmtSelect)) - return false; - - return copyDataPrepared{{ dt.name }}(queryFrom, queryTo); -} - -bool RecordReplayPlugin::copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';')); - - if (!queryFrom.exec(stmt)) - return false; - - return copyDataPrepared{{ dt.name }}(queryFrom, queryTo); -} - -bool RecordReplayPlugin::copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo) -{ - const QString stmtInsert(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES " - " (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});")); - - if (!queryTo.prepare(stmtInsert)) - return false; - - while (queryFromPrepared.next()) - { - int i = 0; - - queryTo.bindValue(QStringLiteral(":dds_is_alive"), queryFromPrepared.value(i++)); - {% for row in dt.recTable.cppMember %} - queryTo.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), queryFromPrepared.value(i++)); - {% endfor %} - if (!queryTo.exec()) - return false; - } - return true; -} - -{% endif %} {% endfor %} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions b/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions deleted file mode 100755 index 97a6e9240..000000000 --- a/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions +++ /dev/null @@ -1,637 +0,0 @@ -/** - * RecordReplay Plugin - * $Id: svc_record_replay_plugin.cpp.grantlee 50268 2020-03-06 07:42:09Z ehrlicher $ - * $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_07_assault_energy/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee $ - * $Author: ehrlicher $ - * Created by Christian Ehrlicher, SLSE, 2016-07-21 - */ - - -Leider gibnt es Probleme, wenn man Sequenzen von Sequenzen von Non-PODs erstellt. -Es erbgaben sich unresolved symbols... (Compiler war zufrieden, Linker aber nicht - hmm) - -Mit dem folgenden Konstrukt konnten ich diese "beheben", doch je nach Konstellation in der XYZ_service.xml -ergaben sich Fehler a la "template specialication after instatiation" - -Daher haben wir (zumindest zunächst) Abstand von dieser Erweiterung genommen. - -... - -{% for dt in serializeTopics.member %} -// -// Begin {{ dt.typeFq }} -// -template<> -{{ dt.typeFq }} fromJson(const QJsonValue &val, type<{{ dt.typeFq }}>) -{ - const QJsonObject objElement(val.toObject()); - {{ dt.typeFq }} element; - {% for row in dt.recTable.cppMember %} - { {{ row.jsonElementToDds }}; } {% endfor %} - - return element; -} -template<> -QJsonValue toJson(const {{ dt.typeFq }} &element) -{ - QJsonObject obj; - {% for row in dt.recTable.cppMember %} - {{ row.ddsToJsonElement }}; {% endfor %} - - return obj; -} - -template <> -QVariant toQVariant(const {{ dt.typeFq }} &val) -{ - return QString::fromUtf8(QJsonDocument(toJson(val).toObject()).toJson()); -} - -template<> -{{ dt.typeFq }} fromQVariant(const QVariant &val, type<{{ dt.typeFq }}>) -{ - return fromJson(QJsonDocument::fromJson(val.toString().toUtf8()).object(), type<{{ dt.typeFq }}>{}); -} - -// -// extensions to support sequence of seqence of Non-POD : -// - -template<> -QJsonValue toJson(const std::vector < {{ dt.typeFq }} > &data) -{ - if (data.size() == 0) - return QJsonValue(); - QJsonArray array; - for(size_t i = 0; i < data.size(); ++i) - array.push_back(toJson(data.at(i))); - - return QJsonDocument(array).object(); -} - -template<> -std::vector < {{ dt.typeFq }} > fromJson(const QJsonValue &val) -{ - std::vector < {{ dt.typeFq }} > ret; - if (val.isNull() == 0) - return ret; - if (val.isArray() == 0) return ret; - const QJsonArray array = val.toArray(); - ret.reserve(array.size()); - for(const auto &ele : array) - ret.push_back(fromJson(ele, type<{{ dt.typeFq }}>{})); - return ret; -} -// -// : extensions to support sequence of seqence of Non-POD -// - - -// -// End {{ dt.typeFq }} -// -{% endfor %} - - - - -RecordReplayPlugin::RecordReplayPlugin() - : m_exerciseId(0) - , m_bCanHandleSchemasRead(true) - , m_bCanHandleSchemasWrite(true) - , m_logger(getLogger("RecordReplay")) -{} - -RecordReplayPlugin::~RecordReplayPlugin() = default; - -QString RecordReplayPlugin::getTableName(const QString &tableName, bool bCanHandleSchemas) const -{ - if (!bCanHandleSchemas) - return tableName; - return DBCommon::getExerciseTableName(tableName, m_exerciseId); -} - -bool RecordReplayPlugin::initDtc(const std::shared_ptr &ddsConnector, - int exerciseId) -{ - m_ddsConnector = ddsConnector; - m_exerciseId = exerciseId; - if (!m_ddsConnector) - return false; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC()); -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::initDts(const std::shared_ptr &ddsConnector, - int exerciseId, - bool bCanHandleSchemas, - NSqlQuery &query) -{ - Q_UNUSED(query); - m_ddsConnector = ddsConnector; - m_exerciseId = exerciseId; - m_bCanHandleSchemasRead = bCanHandleSchemas; - m_bCanHandleSchemasWrite = bCanHandleSchemas; - if (!m_ddsConnector) - return false; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS()); -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite) -{ - m_exerciseId = exerciseId; - m_bCanHandleSchemasRead = bCanHandleSchemasRead; - m_bCanHandleSchemasWrite = bCanHandleSchemasWrite; - return true; -} - -bool RecordReplayPlugin::copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) -{ - {% for dt in dataTopics.member %} {% if dt.recordTopic %} - // {{ dt.name }} - if (!copyInitialData{{ dt.name }}(queryFrom, queryTo, fromTime)) return false; - if (!copyData{{ dt.name }}(queryFrom, queryTo, fromTime, toTime)) return false; - {% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::ddsTopicsToDatabase(NSqlQuery &q, QHash &minRecordSampleTimes) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!write{{ dt.name }}(q, minRecordSampleTimes)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::setReplayStartTime(NSqlQuery &q, qint64 startTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!read{{ dt.name }}At(q, startTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!read{{ dt.name }}(q, fromTime, toTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -bool RecordReplayPlugin::publishData(qint64 desiredTime) -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (!publish{{ dt.name }}(desiredTime)) return false; -{% endif %} {% endfor %} - - return true; -} - -void RecordReplayPlugin::clearData() -{ -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - clearData{{ dt.name }}(); -{% endif %} {% endfor %} -} - -std::map> RecordReplayPlugin::getSnapshotTimes(NSqlQuery &q, - qint64 toTime, - qint64 interval, - const QHash &minRecordedSampleTimes) -{ - std::map> ret; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - { - const QString &topicName(QStringLiteral("{{ dt.name }}")); - std::vector vec; - auto it = minRecordedSampleTimes.find(topicName); - if (it != minRecordedSampleTimes.end()) - { - qint64 nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, it.value()); // is 0 or -1 if no samples are available - nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging - - if (nextSampleTime != m_snapshot{{ dt.name }}MapTime) - { - // the current snapshot data does not match the starting time -> reread m_{{ dt.nameFlc }}Keys - readLastSnapshotKeysFor{{ dt.name }}(q, nextSampleTime); - //DEBUG_LOG(m_logger) << "fromTime: " << fromTime << ", m_snapshotParticipantMapTime: " << m_snapshotParticipantMapTime - // << " - rereading snapshot times! Map now contains " << m_snapshot{{ dt.name }}Map.size() << " entries"; - } - - while (nextSampleTime > 0 && nextSampleTime <= toTime) - { - const qint64 intervalStart = nextSampleTime; - const qint64 intervalEnd = intervalStart + interval; - updateSnapshotKeys{{ dt.name }}(q, intervalStart, intervalEnd); - - vec.emplace_back(RecSnapshot(intervalEnd, m_snapshot{{ dt.name }}Map)); - - nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, intervalEnd); - nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging - } - } - if (!vec.empty()) - ret.insert({topicName, std::move(vec)}); - } -{% endif %} {% endfor %} - - return ret; -} - -QVector RecordReplayPlugin::getRecordTopicNames() const -{ - return {{% for dt in dataTopics.member %} {% if dt.recordTopic %} - QStringLiteral("{{ dt.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %} - }; -} - -QVector RecordReplayPlugin::getRecordTableNames() const -{ - return {{% for dt in dataTopics.member %} {% if dt.recordTopic %} - QStringLiteral("{{ dt.recTable.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %} - }; -} - -qint64 RecordReplayPlugin::getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const -{ - QString tableName; - {% for dt in dataTopics.member %} {% if dt.recordTopic %} - if (topicName == QLatin1String("{{ dt.name }}")) - tableName = QStringLiteral("{{ dt.recTable.name }}"); - {% endif %} {% endfor %} - if (tableName.isEmpty()) - return defaultValue; - const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + tableName); - if (!q.execAndFetch(stmt)) - return defaultValue; - const QVariant val = q.value(0); - return val.isNull() ? defaultValue : val.toLongLong(); -} - -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - -bool RecordReplayPlugin::read{{ dt.name }}At(NSqlQuery &q, qint64 startTime) -{ - // read snapshot data for {{ dt.name }} - if (!readLastSnapshotFor{{ dt.name }}(q, startTime)) return false; - // read other samples between snapshot time and startTime - if (!read{{ dt.name }}(q, m_snapshot{{ dt.name }}MapTime, startTime)) return false; - return true; -} - -bool RecordReplayPlugin::write{{ dt.name }}(NSqlQuery &q, QHash &minRecordSampleTimes) -{ - using DATA = {{ namespace }}::{{ dt.name }}; - - if (!m_dtc{{ dt.name }}) - return false; - - qint64 minRecordSampleTime = std::numeric_limits::max(); - - if (m_dtc{{ dt.name }}->hasSamples()) - { - TRACE_LOG(m_logger) << "m_dtc{{ dt.name }}->hasSamples() == true "; - - const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES " - " (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});")); - - if (!q.prepare(stmt)) - return false; - - dds::sub::LoanedSamples samples = m_dtc{{ dt.name }}->takeAlive(); - TRACE_LOG(m_logger) << "samples count: " << samples.length(); - for (const auto &sample : samples) - { - const DATA &data = sample.data(); - INFO_LOG(m_logger) << "m_dtc{{ dt.name }} {{ dt.recTable.ddsKey.cppMemberName }}: " << data.{{ dt.recTable.ddsKey.cppMemberName }}(); - - {% for row in dt.recTable.cppMember %} - q.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.ddsToQVariant }}); - {% endfor %} - q.bindValue(QStringLiteral(":dds_is_alive"), true); - - if (!q.exec()) - return false; - - minRecordSampleTime = std::min(minRecordSampleTime, data.sampleTime()); - } - } - - dds::sub::LoanedSamples disposedSamples = m_dtc{{ dt.name }}->takeDisposed(); - if (disposedSamples.length() != 0) - { - const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, sample_time, {{ dt.recTable.ddsKey.dbColumnName }}) VALUES " - " (:dds_is_alive,:sample_time,:{{ dt.recTable.ddsKey.dbColumnName }});")); - - if (!q.prepare(stmt)) - return false; - - DEBUG_LOG(m_logger) << "disposedSamples count: " << disposedSamples.length(); - auto dr = m_dtc{{ dt.name }}->dataReader()->dataReader(); - {{ dt.name }} data; - for (const auto &sample : disposedSamples) - { - const qint64 sampleTime = sample.info().source_timestamp().to_millisecs(); - dr.key_value(data, sample.info().instance_handle()); - q.bindValue(QStringLiteral(":dds_is_alive"), false); - q.bindValue(QStringLiteral(":sample_time"), sampleTime); - q.bindValue(QStringLiteral(":{{ dt.recTable.ddsKey.cppMemberName }}"), {{ dt.recTable.ddsKey.ddsToQVariant }}); - - if (!q.exec()) - return false; - - minRecordSampleTime = std::min(minRecordSampleTime, sampleTime); - } - } - - if (minRecordSampleTime != std::numeric_limits::max()) - minRecordSampleTimes[QStringLiteral("{{ dt.name }}")] = minRecordSampleTime; - - return true; -} - -// read the last snapshot which is <= startTime, set m_snapshot{{ dt.name }}MapTime to the snapshot time -bool RecordReplayPlugin::readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime) -{ - // rebuild m_{{ dt.nameFlc }}Map content for given startTime - clearData{{ dt.name }}(); - - const QString stmt(QStringLiteral("SELECT id, time_stamp FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(startTime)); - - if (!q.exec(stmt)) - { - ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}"; - return false; - } - if (!q.next() || q.value(0).isNull()) - { - INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}"; - return true; - } - const qint64 idSnapshot = q.value(0).toLongLong(); - const qint64 timeSnapshot = q.value(1).toLongLong(); - - const QString stmt2(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')')); - - if (!q.exec(stmt2)) - return false; - - {{ namespace }}::{{ dt.name }} data; - while (q.next()) - { - int i = 0; - - const bool bIsAlive = q.value(i++).toBool(); - {% for row in dt.recTable.viewMember %} - {{ row.qVariantToDds }}; - {% endfor %} - - if (bIsAlive) - m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data); - else - m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data); - m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}()); - } - m_snapshot{{ dt.name }}MapTime = timeSnapshot; - - return true; -} - - -bool RecordReplayPlugin::read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';')); - - if (!q.exec(stmt)) - return false; - - {{ namespace }}::{{ dt.name }} data; - while (q.next()) - { - int i = 0; - const bool bIsAlive = q.value(i++).toBool(); - - {% for row in dt.recTable.viewMember %} - {{ row.qVariantToDds }}; - {% endfor %} - - if (bIsAlive) - m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data); - else - m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data); - m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}()); - } - return true; -} - -bool RecordReplayPlugin::updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT {{ dt.recTable.ddsKey.dbColumnName }}, {{ dt.recTable.primaryKey.dbColumnName }}, dds_is_alive FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + - QStringLiteral(" ORDER BY sample_time ")); - - DEBUG_LOG(m_logger) << "stmt: " << stmt; - - if (!q.exec(stmt)) - return false; - - while (q.next()) - { - const qint64 key = q.value(0){{ dt.recTable.ddsKey.cppFromQvPost }}; - const qint64 pk = q.value(1){{ dt.recTable.primaryKey.cppFromQvPost }}; - const bool isAlive = q.value(2).toBool(); - if (isAlive) - { - DEBUG_LOG(m_logger) << "INSERT {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive; - m_snapshot{{ dt.name }}Map[key] = pk; - m_{{ dt.nameFlc }}Keys.insert(key); - } - else - { - DEBUG_LOG(m_logger) << "REMOVE {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive; - m_snapshot{{ dt.name }}Map.erase(key); - m_{{ dt.nameFlc }}Keys.erase(key); - } - } - - // new validity time for snapshot data - m_snapshot{{ dt.name }}MapTime = toTime; - - DEBUG_LOG(m_logger) << "updateSnapshotKeys{{ dt.name }}(" << fromTime << ", " << toTime << ") = " - << m_snapshot{{ dt.name }}Map.size(); - - return true; -} - -bool RecordReplayPlugin::readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime) -{ - const QString stmt(QStringLiteral("SELECT rec_id_array FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(fromTime)); - - // no previous snapshot found? - if (!q.exec(stmt)) - return false; - if (!q.next() || q.value(0).isNull()) - { - DEBUG_LOG(m_logger) << "no snapshot entry for {{ dt.name }} found"; - return true; - } - - m_snapshot{{ dt.name }}Map = LtsUtils::BigintArray2D::fromPgString(q.value(0).toString())(); - - DEBUG_LOG(m_logger) << "Map size {{ dt.name }}: " << m_snapshot{{ dt.name }}Map.size(); - - return true; -} - - -qint64 RecordReplayPlugin::getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime) -{ - const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime)); - qint64 ret = -1; - if (q.execAndFetch(stmt) && !q.value(0).isNull()) - ret = q.value(0).toLongLong(); - - DEBUG_LOG(m_logger) << "next min sampleTime for {{ dt.name }} is " << ret << " (> " << fromTime << ")"; - - return ret; -} - - -bool RecordReplayPlugin::publish{{ dt.name }}(qint64 desiredTime) -{ - if (!m_dts{{ dt.name }}) - return false; - { - auto it = m_{{ dt.nameFlc }}Map.begin(); - while (it != m_{{ dt.nameFlc }}Map.end() && it.key() <= desiredTime) - { - m_dts{{ dt.name }}->write(it.value()); - it = m_{{ dt.nameFlc }}Map.erase(it); - } - } - { - auto it = m_{{ dt.nameFlc }}DisposedMap.begin(); - while (it != m_{{ dt.nameFlc }}DisposedMap.end() && it.key() <= desiredTime) - { - m_dts{{ dt.name }}->remove(it.value()); - it = m_{{ dt.nameFlc }}DisposedMap.erase(it); - } - } - - return true; -} - -void RecordReplayPlugin::clearData{{ dt.name }}() -{ - if (m_dts{{ dt.name }}) - { - {{ dt.name }} tmpData; - for (auto key : m_{{ dt.nameFlc }}Keys) - { - tmpData.{{ dt.key.name }}(key); - m_dts{{ dt.name }}->remove(tmpData); - } - } - m_{{ dt.nameFlc }}Keys.clear(); - m_{{ dt.nameFlc }}Map.clear(); - m_{{ dt.nameFlc }}DisposedMap.clear(); -} - -bool RecordReplayPlugin::copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime) -{ - const QString stmt(QStringLiteral("SELECT id FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 " - " ORDER BY time_stamp DESC LIMIT 1").arg(startTime)); - - if (!queryFrom.exec(stmt)) - { - ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}"; - return false; - } - if (!queryFrom.next() || queryFrom.value(0).isNull()) - { - INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}"; - return true; - } - const qint64 idSnapshot = queryFrom.value(0).toLongLong(); - - const QString stmtSelect(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')')); - - if (!queryFrom.exec(stmtSelect)) - return false; - - return copyDataPrepared{{ dt.name }}(queryFrom, queryTo); -} - -bool RecordReplayPlugin::copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) -{ - const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") + - getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) + - QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) + - QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';')); - - if (!queryFrom.exec(stmt)) - return false; - - return copyDataPrepared{{ dt.name }}(queryFrom, queryTo); -} - -bool RecordReplayPlugin::copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo) -{ - const QString stmtInsert(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) + - QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES " - " (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});")); - - if (!queryTo.prepare(stmtInsert)) - return false; - - while (queryFromPrepared.next()) - { - int i = 0; - - queryTo.bindValue(QStringLiteral(":dds_is_alive"), queryFromPrepared.value(i++)); - {% for row in dt.recTable.cppMember %} - queryTo.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), queryFromPrepared.value(i++)); - {% endfor %} - if (!queryTo.exec()) - return false; - } - return true; -} - -{% endif %} {% endfor %} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee b/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee deleted file mode 100755 index 2aded1edd..000000000 --- a/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee +++ /dev/null @@ -1,126 +0,0 @@ -/** - * RecordReplay Plugin - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-07-21 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_record_replay_plugin.h -#pragma once - -#include -#include -#include -#include -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_dts_types.h> -#include <{{ include_prefix }}/logger.h> - -// TODO: Problem with QT5_WRAP_CPP ... -#include <../../Common/PluginInterfaces/record_replay_plugin.h> - -#include - -{% if custom_record_replay %} -#ifndef {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT -# else -# if defined(MAKE_{{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_LIB) -# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT Q_DECL_EXPORT -# else -# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif -{% endif %} - -namespace {{ namespace }} { - -class {% if custom_record_replay %}{{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT{% endif %} - RecordReplayPlugin {% if not custom_record_replay %}final{% endif %}: public QObject, public RecordReplay::RecordReplayPlugin -{ - Q_OBJECT -{% if not custom_record_replay %} - Q_PLUGIN_METADATA(IID "special:3:recordreplay.{{ namespace_abbr }}") -{% endif %} - Q_INTERFACES(RecordReplay::RecordReplayPlugin) -public: - RecordReplayPlugin(); - ~RecordReplayPlugin(); - QString pluginName() const override { return QStringLiteral("{{ namespace_abbr }}RecordReplayPlugin"); } - - bool initDtc(const std::shared_ptr &ddsConnector, - int exerciseId) override; - bool initDts(const std::shared_ptr &ddsConnector, - int exerciseId, - bool bCanHandleSchemas, - NSqlQuery &query) override; - bool initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite) override; - bool copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) override; - QString svcShortName() const override { return QStringLiteral("{{ namespace_abbr }}"); } - // recording functions - bool ddsTopicsToDatabase(NSqlQuery &q, QHash &minRecordSampleTimes) override; - // publishing functions - bool setReplayStartTime(NSqlQuery &q, qint64 startTime) override; - bool ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime) override; - bool publishData(qint64 desiredTime) override; - void clearData() override; - - // structure/functions for snapshot creation - std::map> getSnapshotTimes(NSqlQuery &query, - qint64 toTime, - qint64 interval, - const QHash &minRecordedSampleTimes) override; - QVector getRecordTopicNames() const override; - QVector getRecordTableNames() const override; - qint64 getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const override; - - // getter for initial service startup -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - // {{ dt.name }} - QMultiMap &get{{ dt.name }}Map() { return m_{{ dt.nameFlc }}Map; } - const QMultiMap &get{{ dt.name }}Map() const { return m_{{ dt.nameFlc }}Map; } - bool read{{ dt.name }}At(NSqlQuery &q, qint64 startTime); -{% endif %} {% endfor %} - -protected: -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - // {{ dt.name }} - bool write{{ dt.name }}(NSqlQuery &q, QHash &minRecordSampleTimes); - bool read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime); - bool readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime); - bool updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime); - bool readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime); - qint64 getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime); - bool publish{{ dt.name }}(qint64 desiredTime); - void clearData{{ dt.name }}(); - bool copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime); - bool copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime); - bool copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo); -{% endif %} {% endfor %} - - QString getTableName(const QString &tableName, bool bCanHandleSchemas) const; -protected: - std::shared_ptr m_ddsConnector; - int m_exerciseId; - bool m_bCanHandleSchemasRead; - bool m_bCanHandleSchemasWrite; -{% for dt in dataTopics.member %} {% if dt.recordTopic %} - // {{ dt.name }} - std::unique_ptr m_dtc{{ dt.name }}; - std::unique_ptr m_dts{{ dt.name }}; - QMultiMap m_{{ dt.nameFlc }}Map; - QMultiMap m_{{ dt.nameFlc }}DisposedMap; - std::set m_{{ dt.nameFlc }}Keys; - std::map m_snapshot{{ dt.name }}Map; - int64_t m_snapshot{{ dt.name }}MapTime = -1; // time for which the snapshot is valid -{% endif %} {% endfor %} - - LOGGER_T m_logger; -}; - -} -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee b/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee deleted file mode 100755 index 8f944ce20..000000000 --- a/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee +++ /dev/null @@ -1,102 +0,0 @@ -/** - * DDS Type Plugin to provide custom types to other applications like ddsview - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-07-08 - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_topic_type_plugin.cpp - -#include "{{ namespace_abbr }}_topic_type_plugin.h" -#include <{{ namespace_abbr }}_dtc_types.h> -#include <{{ namespace_abbr }}_ftc_types.h> -#include <{{ namespace_abbr }}_fts_types.h> -#include -#include -#include -#include - -namespace {{ namespace }} -{ -QMap TopicTypePlugin::getDataTopics() const -{ - return - {{% for dt in dataTopics.member %} - { - dds::topic::topic_type_name<{{ dt.name }}>::value(), - { - DDS::Helper::getShortTopicName(dds::topic::topic_type_name<{{ dt.name }}>::value()), - dds::topic::topic_type_name<{{ dt.name }}>::value(), - rti::topic::dynamic_type<{{ dt.name }}>::get() - } - }{% if not forloop.last %}, {% endif %}{% endfor %} - }; -} - -QMap TopicTypePlugin::getFunctionTopics() const -{ - return - {{% for ft in functionTopics.member %} - { - FTC::TopicNames::requestTopicName(), - { - FTC::TopicNames::requestTopicName(), - dds::topic::topic_type_name::value(), - rti::topic::dynamic_type::get() - } - }, - { - FTC::TopicNames::replyTopicName(), - { - FTC::TopicNames::replyTopicName(), - dds::topic::topic_type_name::value(), - rti::topic::dynamic_type::get() - } - }{% if not forloop.last %}, {% endif %}{% endfor %} - }; -} - -inline NGL::INGLLogger &logger(const char *name) -{ - return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(name); -} - -std::vector TopicTypePlugin::topicsToRouteToWs(IDL::PartitionType partitionType) const -{ - if (partitionType == IDL::PartitionType::Replay) - return - {{% for dt in dataTopics.topicsToRouteInReplay %} - new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %} - {% for ft in functionTopics.topicsToRouteInReplay %} - new RoutingService::FunctionTopicReplyDispatcher(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Reply")){% if not forloop.last %},{% endif %}{% endfor %} - }; - - return - {{% for dt in dataTopics.topicsToRouteToClient %} - new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %} - {% for ft in functionTopics.member %} - new RoutingService::FunctionTopicReplyDispatcher(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Reply")){% if not forloop.last %},{% endif %}{% endfor %} - }; -} - -std::vector TopicTypePlugin::topicsToRouteToSv(IDL::PartitionType partitionType) const -{ - if (partitionType == IDL::PartitionType::Replay) - return - {{% for ft in functionTopics.topicsToRouteInReplay %} - new RoutingService::FunctionTopicRequestDispatcher(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Request")){% if not forloop.last %},{% endif %}{% endfor %} - }; - - return - {{% for dt in dataTopics.topicsToRouteToServer %} - new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %} - {% for ft in functionTopics.member %} - new RoutingService::FunctionTopicRequestDispatcher(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Request")){% if not forloop.last %},{% endif %}{% endfor %} - }; -} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee b/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee deleted file mode 100755 index ea0b10408..000000000 --- a/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee +++ /dev/null @@ -1,39 +0,0 @@ -/** - * DDS Type Plugin to provide custom types to other applications like ddsview - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, SLSE, 2016-07-08 - */ - -#pragma once - -{% autoescape off %} -// filename: {{ namespace_abbr }}_topic_type_plugin.h - -#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp> -#include - -#include -// TODO: Problem with QT5_WRAP_CPP ... -#include <../../DDS/Generic/topic_type_plugin.h> - -namespace {{ namespace }} -{ -class TopicTypePlugin final : public QObject, public DDSGeneric::TopicTypePlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "topic_type:3:{{ namespace_abbr }}") - Q_INTERFACES(DDSGeneric::TopicTypePlugin) -public: - TopicTypePlugin() = default; - - QMap getDataTopics() const override final; - QMap getFunctionTopics() const override final; - std::vector topicsToRouteToWs(IDL::PartitionType partitionType) const override final; - std::vector topicsToRouteToSv(IDL::PartitionType partitionType) const override final; - QString getServiceShortName() const override final { return QStringLiteral("{{ namespace_abbr_upper }}"); } -}; -} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_translation.cpp.grantlee b/Lts/Common/CodeGen/svc_translation.cpp.grantlee deleted file mode 100755 index ebf6bea72..000000000 --- a/Lts/Common/CodeGen/svc_translation.cpp.grantlee +++ /dev/null @@ -1,43 +0,0 @@ -/** -* @RDE_HEADER@ -* Convert DSS enumerations to translatable strings -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_translation.cpp - -#include "{{ namespace_abbr }}_translation.h" -#include - -namespace {{ namespace }} -{ -{% for idl_enum in idl_enums %} -// -// {{ idl_enum.name }} -// -QString toString({{ idl_enum.name }} state) -{ - switch(state.underlying()) - {{% for idl_member in idl_enum.member %} - case {{ idl_enum.name }}_def::{{ idl_member.key }}: return QCoreApplication::translate("{{ idl_enum.name }}", "{{ idl_member.key }}"); - {% endfor %} - default: break; - } - return QCoreApplication::translate("{{ idl_enum.name }}", "Unknown (%1)").arg(state.underlying()); -} -{% endfor %} - -{% for dt in dataTopics.member %}{% if dt.hasTrColumns %}{% for tr in dt.trColumns %} -// {{ dt.name }}::{{ tr.name }} -QString tr{{ dt.name }}{{ tr.nameCc }}(const std::string &text) -{ - return QCoreApplication::translate("{{ dt.name }}", text.c_str(), "{{ tr.name }}"); -} -{% endfor %}{% endif %}{% endfor %} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/svc_translation.h.grantlee b/Lts/Common/CodeGen/svc_translation.h.grantlee deleted file mode 100755 index 5f7c33796..000000000 --- a/Lts/Common/CodeGen/svc_translation.h.grantlee +++ /dev/null @@ -1,31 +0,0 @@ -/** -* @RDE_HEADER@ -* Convert DSS enumerations to translatable strings -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -{% autoescape off %} -#pragma once - -// filename: {{ namespace_abbr }}_translation.h - -#include "{{ namespace_abbr }}_enums.hpp" -#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> - -class QString; - -namespace {{ namespace }} -{ -// Translatable enumerations: -{% for idl_enum in idl_enums %} -{{ namespace_abbr_upper }}_COMMON_EXPORT QString toString({{ idl_enum.name }} state);{% endfor %} - -// Translatable struct members: -{% for dt in dataTopics.member %}{% if dt.hasTrColumns %}{% for tr in dt.trColumns %} -{{ namespace_abbr_upper }}_COMMON_EXPORT QString tr{{ dt.nameCc }}{{ tr.nameCc }}(const std::string &text); // {{ dt.nameCc }}::{{ tr.name }} {% endfor %}{% endif %}{% endfor %} - -} // namespace {{ namespace }} - -{% endautoescape %} diff --git a/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp b/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp deleted file mode 100644 index 185523b38..000000000 --- a/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -#include -#include - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - - QString templatesDir = QDir::currentPath(); - if (argc > 1) { - templatesDir = QString::fromLocal8Bit(argv[1]); - } - - Grantlee::Engine engine; - auto loader = QSharedPointer::create(); - loader->setTemplateDirs(QStringList() << templatesDir); - engine.addTemplateLoader(loader); - - QDir d(templatesDir); - QStringList files = d.entryList(QStringList() << QStringLiteral("*.grantlee"), QDir::Files); - if (files.isEmpty()) { - qDebug() << "No .grantlee templates found in" << templatesDir; - // Treat absence as successful parse (nothing to check) to avoid failing - // when directory is empty in different setups. - return 0; - } - - bool ok = true; - for (const QString &f : files) { - Grantlee::Template t; - try { - t = engine.loadByName(f); - } catch (...) { - qDebug() << "Exception while loading" << f; - ok = false; - continue; - } - if (!t) { - qDebug() << "Failed to load template" << f; - ok = false; - continue; - } - if (t->error() != Grantlee::NoError) { - qDebug() << "Parse error in" << f << ":" << t->errorString(); - ok = false; - continue; - } - // Render with empty context to ensure rendering path also works. - Grantlee::Context ctx; - QString out = t->render(&ctx); - if (out.isNull()) { - qDebug() << "Rendering produced null for" << f; - ok = false; - continue; - } - } - - return ok ? 0 : 2; -} diff --git a/Lts/Common/CommonUnitTestsPlaceholder/CMakeLists.txt b/Lts/Common/CommonUnitTestsPlaceholder/CMakeLists.txt deleted file mode 100644 index 2864c81b1..000000000 --- a/Lts/Common/CommonUnitTestsPlaceholder/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(CommonUnitTestsPlaceholder) - -include(FetchContent) - -option(BUILD_SMOKE_TESTS "Build QtTest smoke tests" OFF) -option(BUILD_GTEST_SMOKE "Build GTest smoke tests" OFF) - -add_subdirectory(mocks) - -if(BUILD_SMOKE_TESTS) - find_package(Qt6 REQUIRED COMPONENTS Test) - add_executable(coordTransTests_smoke coordTransTests_smoke.cpp) - target_link_libraries(coordTransTests_smoke PRIVATE Qt6::Test MockCoordTrans::MockCoordTrans) - add_test(NAME coordTransTests_smoke COMMAND coordTransTests_smoke) -endif() - -if(BUILD_GTEST_SMOKE) - find_package(GTest QUIET) - if(NOT GTest_FOUND) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.12.1 - ) - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - endif() - add_executable(coordTransTests_gtest coordTransTests_gtest.cpp) - target_link_libraries(coordTransTests_gtest PRIVATE GTest::gtest_main MockCoordTrans::MockCoordTrans) - add_test(NAME coordTransTests_gtest COMMAND coordTransTests_gtest) -endif() diff --git a/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_gtest.cpp b/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_gtest.cpp deleted file mode 100644 index b0fc3f57e..000000000 --- a/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_gtest.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "mocks/MockCoordTrans.h" -#include - -TEST(CoordTransGTest, ForwardInverseRoundtrip) { - auto p = MockCoordTrans::forward(1.2345, 2.3456); - EXPECT_NEAR(p.first, 1.2345 * 111000.0, 1e-6); - EXPECT_NEAR(p.second, 2.3456 * 111000.0, 1e-6); - - auto inv = MockCoordTrans::inverse(p.first, p.second); - EXPECT_NEAR(inv.first, 1.2345, 1e-9); - EXPECT_NEAR(inv.second, 2.3456, 1e-9); -} diff --git a/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_smoke.cpp b/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_smoke.cpp deleted file mode 100644 index d5a177c35..000000000 --- a/Lts/Common/CommonUnitTestsPlaceholder/coordTransTests_smoke.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include "mocks/MockCoordTrans.h" - -class CoordTransSmoke : public QObject { - Q_OBJECT -private slots: - void testForward() { - auto p = MockCoordTrans::forward(10.0, 20.0); - QVERIFY(qFuzzyCompare(p.first, 10.0 * 111000.0)); - QVERIFY(qFuzzyCompare(p.second, 20.0 * 111000.0)); - } -}; - -QTEST_MAIN(CoordTransSmoke) -#include "coordTransTests_smoke.moc" diff --git a/Lts/Common/CommonUnitTestsPlaceholder/mocks/CMakeLists.txt b/Lts/Common/CommonUnitTestsPlaceholder/mocks/CMakeLists.txt deleted file mode 100644 index 9e634c072..000000000 --- a/Lts/Common/CommonUnitTestsPlaceholder/mocks/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_library(MockCoordTrans STATIC MockCoordTrans.h) -target_include_directories(MockCoordTrans PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -# Export header-only semantics by providing an interface target -add_library(MockCoordTrans::MockCoordTrans ALIAS MockCoordTrans) diff --git a/Lts/Common/CommonUnitTestsPlaceholder/mocks/MockCoordTrans.h b/Lts/Common/CommonUnitTestsPlaceholder/mocks/MockCoordTrans.h deleted file mode 100644 index 5028ddc30..000000000 --- a/Lts/Common/CommonUnitTestsPlaceholder/mocks/MockCoordTrans.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -// Minimal realistic mock for coordinate transforms used during migration. -// Forward conversion returns approximate meters using lat/lon*111000. -class MockCoordTrans { -public: - // approximate forward: (lat, lon) -> (x_m, y_m) - static std::pair forward(double lat_deg, double lon_deg) { - return {lat_deg * 111000.0, lon_deg * 111000.0}; - } - // approximate inverse - static std::pair inverse(double x_m, double y_m) { - return {x_m / 111000.0, y_m / 111000.0}; - } -}; diff --git a/Lts/Common/LtsAppDescriptor/CMakeLists.txt b/Lts/Common/LtsAppDescriptor/CMakeLists.txt deleted file mode 100755 index 1c9d87f41..000000000 --- a/Lts/Common/LtsAppDescriptor/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-10-24 -# - - -# LtsAppDescriptor (lib) -SET(LtsAppDescriptor_SRCS - lts_app_descriptor.cpp - lts_app_descriptor.h -) - -NGL_ADD_LIBRARY(WS LtsAppDescriptor SHARED ${LtsAppDescriptor_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptor - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptor - PUBLIC - Qt5::Core -) - -NGL_INSTALL_TARGET(LtsAppDescriptor) - - - -# LtsAppDescriptorChecker -SET(LtsAppDescriptorChecker_SRCS - checker/main.cpp -) - -NGL_ADD_EXECUTABLE(WS LtsAppDescriptorChecker ${LtsAppDescriptorChecker_SRCS}) - -NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorChecker - PRIVATE - LtsAppDescriptor - PUBLIC - Qt5::Core -) - - - - - -# LtsAppDescriptorEditor => Hat niemand benutzt, wird keiner benutzen, also wird der bis auf Weiteres auch nicht an die neue Syntax angepasst. -#SET(LtsAppDescriptorEditor_SRCS -# lts_app_descriptor_editor.cpp -# lts_app_descriptor_editor.h -#) - -#NGL_WRAP_UI(LtsAppDescriptorEditor_SRCS -# lts_app_descriptor_editor.ui -#) - -#NGL_ADD_LIBRARY(WS NO_TRANSLATION LtsAppDescriptorEditor SHARED ${LtsAppDescriptorEditor_SRCS}) - -#NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptorEditor -# PUBLIC -# ${CMAKE_CURRENT_SOURCE_DIR}/../ -#) - -#NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorEditor -# PUBLIC -# Qt5::Core -# Qt5::Widgets -# LtsAppDescriptor -#) - -#NGL_INSTALL_TARGET(LtsAppDescriptorEditor) #not visible for end-user - -## Executable: -#NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsAppDescriptorEditorApp main.cpp) -#NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorEditorApp LtsAppDescriptorEditor) diff --git a/Lts/Common/LtsAppDescriptor/checker/main.cpp b/Lts/Common/LtsAppDescriptor/checker/main.cpp deleted file mode 100755 index 725bf2a71..000000000 --- a/Lts/Common/LtsAppDescriptor/checker/main.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - - const QStringList args(a.arguments()); - - if (args.count() != 3) - { - printf("usage: %s \n", args.at(0).toStdString().c_str()); - return -1; - } - - const QString currentSrcDir(a.arguments().at(1)); - - const QString appsJsonFilePath(QStringLiteral("%1/%2").arg(currentSrcDir, a.arguments().at(2))); - QFile f(appsJsonFilePath); - - if (!f.open(QFile::ReadOnly)) - { - printf("LtsAppDescriptorChecker: cannot open apps.json at %s: %s\n", appsJsonFilePath.toStdString().c_str(), f.errorString().toStdString().c_str()); - return -2; - } - - printf("LtsAppDescriptorChecker: checking %s\n", appsJsonFilePath.toStdString().c_str()); - - QStringList lastErrors; - QStringList dummy1; - QString dummy2; - LtsAppDescriptor::AppDescriptionVector adv = LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(f.readAll(), lastErrors, dummy1, dummy2); - - if (!lastErrors.isEmpty()) - { - printf("LtsAppDescriptorChecker: apps.json at %s invalid:\n%s\n", appsJsonFilePath.toStdString().c_str(), lastErrors.join(QStringLiteral("\r\n\r\n")).toStdString().c_str()); - return 1; - } - - - - QFileInfoList guiPluginFiles; - QDirIterator it(QStringLiteral("%1/../../Plugins/").arg(currentSrcDir), QDirIterator::Subdirectories); - - while (it.hasNext()) - { - QString filename(it.next()); - QFileInfo file(filename); - - if (file.isDir()) - continue; - - if (file.fileName().endsWith(QStringLiteral("_gui_plugin.h"), Qt::CaseInsensitive)) - guiPluginFiles.append(file); - } - - QStringList filenames; - filenames.reserve(guiPluginFiles.size()); - for (const QFileInfo &fi : qAsConst(guiPluginFiles)) - { - filenames += fi.absoluteFilePath(); - } - - - QStringList guiPluginIIDs; - for (const QFileInfo &fi : qAsConst(guiPluginFiles)) - { - QFile f2(fi.absoluteFilePath()); - - if (!f2.open(QFile::ReadOnly)) - continue; - - QTextStream ts(&f2); - while (!ts.atEnd()) - { - QString line(ts.readLine().trimmed().simplified()); - static QString iidIdentifier(QStringLiteral("Q_PLUGIN_METADATA")); - - if (line.contains(iidIdentifier)) - { - QRegularExpression re(QStringLiteral("(?<=gui:3:).*(?=\"\\))")); - QRegularExpressionMatch match = re.match(line); - if (match.hasMatch()) - { - QString matched = match.captured(0); - - guiPluginIIDs += matched; - } - break; - } - } - } - - - - bool ok = true; - - for (const LtsAppDescriptor::AppDescription &appdesc : qAsConst(adv)) - { - for (const LtsAppDescriptor::PluginInfo &pi : qAsConst(appdesc.pluginInfos)) - { - if (!guiPluginIIDs.contains(pi.iid)) - { - printf("LtsAppDescriptorChecker: apps.json at '%s' uses unknown IID '%s'\n", appsJsonFilePath.toStdString().c_str(), pi.iid.toStdString().c_str()); - ok = false; - } - } - } - - - - return !ok; -} - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsAppDescriptor/lts_app_descriptor.cpp b/Lts/Common/LtsAppDescriptor/lts_app_descriptor.cpp deleted file mode 100755 index 047d0aab1..000000000 --- a/Lts/Common/LtsAppDescriptor/lts_app_descriptor.cpp +++ /dev/null @@ -1,215 +0,0 @@ - - - -#include "lts_app_descriptor.h" - -#include -#include -#include -#include -#include -#include - - - - -namespace LtsAppDescriptor -{ -static void addOptionToPlugin(PluginInfo &pluginInfo, const PluginInfo::Flag &flag) -{ - pluginInfo.flags |= flag; -} - - -static void addOptionToPlugin(QVector &pluginInfos, const PluginInfo::Flag &flag, const QString &pluginName) -{ - for (PluginInfo &pi : pluginInfos) - { - if (pi.iid == pluginName) - { - addOptionToPlugin(pi, flag); - return; - } - } - - PluginInfo pi; - pi.iid = pluginName; - pi.type = PluginInfo::PluginType::GUI; - addOptionToPlugin(pi, flag); - pluginInfos.append(pi); -} - - -static bool addOptionsToPlugin(AppDescription &appDesc, const QString &optionIdentifier, const QStringList &ignoredPlugins, - const QJsonObject &optionsObject, const PluginInfo::Flag flag, QStringList &lastErrors, - bool atLeastOnePluginNeeded) -{ - bool hasPlugins = false; - auto optionsObjectIt = optionsObject.constFind(optionIdentifier); - if (optionsObjectIt != optionsObject.constEnd()) - { - const QJsonArray plugins((*optionsObjectIt).toObject()[QStringLiteral("Plugins")].toArray()); - - for (const auto &pluginElem : plugins) - { - const QString pluginName = pluginElem.toString(); - if (!ignoredPlugins.contains(pluginName)) - { - addOptionToPlugin(appDesc.pluginInfos, flag, pluginName); - hasPlugins = true; - } - } - } - - if (atLeastOnePluginNeeded && !hasPlugins) - { - lastErrors.append(QCoreApplication::tr("No plugins specified for '%1' option for app '%2'").arg(optionIdentifier, appDesc.appName)); - return false; - } - - return true; -} - - -App LtsAppDescriptor::appFromInteger(int i) -{ - switch (i) - { - case int(App::Login): - case int(App::Situation): - case int(App::ResourceManager): - case int(App::PlanEditor): - case int(App::BuildingViewer): - case int(App::OC): - case int(App::OrgChart): - case int(App::Scripts): - - case int(App::Snapshot): - case int(App::SystemMonitor): - case int(App::Timeline): - case int(App::SymbolEditor): - case int(App::ScriptEditorEssentialsView): - case int(App::BaseDownloader): - case int(App::OnlineHelp): - case int(App::ThpSituationReplay): - case int(App::SituationTablet): - case int(App::SilhouetteEditor): - case int(App::Shootback): - case int(App::SAAs): - return static_cast(i); - } - Q_ASSERT_X(false, "LtsAppDescriptor::appFromInteger", qPrintable(QStringLiteral("Unknown app id: %1").arg(i))); - return App::Login; -} - -AppDescriptionVector LtsAppDescriptor::getAppDescriptionVector(const QByteArray &jsonDocument, - QStringList &lastErrors, - const QStringList &ignoredPlugins, - QString &loginPluginIid) -{ - - - AppDescriptionVector appDescriptions; - - QJsonParseError parseError; - QJsonDocument appsDoc(QJsonDocument::fromJson(jsonDocument, &parseError)); - - if (parseError.error != QJsonParseError::NoError) - { - - - int row = 0; - int column = 0; - QChar x = QLatin1Char('\n'); - - for (int i = 0; i < jsonDocument.count(); i++) - { - if (i == parseError.offset) - break; - - if (QChar::fromLatin1(jsonDocument[i]) == x) - { - row++; - column = 0; - } - else - { - column++; - } - } - - - lastErrors.append(QCoreApplication::tr("JSON: %1
Row: %2, Column: %3").arg(parseError.errorString()).arg(row+1).arg(column+1)); - return appDescriptions; - } - - const QJsonObject root(appsDoc.object()); - - loginPluginIid = root[QStringLiteral("LoginPlugin")].toString(); - - const QJsonArray appArray(root[QStringLiteral("Apps")].toArray()); - if (appArray.isEmpty()) - { - lastErrors.append(QCoreApplication::tr("Invalid json: Could not find 'Apps' array")); - return appDescriptions; - } - - - for (const auto &appElem : appArray) - { - AppDescription appDesc; - const QJsonObject appObject = appElem.toObject(); - - - - appDesc.id = appFromInteger(appObject.value(QStringLiteral("Id")).toInt(-1)); - if (appDesc.id == App::Login) - { - lastErrors.append(QCoreApplication::tr("App '%1': no App Id or invalid App Id given").arg(appDesc.appName)); - return appDescriptions; - } - - appDesc.appName = appObject.value(QStringLiteral("AppName")).toString(); - - const QJsonValue valueActive = appObject.value(QStringLiteral("Active")); - if (!valueActive.isUndefined()) - { - if (valueActive.type() != QJsonValue::Type::Bool) - { - lastErrors.append(QCoreApplication::tr("App '%1': value App.Active is not a bool (%2)").arg(appDesc.appName).arg((int)valueActive.type())); - return appDescriptions; - } - } - appDesc.active = valueActive.toBool(false); - - const QJsonValue valueVisible = appObject.value(QStringLiteral("Visible")); - if (!valueVisible.isUndefined() && valueVisible.type() != QJsonValue::Type::Bool) - { - lastErrors.append(QCoreApplication::tr("App '%1': value App.Visible is not a bool (%2)").arg(appDesc.appName).arg((int)valueVisible.type())); - return appDescriptions; - } - appDesc.visible = valueVisible.toBool(true); - - const QJsonArray array(appObject.value(QStringLiteral("ACLs")).toArray()); - for (const QJsonValue &value : array) - appDesc.requiredAcls.append(value.toInt()); - - - - const QJsonObject optionsObject(appObject.value(QStringLiteral("Options")).toObject()); - - addOptionsToPlugin(appDesc, QStringLiteral("Payloads"), ignoredPlugins, optionsObject, PluginInfo::Flag::PAYLOAD, lastErrors, false); - addOptionsToPlugin(appDesc, QStringLiteral("Actions"), ignoredPlugins, optionsObject, PluginInfo::Flag::ACTIONS, lastErrors, true ); - addOptionsToPlugin(appDesc, QStringLiteral("Main"), ignoredPlugins, optionsObject, PluginInfo::Flag::MAIN, lastErrors, false); - - - appDescriptions.append(appDesc); - - } - - - - return appDescriptions; -} - -} diff --git a/Lts/Common/LtsAppDescriptor/lts_app_descriptor.h b/Lts/Common/LtsAppDescriptor/lts_app_descriptor.h deleted file mode 100755 index 34d217f12..000000000 --- a/Lts/Common/LtsAppDescriptor/lts_app_descriptor.h +++ /dev/null @@ -1,96 +0,0 @@ - - - -#pragma once - -#include -#include - -#ifndef LTSAPPDESCRIPTOR_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSAPPDESCRIPTOR_EXPORT -# else -# if defined(MAKE_LTSAPPDESCRIPTOR_LIB) -# define LTSAPPDESCRIPTOR_EXPORT Q_DECL_EXPORT -# else -# define LTSAPPDESCRIPTOR_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - - -namespace LtsAppDescriptor -{ - -enum class App -{ - Login = -1, - Situation = 0, - ResourceManager = 1, - PlanEditor = 2, - BuildingViewer = 3, - OC = 4, - OrgChart = 5, - Scripts = 6, - - Snapshot = 8, - SystemMonitor = 9, - Timeline = 10, - SymbolEditor = 11, - ScriptEditorEssentialsView = 12, - BaseDownloader = 13, - OnlineHelp = 14, - ThpSituationReplay = 15, - SituationTablet = 17, - SilhouetteEditor = 18, - Shootback = 19, - SAAs = 20, -}; - -struct PluginInfo -{ - enum class PluginType - { - UNKNOWN, - DATA, - GUI, - }; - - enum class Flag - { - UNKNOWN = 1 << 0, - ACTIONS = 1 << 1, - MAIN = 1 << 2, - PAYLOAD = 1 << 3, - OPTION = 1 << 4, - }; - - PluginType type = PluginType::UNKNOWN; - QString iid; - QFlags flags; - QStringList options; -}; - -struct AppDescription -{ - App id = App::Login; - bool active = false; - bool visible = true; - QString appName; - QVector requiredAcls; - QVector pluginInfos; - - bool isValid() const { return !appName.isEmpty(); } -}; - -typedef QVector AppDescriptionVector; - - -namespace LtsAppDescriptor -{ -LTSAPPDESCRIPTOR_EXPORT App appFromInteger(int i); -LTSAPPDESCRIPTOR_EXPORT AppDescriptionVector getAppDescriptionVector(const QByteArray &jsonDocument, QStringList &lastErrors, - const QStringList &ignoredPlugins, QString &loginPluginIid); -} - -} diff --git a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.cpp b/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.cpp deleted file mode 100755 index d90fe9c04..000000000 --- a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.cpp +++ /dev/null @@ -1,554 +0,0 @@ -#include "lts_app_descriptor_editor.h" -#include "ui_lts_app_descriptor_editor.h" - -#include -#include -#include -#include -#include -#include - -#include - -LtsAppDescriptorEditor::LtsAppDescriptorEditor(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui_LtsAppDescriptionEditor) -{ - m_ui->setupUi(this); - m_vector = LtsAppDescriptor::AppDescriptionVector(); - m_appIndex = QModelIndex(); - m_pluginIndex = QModelIndex(); - m_fileName = QString(); - m_changed = false; - - connect(m_ui->lvApps, SIGNAL(clicked(QModelIndex)), this, SLOT(onAppsClicked(QModelIndex))); - connect(m_ui->lvPlugins, SIGNAL(clicked(QModelIndex)), this, SLOT(onPluginsClicked(QModelIndex))); - connect(m_ui->lePluginType, SIGNAL(editingFinished()), this, SLOT(pluginTypeChanged())); - connect(m_ui->pbAddApp, SIGNAL(pressed()), this, SLOT(onAppAddClicked())); - connect(m_ui->pbAddPlugin, SIGNAL(pressed()), this, SLOT(onPluginAddClicked())); - connect(m_ui->pbDeleteApp, SIGNAL(pressed()), this, SLOT(onAppDeleteClicked())); - connect(m_ui->pbDeletePlugin, SIGNAL(pressed()), this, SLOT(onPluginDeletedClicked())); - connect(m_ui->pbUpdate, SIGNAL(pressed()), this, SLOT(onUpdateClicked())); - connect(m_ui->pbNewPlugin, SIGNAL(pressed()), this, SLOT(onNewpluginClicked())); - connect(m_ui->pbNewApp, SIGNAL(pressed()), this, SLOT(onNewAppClicked())); - connect(m_ui->lePluginName, SIGNAL(textChanged(QString)), this, SLOT(enableNewPlugin())); - connect(m_ui->lePluginType, SIGNAL(textChanged(QString)), this, SLOT(enableNewPlugin())); - - m_ui->gbPlugins->setEnabled(false); - m_ui->pbDeletePlugin->setEnabled(false); - m_ui->pbDeleteApp->setEnabled(false); - m_ui->pbAddPlugin->setEnabled(false); - m_ui->pbUpdate->setEnabled(false); - m_ui->pbNewPlugin->setEnabled(false); - m_ui->pbNewApp->setEnabled(false); - QTimer::singleShot(0, this, &LtsAppDescriptorEditor::getAppDescriptionVector); -} - -LtsAppDescriptorEditor::~LtsAppDescriptorEditor() -{ - checkSave(); - delete m_ui; -} - - -void LtsAppDescriptorEditor::getAppDescriptionVector() -{ - m_fileName = QFileDialog::getOpenFileName(this, tr("Open JSON"), QLatin1Literal("/shd/CTC/TOOLS/TDS/TDS-source/Lts/Projects/Legatus/resources/"), - tr("JSON File (*.json)")); - if (m_fileName.isEmpty()) - return; - - QFile file; - file.setFileName(m_fileName); - file.open(QIODevice::ReadOnly | QIODevice::Text); - QString val(QString::fromUtf8(file.readAll())); - file.close(); - - QStringList errors; - m_vector = LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(val.toUtf8(), errors); - - updateAppModel(); -} - -void LtsAppDescriptorEditor::onAppsClicked(const QModelIndex &index) -{ - m_ui->gbPlugins->setEnabled(true); - m_ui->pbDeleteApp->setEnabled(true); - m_ui->pbAddPlugin->setEnabled(true); - m_ui->pbNewApp->setEnabled(true); - m_appIndex = index; - - QStringList pluginList; - Q_FOREACH(const LtsAppDescriptor::PluginInfo &reqPlugin, m_vector.at(m_appIndex.row()).requiredPlugins) - { - LtsAppDescriptor::PluginInfo::PluginType pluginType = reqPlugin.type; - QString type; - if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::DATA) - type = QLatin1String("data"); - else if(pluginType == LtsAppDescriptor::PluginInfo::PluginType::GUI) - type = QLatin1String("gui"); - else - type = QLatin1String("unknown"); - - if(type == QLatin1String("data")) - pluginList << QString(type + QLatin1String(": ") +reqPlugin.iid); - else - { - QString strFlags; - QFlags flags = reqPlugin.flags; - - bool actionsFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS); - if(actionsFlag) - strFlags.append(QLatin1String("actions ")); - bool mainFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN); - if(mainFlag) - strFlags.append(QLatin1String("main ")); - bool payloadFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::PAYLOAD); - if(payloadFlag) - strFlags.append(QLatin1String("payload ")); - bool optionalFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::OPTION); - if(optionalFlag) - strFlags.append(QLatin1String("optional ")); - - pluginList << QString(type + QLatin1String(": ") + reqPlugin.iid + QLatin1String(" [ ") + strFlags + QLatin1String("]")); - } - - } - updatePluginModel(pluginList); - - m_ui->chbAppActive->setChecked(m_vector.at(m_appIndex.row()).active); - m_ui->leAppName->setText(m_vector.at(m_appIndex.row()).appName); -} - -void LtsAppDescriptorEditor::onPluginsClicked(const QModelIndex &index) -{ - m_pluginIndex = index; - m_ui->pbDeletePlugin->setEnabled(true); - m_ui->pbUpdate->setEnabled(true); - m_ui->pbNewPlugin->setEnabled(true); - - const LtsAppDescriptor::PluginInfo plugin = m_vector.at(m_appIndex.row()).requiredPlugins.at(m_pluginIndex.row()); - QString pluginName = plugin.iid; - m_ui->lePluginName->setText(pluginName); - - LtsAppDescriptor::PluginInfo::PluginType pluginType = plugin.type; - QString type; - if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::DATA) - type = QLatin1String("data"); - else if(pluginType == LtsAppDescriptor::PluginInfo::PluginType::GUI) - type = QLatin1String("gui"); - else - type = QLatin1String("unknown"); - m_ui->lePluginType->setText(type); - - if(type == QLatin1String("gui")) - { - m_ui->chbActions->setEnabled(true); - m_ui->chbMain->setEnabled(true); - m_ui->chbPayload->setEnabled(true); - - QFlags flags = plugin.flags; - bool actionsFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS); - m_ui->chbActions->setChecked(actionsFlag); - bool mainFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN); - m_ui->chbMain->setChecked(mainFlag); - bool payloadFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::PAYLOAD); - m_ui->chbPayload->setChecked(payloadFlag); - } - else - { - m_ui->chbActions->setChecked(false); - m_ui->chbActions->setEnabled(false); - m_ui->chbMain->setChecked(false); - m_ui->chbMain->setEnabled(false); - m_ui->chbPayload->setChecked(false); - m_ui->chbPayload->setEnabled(false); - } -} - -void LtsAppDescriptorEditor::pluginTypeChanged() -{ - if(m_ui->lePluginType->text().isEmpty()) - return; - - if(m_ui->lePluginType->text() == QLatin1String("gui")) - { - m_ui->chbActions->setEnabled(true); - m_ui->chbMain->setEnabled(true); - m_ui->chbPayload->setEnabled(true); - } - else if(m_ui->lePluginType->text() == QLatin1String("data")) - { - m_ui->chbActions->setChecked(false); - m_ui->chbActions->setEnabled(false); - m_ui->chbMain->setChecked(false); - m_ui->chbMain->setEnabled(false); - m_ui->chbPayload->setChecked(false); - m_ui->chbPayload->setEnabled(false); - } - else - { - QMessageBox msgBox(QMessageBox::Information, - tr("Error"), - tr("Plugin Type must either be \"gui\" or \"data\".")); - msgBox.exec(); - m_ui->lePluginType->setText(QString()); - } -} - -void LtsAppDescriptorEditor::onAppAddClicked() -{ - m_changed = true; - - if(m_ui->leAppName->text().isEmpty()) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("The App needs to have a name.")); - msgBox.exec(); - return; - } - - QStringList appList; - Q_FOREACH(const LtsAppDescriptor::AppDescription &appDesc, m_vector) - { - appList << appDesc.appName; - } - - if(appList.contains(m_ui->leAppName->text(),Qt::CaseInsensitive)) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("There already exists an app named \"%1\".").arg(m_ui->leAppName->text())); - msgBox.exec(); - return; - } - - LtsAppDescriptor::AppDescription newApp; - newApp.appName = m_ui->leAppName->text(); - newApp.active = m_ui->chbAppActive->isChecked(); - m_vector.append(newApp); - - updateAppModel(); - -} - -void LtsAppDescriptorEditor::onPluginAddClicked() -{ - m_changed = true; - - if(m_ui->lePluginType->text().isEmpty()) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("The Plugin needs to contain a type.")); - msgBox.exec(); - return; - } - if(m_ui->lePluginName->text().isEmpty()) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("The Plugin needs to have a name.")); - msgBox.exec(); - return; - } - - QStringList pluginList; - Q_FOREACH(const LtsAppDescriptor::PluginInfo &pluginDesc, m_vector.at(m_appIndex.row()).requiredPlugins) - { - pluginList << pluginDesc.iid; - } - - if(pluginList.contains(m_ui->lePluginName->text(),Qt::CaseInsensitive)) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("There already exists an plugin named \"%1\".").arg(m_ui->lePluginName->text())); - msgBox.exec(); - return; - } - - - LtsAppDescriptor::PluginInfo newPlugin; - - newPlugin.iid = m_ui->lePluginName->text(); - - if(m_ui->lePluginType->text() == QLatin1String("gui")) - newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::GUI; - else if(m_ui->lePluginType->text() == QLatin1String("data")) - newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - else - newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::UNKNOWN; - - if(m_ui->chbActions->isChecked()) - newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS; - if(m_ui->chbMain->isChecked()) - newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::MAIN; - if(m_ui->chbPayload->isChecked()) - newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::PAYLOAD; - - m_vector[m_appIndex.row()].requiredPlugins.append(newPlugin); - - onAppsClicked(m_appIndex); - onNewpluginClicked(); - -} - -void LtsAppDescriptorEditor::onAppDeleteClicked() -{ - m_changed = true; - - if(!m_appIndex.isValid()) return; - - QMessageBox msgBox(tr("Warning"), - tr("The App \n\"%1\" \nwill be removed. Continue?").arg(m_appIndex.data().toString()), - QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::Default, - QMessageBox::No | QMessageBox::Escape, - QMessageBox::NoButton); - - if (msgBox.exec() == QMessageBox::No) - return; - - m_vector.remove(m_appIndex.row()); - - updateAppModel(); - -} - -void LtsAppDescriptorEditor::onPluginDeletedClicked() -{ - m_changed = true; - - if(!m_pluginIndex.isValid()) return; - - QMessageBox msgBox(tr("Warning"), - tr("The Plugin \n\"%1\" \nwill be removed. Continue?").arg(m_pluginIndex.data().toString()), - QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::Default, - QMessageBox::No | QMessageBox::Escape, - QMessageBox::NoButton); - - if (msgBox.exec() == QMessageBox::No) - return; - - m_vector[m_appIndex.row()].requiredPlugins.remove(m_pluginIndex.row()); - m_pluginIndex = QModelIndex(); - m_ui->lePluginName->setText(QString()); - m_ui->lePluginType->setText(QString()); - m_ui->chbActions->setChecked(false); - m_ui->chbMain->setChecked(false); - m_ui->chbPayload->setChecked(false); - - m_ui->pbDeletePlugin->setEnabled(false); - m_ui->pbUpdate->setEnabled(false); - m_ui->pbNewPlugin->setEnabled(false); - - onAppsClicked(m_appIndex); -} - -void LtsAppDescriptorEditor::onUpdateClicked() -{ - m_changed = true; - - if(m_ui->lePluginType->text().isEmpty()) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("The Plugin needs to contain a type.")); - msgBox.exec(); - return; - } - if(m_ui->lePluginName->text().isEmpty()) - { - QMessageBox msgBox(QMessageBox::Critical, - tr("Error"), - tr("The Plugin needs to have a name.")); - msgBox.exec(); - return; - } - - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].iid = m_ui->lePluginName->text(); - - if(m_ui->lePluginType->text() == QLatin1String("gui")) - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type = LtsAppDescriptor::PluginInfo::PluginType::GUI; - else if(m_ui->lePluginType->text() == QLatin1String("data")) - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - else - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type = LtsAppDescriptor::PluginInfo::PluginType::UNKNOWN; - - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags = QFlags(); - - if(m_ui->chbActions->isChecked()) - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS; - if(m_ui->chbMain->isChecked()) - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |= LtsAppDescriptor::PluginInfo::Flag::MAIN; - if(m_ui->chbPayload->isChecked()) - m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |= LtsAppDescriptor::PluginInfo::Flag::PAYLOAD; - - onAppsClicked(m_appIndex); - onNewpluginClicked(); -} - -void LtsAppDescriptorEditor::onNewpluginClicked() -{ - m_ui->lePluginName->setText(QString()); - m_ui->lePluginType->setText(QString()); - m_ui->chbActions->setEnabled(true); - m_ui->chbMain->setEnabled(true); - m_ui->chbPayload->setEnabled(true); - m_ui->chbActions->setChecked(false); - m_ui->chbMain->setChecked(false); - m_ui->chbPayload->setChecked(false); - m_ui->pbUpdate->setEnabled(false); - m_ui->pbNewPlugin->setEnabled(false); - m_ui->pbDeletePlugin->setEnabled(false); - - if(m_pluginIndex.isValid()) - m_ui->lvPlugins->selectionModel()->select(m_pluginIndex,QItemSelectionModel::Deselect); - m_pluginIndex = QModelIndex(); -} - -void LtsAppDescriptorEditor::onNewAppClicked() -{ - updatePluginModel(QStringList()); - - m_ui->gbPlugins->setEnabled(false); - m_ui->pbDeletePlugin->setEnabled(false); - m_ui->pbDeleteApp->setEnabled(false); - m_ui->pbAddPlugin->setEnabled(false); - m_ui->pbUpdate->setEnabled(false); - m_ui->pbNewPlugin->setEnabled(false); - m_ui->leAppName->setText(QString()); - m_ui->chbAppActive->setChecked(false); - m_ui->lePluginName->setText(QString()); - m_ui->lePluginType->setText(QString()); - m_ui->chbActions->setChecked(false); - m_ui->chbMain->setChecked(false); - m_ui->chbPayload->setChecked(false); - m_ui->pbNewApp->setEnabled(false); - - if(m_appIndex.isValid()) - m_ui->lvApps->selectionModel()->select(m_appIndex,QItemSelectionModel::Deselect); - m_appIndex = QModelIndex(); -} - -void LtsAppDescriptorEditor::updateAppModel() -{ - onNewAppClicked(); - QStringList appList; - Q_FOREACH(const LtsAppDescriptor::AppDescription &appDesc, m_vector) - { - appList << appDesc.appName; - } - - if (!m_ui->lvApps->model()) - m_ui->lvApps->setModel(new QStringListModel(this)); - QStringListModel *m = qobject_cast(m_ui->lvApps->model()); - if (!m) - return; - m->setStringList(appList); -} - -void LtsAppDescriptorEditor::updatePluginModel(const QStringList &list) -{ - if (!m_ui->lvPlugins->model()) - m_ui->lvPlugins->setModel(new QStringListModel(this)); - QStringListModel *m = qobject_cast(m_ui->lvPlugins->model()); - if (!m) - return; - m->setStringList(list); -} - -void LtsAppDescriptorEditor::checkSave() -{ - if(m_changed) - { - QMessageBox msgBox(tr("Warning"), - tr("The Data has been modified.\nDo you want to save your changes?"), - QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::Default, - QMessageBox::No | QMessageBox::Escape, - QMessageBox::NoButton); - - if (msgBox.exec() == QMessageBox::Yes) - onSaveAsClicked(); - } -} - -void LtsAppDescriptorEditor::onSaveAsClicked() -{ - QString fileName = QFileDialog::getSaveFileName(this, tr("Save JSON"), QLatin1Literal("/shd/CTC/TOOLS/TDS/TDS-source/Lts/Projects/Legatus/resources/"), - tr("JSON File (*.json)")); - if(fileName.isEmpty()) - return; - - QByteArray jsonByteArray; - jsonByteArray = LtsAppDescriptor::LtsAppDescriptor::createJson(m_vector); - - QFile f(fileName); - f.open(QIODevice::WriteOnly); - f.write(jsonByteArray); - f.close(); - - m_changed = false; - - QMessageBox msgBox(QMessageBox::Information, - tr("Success"), - tr("Your file has been saved.")); - msgBox.exec(); -} - -void LtsAppDescriptorEditor::onSaveClicked() -{ - const QByteArray jsonByteArray(LtsAppDescriptor::LtsAppDescriptor::createJson(m_vector)); - - QFile f(m_fileName); - f.open(QIODevice::WriteOnly); - f.write(jsonByteArray); - f.close(); - - m_changed = false; - - QMessageBox msgBox(QMessageBox::Information, - tr("Success"), - tr("Your file has been saved.")); - msgBox.exec(); -} - -void LtsAppDescriptorEditor::onOpenClicked() -{ - checkSave(); - getAppDescriptionVector(); - m_changed = false; -} - -void LtsAppDescriptorEditor::onNewClicked() -{ - checkSave(); - m_ui->chbActions->setChecked(false); - m_ui->chbMain->setChecked(false); - m_ui->chbPayload->setChecked(false); - m_ui->chbAppActive->setChecked(false); - m_ui->leAppName->setText(QString()); - m_ui->lePluginName->setText(QString()); - m_ui->lePluginType->setText(QString()); - m_ui->gbPlugins->setEnabled(false); - - m_vector = LtsAppDescriptor::AppDescriptionVector(); - - updateAppModel(); - updatePluginModel(QStringList()); - -} - -void LtsAppDescriptorEditor::enableNewPlugin() -{ - m_ui->pbNewPlugin->setEnabled(true); -} - -void LtsAppDescriptorEditor::onQuitClicked() -{ - QApplication::quit(); -} diff --git a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.h b/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.h deleted file mode 100755 index 97be5c4ff..000000000 --- a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include - -#include "lts_app_descriptor.h" - -#ifndef LTSAPPDESCRIPTOREDITOR_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSAPPDESCRIPTOREDITOR_EXPORT -# else -# if defined(MAKE_LTSAPPDESCRIPTOREDITOR_LIB) -# define LTSAPPDESCRIPTOREDITOR_EXPORT Q_DECL_EXPORT -# else -# define LTSAPPDESCRIPTOREDITOR_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - -class Ui_LtsAppDescriptionEditor; - - -class LTSAPPDESCRIPTOREDITOR_EXPORT LtsAppDescriptorEditor : public QWidget -{ - Q_OBJECT - -public: - explicit LtsAppDescriptorEditor(QWidget *parent = 0); - ~LtsAppDescriptorEditor(); - -private: - Ui_LtsAppDescriptionEditor *m_ui; - LtsAppDescriptor::AppDescriptionVector m_vector; - QModelIndex m_appIndex; - QModelIndex m_pluginIndex; - - QString m_fileName; - - bool m_changed; - - void getAppDescriptionVector(); - void setupMainWindow(); - void updateAppModel(); - void updatePluginModel(const QStringList &list); - void checkSave(); - -private slots: - void onAppsClicked(const QModelIndex &index); - void onPluginsClicked(const QModelIndex &index); - void pluginTypeChanged(); - void onAppAddClicked(); - void onPluginAddClicked(); - void onAppDeleteClicked(); - void onPluginDeletedClicked(); - void onUpdateClicked(); - void onNewpluginClicked(); - void onNewAppClicked(); - void enableNewPlugin(); - -public slots: - void onSaveClicked(); - void onSaveAsClicked(); - void onOpenClicked(); - void onNewClicked(); - void onQuitClicked(); -}; diff --git a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.ui b/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.ui deleted file mode 100755 index a791a3218..000000000 --- a/Lts/Common/LtsAppDescriptor/lts_app_descriptor_editor.ui +++ /dev/null @@ -1,434 +0,0 @@ - - - LtsAppDescriptionEditor - - - - 0 - 0 - 865 - 337 - - - - - 0 - 0 - - - - - - - true - - - - - - Required Plugins: - - - - - - - App: - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - QAbstractItemView::NoEditTriggers - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - New - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - Name: - - - Qt::AlignCenter - - - - - - - Delete - - - - - - - - 0 - 0 - - - - New Plugin Requirements - - - - - - Type: - - - - - - - - 0 - 0 - - - - - 210 - 0 - - - - - 200 - 16777215 - - - - gui/data - - - - - - - IID: - - - - - - - - 0 - 0 - - - - - 210 - 0 - - - - Name of the Plugin - - - - - - - Flags: - - - - - - - - 0 - 25 - - - - Actions - - - - - - - - 0 - 25 - - - - Main - - - - - - - - 0 - 25 - - - - Payload - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 0 - - - - - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - Active: - - - Qt::AlignCenter - - - - - - - Delete - - - - - - - - 0 - 0 - - - - - 300 - 0 - - - - QAbstractItemView::NoEditTriggers - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Update - - - - - - - - 0 - 0 - - - - - 100 - 30 - - - - - 100 - 30 - - - - Name of the App - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Add - - - - - - - New - - - - - - - - 0 - 0 - - - - - 80 - 0 - - - - - - - - - - - Add - - - - - - - - 0 - 0 - - - - List of Apps - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - lvApps - pbNewApp - pbAddApp - pbDeleteApp - chbAppActive - leAppName - lvPlugins - pbDeletePlugin - pbNewPlugin - lePluginType - lePluginName - chbPayload - chbMain - chbActions - pbAddPlugin - pbUpdate - - - - diff --git a/Lts/Common/LtsAppDescriptor/main.cpp b/Lts/Common/LtsAppDescriptor/main.cpp deleted file mode 100755 index 1904bac5c..000000000 --- a/Lts/Common/LtsAppDescriptor/main.cpp +++ /dev/null @@ -1,56 +0,0 @@ - - - -#include "lts_app_descriptor_editor.h" -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - - QMainWindow *mainWindow = new QMainWindow(); - QMenu *fileMenu = mainWindow->menuBar()->addMenu(QObject::tr("&File")); - - QAction *openAct = new QAction(QObject::tr("&Open"), mainWindow); - openAct->setShortcuts(QKeySequence::Open); - openAct->setStatusTip(QObject::tr("Open an existing file")); - fileMenu->addAction(openAct); - - QAction *newAct = new QAction(QObject::tr("&New"), mainWindow); - newAct->setShortcuts(QKeySequence::New); - newAct->setStatusTip(QObject::tr("Create a new App")); - fileMenu->addAction(newAct); - - QAction *saveAct = new QAction(QObject::tr("&Save"), mainWindow); - saveAct->setShortcuts(QKeySequence::Save); - saveAct->setStatusTip(QObject::tr("Save modified file")); - fileMenu->addAction(saveAct); - - QAction *saveAsAct = new QAction(QObject::tr("&SaveAs"), mainWindow); - saveAsAct->setShortcuts(QKeySequence::SaveAs); - saveAsAct->setStatusTip(QObject::tr("Save modified file as")); - fileMenu->addAction(saveAsAct); - - QAction *quitAct = new QAction(QObject::tr("&Quit"), mainWindow); - quitAct->setShortcuts(QKeySequence::Quit); - quitAct->setStatusTip(QObject::tr("Quit the Application")); - fileMenu->addAction(quitAct); - - - LtsAppDescriptorEditor *editor = new LtsAppDescriptorEditor(mainWindow); - mainWindow->setCentralWidget(editor); - QObject::connect(openAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onOpenClicked); - QObject::connect(newAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onNewClicked); - QObject::connect(saveAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onSaveClicked); - QObject::connect(saveAsAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onSaveAsClicked); - QObject::connect(quitAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onQuitClicked); - - mainWindow->show(); - - int ret = a.exec(); - delete mainWindow; - return ret; -} diff --git a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp b/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp deleted file mode 100755 index 280de0beb..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp +++ /dev/null @@ -1,35 +0,0 @@ - - - -#include "app.h" -#include "ui_app.h" -#include "AppChooser/app_chooser_model.h" - - -namespace LtsAppManager -{ - -CustomViewApp::CustomViewApp(QWidget *parent) - : AbstractDisplayWidget(parent) - , m_ui(new Ui::CustomViewApp) -{ - m_ui->setupUi(this); - m_ui->image->setMenuEnabled(false); -} - -CustomViewApp::~CustomViewApp() -{ - delete m_ui; -} - -bool CustomViewApp::init(const QModelIndex &idx) -{ - if (!idx.isValid()) - return false; - - m_ui->lbAppName->setText(rowData(idx, AppChooserModel::Name).toString()); - - return true; -} - -} diff --git a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h b/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h deleted file mode 100755 index 096e7ffef..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h +++ /dev/null @@ -1,30 +0,0 @@ - - -#pragma once - -#include - - -namespace Ui -{ -class CustomViewApp; -} - -namespace LtsAppManager -{ - -class CustomViewApp final : public LtsDataView::AbstractDisplayWidget -{ - Q_OBJECT - -public: - explicit CustomViewApp(QWidget *parent = nullptr); - ~CustomViewApp(); - bool init(const QModelIndex &idx) override; - -private: - Ui::CustomViewApp* m_ui; - -}; - -} diff --git a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui b/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui deleted file mode 100755 index d4653b571..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui +++ /dev/null @@ -1,108 +0,0 @@ - - - CustomViewApp - - - - 0 - 0 - 300 - 150 - - - - - 0 - 0 - - - - - - - - - - - 0 - 0 - - - - - 128 - 128 - - - - - 128 - 128 - - - - - - - - - - - - <App Name> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - <html><head/><body><p>&lt;Description with wordwrap&gt;</p></body></html> - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - LtsWidgets::LtsImageEditor - QWidget -
LtsWidgets/lts_image_editor.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp b/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp deleted file mode 100755 index aaf658738..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp +++ /dev/null @@ -1,93 +0,0 @@ - - -#include "AppChooser/app_chooser.h" -#include "ui_app_chooser.h" -#include "AppChooser/app_chooser_model.h" -#include "lts_app_manager.h" -#include "AppChooser/CustomViews/app.h" - -#include - -#include - - -namespace LtsAppManager -{ - -AppChooser::AppChooser() - : QWidget(nullptr) - , m_ui(new Ui::AppChooser) - , m_model() - , m_madeSelection(false) -{ - m_ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalDoubleClicked, - this, &AppChooser::slotAppSelected); - connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalEnterPressed, - this, &AppChooser::slotAppSelected); - connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalSelectionChanged, - this, &AppChooser::enaDisa); - connect(m_ui->pbQuit, &QPushButton::clicked, - this, &QWidget::close); - connect(m_ui->pbSelect, &QPushButton::clicked, - this, &AppChooser::slotAppSelected); - - m_model = new AppChooserModel(this); - m_model->initModel(LtsAppManager::getInstance()->getAvailableApps()); - - m_ui->widget->seamless(); - - m_ui->widget->setModel(m_model); - m_ui->widget->hideColumn(AppChooserModel::Columns::Visible); -} - - -AppChooser::~AppChooser() -{ - - delete m_ui; -} - - -void AppChooser::slotQuit() -{ - if (m_alreadyCloseEmitted) - return; - m_alreadyCloseEmitted = true; - emit signalLogout(); -} - - -void AppChooser::closeEvent(QCloseEvent *) -{ - if (!m_madeSelection) - slotQuit(); -} - - -void AppChooser::slotAppSelected() -{ - const QModelIndex idx = m_ui->widget->currentIndex(); - const LtsAppDescriptor::AppDescription app = m_model->getRowData(idx.row()); - - if (!app.active) - return; - - hide(); - m_madeSelection = true; - emit signalAppSelected(app.id); - close(); -} - - -void AppChooser::enaDisa() -{ - const QModelIndexList selectedRows = m_ui->widget->selectedRows(); - const int cntSelectedRows = selectedRows.size(); - - m_ui->pbSelect->setEnabled(cntSelectedRows == 1); -} - -} diff --git a/Lts/Common/LtsAppManager/AppChooser/app_chooser.h b/Lts/Common/LtsAppManager/AppChooser/app_chooser.h deleted file mode 100755 index 27fdad5ba..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/app_chooser.h +++ /dev/null @@ -1,47 +0,0 @@ - -#pragma once - -#include -#include - -namespace Ui -{ -class AppChooser; -} - - -namespace LtsAppManager -{ - -class AppChooserModel; - -class AppChooser : public QWidget -{ - Q_OBJECT - -public: - AppChooser(); - ~AppChooser(); - -private: - Ui::AppChooser *m_ui; - AppChooserModel *m_model; - bool m_madeSelection; - bool m_alreadyCloseEmitted { false }; - - void enaDisa(); - -protected: - void closeEvent(QCloseEvent *); - -signals: - void signalAppSelected(LtsAppDescriptor::App appId); - void signalLogout(); - -private slots: - void slotAppSelected(); - void slotQuit(); - -}; - -} diff --git a/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui b/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui deleted file mode 100755 index 841d60178..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui +++ /dev/null @@ -1,92 +0,0 @@ - - - AppChooser - - - - 0 - 0 - 800 - 400 - - - - - 0 - 0 - - - - App Chooser - - - - :/rde_icon_256.png:/rde_icon_256.png - - - - - - Select App: - - - - - - - - 0 - 0 - - - - - - - - - - Quit - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - Select - - - - - - - - - - LtsDataView::LtsDataView - QWidget -
Viewer/LtsDataView/lts_data_view.h
- 1 -
-
- - - - -
diff --git a/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp b/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp deleted file mode 100755 index 505c50c0a..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp +++ /dev/null @@ -1,50 +0,0 @@ - - -#include "app_chooser_model.h" - -#include - - -namespace LtsAppManager -{ - -AppChooserModel::AppChooserModel(QObject *parent) - : LtsWidgets::LtsAbstractTableModel({tr("Name")}, parent) - -{} - - -bool AppChooserModel::initModel(const LtsAppDescriptor::AppDescriptionVector &availableApps) -{ - for (auto i = availableApps.constBegin(); i != availableApps.constEnd(); ++i) - { - const LtsAppDescriptor::AppDescription& appDesc = *i; - - if (!appDesc.visible) - continue; - - insertRow(appDesc); - } - - return true; -} - - -QVariant AppChooserModel::data(const QModelIndex &index, int role) const -{ - if (role != Qt::DisplayRole || !indexIsValid(index)) - return QVariant(); - - const LtsAppDescriptor::AppDescription &ad = m_vecData.at(index.row()); - - switch (index.column()) - { - case Name: - return LtsSession::LtsSession::appToString(ad.id); - case Visible: - return ad.visible; - } - - return QVariant(); -} -} diff --git a/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h b/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h deleted file mode 100755 index e83b87d46..000000000 --- a/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h +++ /dev/null @@ -1,29 +0,0 @@ - -#pragma once - - -#include -#include - - - -namespace LtsAppManager -{ -class AppChooserModel final : public LtsWidgets::LtsAbstractTableModel -{ - Q_OBJECT - -public: - enum Columns - { - Name = 0, - Visible, - }; - - explicit AppChooserModel(QObject *parent = nullptr); - - bool initModel(const LtsAppDescriptor::AppDescriptionVector &availableApps); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override final; -}; -} diff --git a/Lts/Common/LtsAppManager/CMakeLists.txt b/Lts/Common/LtsAppManager/CMakeLists.txt deleted file mode 100755 index 422572abf..000000000 --- a/Lts/Common/LtsAppManager/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-10-27 -# - - -# LtsAppManager - -set(LtsAppManager_SRCS - lts_app_manager.cpp - AppChooser/app_chooser.cpp - AppChooser/app_chooser_model.cpp - AppChooser/CustomViews/app.cpp -) - -NGL_WRAP_UI(LtsAppManager_SRCS - AppChooser/app_chooser.ui - AppChooser/CustomViews/app.ui -) - -NGL_ADD_LIBRARY(WS LtsAppManager SHARED ${LtsAppManager_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppManager - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_SOURCE_DIR}/AppChooser/ -) - -NGL_TARGET_LINK_LIBRARIES(LtsAppManager - PRIVATE - NGLLogger - LtsDataView - LtsWidgets - LtsLogin - LtsPluginManager - LtsSession - LtsSessionData - LtsUtilsSettings - LtsMultiMonitor - ProjectConfig - Qt5::Gui - Qt5::Widgets - Qt5::Network # debug server - OpmSettingsGuiPluginInterface - LtsPluginLoader - PUBLIC - Qt5::Core - LtsAppDescriptor -) - -NGL_INSTALL_TARGET(LtsAppManager) diff --git a/Lts/Common/LtsAppManager/lts_app_manager.cpp b/Lts/Common/LtsAppManager/lts_app_manager.cpp deleted file mode 100755 index 66b7fcfb3..000000000 --- a/Lts/Common/LtsAppManager/lts_app_manager.cpp +++ /dev/null @@ -1,1151 +0,0 @@ -#include "lts_app_manager.h" -#include "AppChooser/app_chooser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - -#ifndef LTS_VERSION_MAJOR - #define LTS_VERSION_MAJOR "undefined" -#endif -#ifndef LTS_VERSION_MINOR - #define LTS_VERSION_MINOR "undefined" -#endif -#ifndef LTS_VERSION_PATCH - #define LTS_VERSION_PATCH "undefined" -#endif -#ifndef JENKINS_BUILD_NUMBER - #define JENKINS_BUILD_NUMBER "unknown" -#endif -static const char ver_major[] = LTS_VERSION_MAJOR; -static const char ver_minor[] = LTS_VERSION_MINOR; -static const char ver_patch[] = LTS_VERSION_PATCH; -static const char ver_jenkins[] = JENKINS_BUILD_NUMBER; - - -namespace LtsAppManager -{ - -struct LtsAppManagerHandler -{ - LtsAppManager ltsAppManager; -}; - -Q_GLOBAL_STATIC(LtsAppManagerHandler, s_ltsAppManager) - - -LtsAppManager *LtsAppManager::getInstance() -{ - return &s_ltsAppManager->ltsAppManager; -} - - -LtsAppManager::LtsAppManager() - : m_login(nullptr) -{ - - qRegisterMetaType("LtsAppDescriptor::App"); - slotStartDebugServer(); - connect(qApp, &QCoreApplication::aboutToQuit, this, [this] { delete m_login; m_login = nullptr; }); -} - - -LtsAppManager::~LtsAppManager() -{ - - delete m_login; - m_login = nullptr; -} - - -void LtsAppManager::setAvailableApps(const LtsAppDescriptor::AppDescriptionVector &availableApps) -{ - m_availableApps = availableApps; - - - - - - -#ifndef LTS_DEBUG - if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("PluginManager.CheckAppRequirements")) == QVariant(true)) - { - QMessageBox::information(nullptr, QStringLiteral("LtsAppManager"), QStringLiteral("Checking App Requirements now")); -#else - { -#endif - QStringList errors; - LtsPluginManager::LtsPluginLoader::getInstance()->checkPlugins(availableApps, errors); - - if (!errors.isEmpty()) - QMessageBox::critical(nullptr, QStringLiteral("LtsAppManager"), QStringLiteral("App Requirements check failed!
- %1").arg(errors.join(QStringLiteral("
- ")))); - } - - - if (m_availableApps.isEmpty()) - { - QMessageBox::critical(nullptr, tr("App Manager"), tr("There are no apps available for this project.\nPlease contact Support.")); - abort(); - } -} - - -void LtsAppManager::slotStartDebugServer() -{ - QTcpServer *debugServer = new QTcpServer(this); - - if (!debugServer->listen(QHostAddress::Any, 20000)) - { - debugServer->deleteLater(); - const int waitTryAgain = 5; -#ifdef LTS_DEBUG - -#endif - QTimer::singleShot(waitTryAgain * 1000, this, &LtsAppManager::slotStartDebugServer); - return; - } - - connect(debugServer, &QTcpServer::newConnection, this, [this]() - { - QTcpServer *server = qobject_cast(sender()); - - if (server == nullptr) - return; - - QTcpSocket *client = server->nextPendingConnection(); - - if (client == nullptr) - return; - - connect(client, &QTcpSocket::readyRead, this, [this]() - { - QTcpSocket *client = qobject_cast(sender()); - - if (client == nullptr) - return; - - QString commandStr(QString::fromUtf8(client->readLine(100)).remove(QStringLiteral("\r")).remove(QStringLiteral("\n"))); - - QStringList argList = commandStr.split(QLatin1Char(' '), QString::SplitBehavior::SkipEmptyParts); - - if (argList.count() < 1) - return; - - const QString &command = argList.at(0); - - LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance(); - static QStringList commands; - commands << QStringLiteral("showSessions") - << QStringLiteral("operator") - << QStringLiteral("help") - << QStringLiteral("logout") - << QStringLiteral("flag") - << QStringLiteral("setFlag") - << QStringLiteral("start") - << QStringLiteral("voiceCommand") - << QStringLiteral("ftcmock") - ; - - switch (commands.indexOf(command)) - { - case 0: - { - sessionData->setDebugFlagValue(QStringLiteral("DeveloperTools.ShowSessionsOnce"), true); - client->write("K"); - break; - } - - case 1: - { - QString operatorName(sessionData->getOperatorName()); - - if (operatorName.isEmpty()) - operatorName = QStringLiteral("(none)"); - - QString result(QStringLiteral("%1").arg(operatorName)); - client->write(result.toUtf8()); - break; - } - - case 2: - { - QString result(QStringLiteral("Available Commands: %1").arg(commands.join(QStringLiteral(", ")))); - client->write(result.toUtf8()); - break; - } - - case 3: - { - client->write("..."); - slotLogout(); - client->write("\r\nK"); - break; - } - - case 4: - { - if (argList.count() != 2) - { - client->write("Usage: flag "); - break; - } - - const QString &debugFlagName = argList.at(1); - if (!sessionData->hasDebugFlag(debugFlagName)) - { - client->write("(unknown)"); - break; - } - - client->write(sessionData->debugFlagValue(argList.at(1)).toString().toUtf8()); - break; - } - - case 5: - { - if (argList.count() < 3) - { - client->write("Usage: setFlag \r\nFlag types: int, string (no spaces allowed yet), boolean, double"); - break; - } - - static QStringList flagTypes; - flagTypes << QStringLiteral("int") - << QStringLiteral("string") - << QStringLiteral("boolean") - << QStringLiteral("double"); - - const QString &flagType = argList.at(2); - bool k = false; - - const QString &flagName = argList.at(1); - - switch (flagTypes.indexOf(flagType)) - { - case 0: - { - int value = argList.at(3).toInt(&k); - if (k) - sessionData->setDebugFlagValue(flagName, value); - else - client->write("cannot convert value to integer"); - - break; - } - - case 1: - { - sessionData->setDebugFlagValue(flagName, argList.at(3)); - break; - } - - case 2: - { - QString value(argList.at(3).toLower().simplified()); - if (value == QStringLiteral("true")) - sessionData->setDebugFlagValue(flagName, true); - else if (value == QStringLiteral("false")) - sessionData->setDebugFlagValue(flagName, false); - else - client->write("cannot convert value to boolean"); - - break; - } - - case 3: - { - double value = argList.at(3).toDouble(&k); - if (k) - sessionData->setDebugFlagValue(flagName, value); - else - client->write("cannot convert value to double"); - - break; - } - - - default: - { - client->write("unknown flag type"); - break; - } - } - - break; - } - - case 6: - { - if (argList.count() < 3) - { - client->write("Usage: start "); - break; - } - - - - - - - - - - - client->write("not implemented yet"); - break; - } - - case 7: - { - if (argList.count() < 2) - { - client->write("Usage: voiceCommand "); - break; - } - - static QStringList keywords; - keywords << QStringLiteral("AddMarker") - << QStringLiteral("SetMarker") - << QStringLiteral("AddSnapshot") - << QStringLiteral("SaveSnapshot") ; - - - const QString &keyword = argList.at(1); - - switch (keywords.indexOf(keyword)) - { - case 0: - { - auto ev = new LtsWidgets::AddMarkerEvent; - ev->setAccepted(false); - sessionData->setDebugFlagValue(command, keyword); - QCoreApplication::postEvent(qApp, ev); - break; - } - case 1: - { - auto ev = new LtsWidgets::SetMarkerEvent(QString()); - if (argList.size() > 2) - ev->setDescription(argList.at(2)); - ev->setAccepted(false); - sessionData->setDebugFlagValue(command, keyword); - QCoreApplication::postEvent(qApp, ev); - break; - } - case 2: - { - auto ev = new LtsWidgets::AddSnapshotEvent; - ev->setAccepted(false); - sessionData->setDebugFlagValue(command, keyword); - QCoreApplication::postEvent(qApp, ev); - break; - } - case 3: - { - auto ev = new LtsWidgets::SaveSnapshotEvent(QString()); - if (argList.size() > 2) - ev->setDescription(argList.at(2)); - ev->setAccepted(false); - sessionData->setDebugFlagValue(command, keyword); - QCoreApplication::postEvent(qApp, ev); - break; - } - default: - break; - } - - if (!sessionData->hasDebugFlag(command)) - { - client->write("(unknown)"); - break; - } - - client->write(sessionData->debugFlagValue(argList.at(1)).toString().toUtf8()); - break; - } - case 8: - { - auto usage = [client]() - { - client->write("Usage: ftcmock start - See 'Session id' in DevDialog " - "\nftcmock stop "); - }; - - if (argList.count() < 3) - { - usage(); - break; - } - - bool k = false; - switch (QStringList({QStringLiteral("start"), QStringLiteral("stop")}).indexOf(argList.at(1))) - { - case 0: - { - if (argList.count() < 6) - { - usage(); - return; - } - - const int min = argList.at(3).toInt(&k); - if (!k || min < 0) - { - client->write("min must be a positive integer!"); - return; - } - - const int max = argList.at(4).toInt(&k); - if (!k || max < 0) - { - client->write("max must be a positive integer!"); - return; - } - - const int timers = argList.at(5).toInt(&k); - if (!k || timers < 1) - { - client->write("'timer count' must be a positive integer and greater than 0!"); - return; - } - - break; - } - - case 1: - { - break; - } - - default: - { - usage(); - return; - } - } - - const int sessionId = argList.at(2).toInt(&k); - if (!k || sessionId < 0) - { - client->write("sessionId must be a positive integer!"); - return; - } - - emit signalCommand(client, argList); - break; - } - - default: - { - QString result(QStringLiteral("Unknown command '%1'").arg(command)); - client->write(result.toUtf8()); - break; - } - } - - client->write("\r\n> "); - }); - - - QString versionString = QStringLiteral("%1.%2-%3 (rev: %4)") - .arg(QString::fromLatin1(ver_major), - QString::fromLatin1(ver_minor), - QString::fromLatin1(ver_jenkins), - QString::fromLatin1(ver_patch)); - - - QString motd(QStringLiteral("\r\nOperatorApplication - Version %1").arg(versionString)); - client->write(motd.toUtf8()); - client->write("\r\n> "); - }); -} - - -void LtsAppManager::showLogin() -{ - - QTimer::singleShot(0, this, &LtsAppManager::slotShowLogin); -} - - -void LtsAppManager::slotShowLogin() -{ - - - - - - - if (m_login == nullptr) - { - m_login = new LtsLogin::LtsLogin; - connect(m_login, &LtsLogin::LtsLogin::signalLoginDone, - this, [&] - { - - -#ifdef LTS_DEBUG - -#endif - if (m_appList.isEmpty()) - if (!startAppOrLetUserChoose(m_app, {})) - QTimer::singleShot(100, this, &LtsAppManager::slotLogout); - }); - - connect(m_login, &LtsLogin::LtsLogin::signalLogoffDone, - this, &LtsAppManager::signalLogoffDone); - - - connect(m_login, &LtsLogin::LtsLogin::signalLogoffDone, - this, &LtsAppManager::slotShowLogin, Qt::QueuedConnection); - - connect(m_login, &LtsLogin::LtsLogin::signalLogout, - this, &LtsAppManager::slotLogout); - - m_login->setLoginPlugin(m_loginPluginIid); - } - - m_login->login(); -} - - -bool LtsAppManager::hasRightForApp(const LtsAppDescriptor::AppDescription &appDescription) const -{ - const QSet &acls = LtsSessionData::LtsSessionData::getInstance()->getOperatorRights(); - for (int reqRight : appDescription.requiredAcls) - { - if (!acls.contains(reqRight)) - { - QMessageBox::critical(nullptr, tr("App Manager"), tr("The minimal required operator rights are not met. Therefore, the app cannot be started.")); - return false; - } - } - - return true; -} - - -void LtsAppManager::taskSwitch(QObject *_caller) -{ - LtsSession::LtsSession *caller = qobject_cast(_caller); - - if (caller == nullptr) - { - Q_ASSERT(false); - return; - } - - QVector sessions; - - for (const LtsPluginManager::LtsPluginManager *app : m_appList) - sessions += app->sessions(); - - - - - - - if (sessions.isEmpty()) - return; - - int sessionCount = sessions.count(); - - - if (sessionCount < 2) - return; - - - - - - auto sessionIt = std::find_if(sessions.begin(), sessions.end(), [caller](const LtsSession::LtsSession *session) - { - return (session == caller); - }); - - if (sessionIt == sessions.end()) - { - - return; - } - - - auto it = ++sessionIt; - - - - - - - - - - - - - int x = 0; - do - { - - - if (x > 100) - { - - return; - } - x++; - - - if (it == sessions.end()) - { - - if (sessions.begin() == sessionIt) - { - - return; - } - it = sessions.begin(); - - } - - LtsSession::LtsSession *session = *it; - - - - QWidget *blw = session->baseLayoutWidget(); - - if (blw == nullptr || session->isForeverInvisible() || session == caller) - { - - continue; - } - - - caller->baseLayoutWidget()->hide(); - blw->showFullScreen(); - - return; - } - - while (++it != sessionIt); -} - - -void LtsAppManager::hideAllOtherBLWs(QObject *_caller) -{ - - - - - - if (!LtsSessionData::LtsSessionData::getInstance()->isTablet()) - return; - - - - - - LtsSession::LtsSession *caller = qobject_cast(_caller); - - QVector sessions; - for (const LtsPluginManager::LtsPluginManager *app : m_appList) - sessions += app->sessions(); - - for (LtsSession::LtsSession *session : sessions) - { - - QWidget *blw = session->baseLayoutWidget(); - - if (blw != nullptr && !session->isForeverInvisible() && session != caller ) - { - - blw->hide(); - } - } - - - - - - if (caller != nullptr) - return; - - - - - - - for (LtsSession::LtsSession *session : sessions) - { - - QWidget *blw = session->baseLayoutWidget(); - - if (blw == nullptr || session->isForeverInvisible()) - { - - continue; - } - - - blw->showFullScreen(); - return; - } - - -} - - -bool LtsAppManager::startAppOrLetUserChoose(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams &connParams, bool force) -{ - return startAppOrLetUserChoose(getApp(appId), connParams, force); -} - - -bool LtsAppManager::startAppOrLetUserChoose(const LtsAppDescriptor::AppDescription &appDescription, - const LtsSession::DdsConnectionParams &connParams, - bool force) -{ - if (!appDescription.active && !force) - { - - - AppChooser *appChooser = new AppChooser(); - appChooser->setAttribute(Qt::WA_DeleteOnClose); - - connect(appChooser, &AppChooser::signalLogout, - this, &LtsAppManager::slotLogout); - - connect(appChooser, &AppChooser::signalAppSelected, - this, [this, connParams](LtsAppDescriptor::App appId) - { - if (createApp(appId, connParams) == nullptr) - QTimer::singleShot(100, this, &LtsAppManager::slotLogout); - }, Qt::QueuedConnection); - - appChooser->show(); - return true; - } - - - - - if (createApp(appDescription, connParams, {}, false, force) == nullptr) - { - QMessageBox::critical(nullptr, tr("App Manager"), tr("Could not create app.\nPlease contact support.")); - return false; - } - - return true; -} - - -LtsPluginManager::LtsPluginManager *LtsAppManager::createApp(LtsAppDescriptor::App appId, - const LtsSession::DdsConnectionParams &connParams, - const QString &workspaceId, - bool hidden, bool force) -{ - return createApp(getApp(appId), connParams, workspaceId, hidden, force); -} - - -LtsPluginManager::LtsPluginManager *LtsAppManager::createApp(const LtsAppDescriptor::AppDescription &appDescription, - const LtsSession::DdsConnectionParams &connParams, - const QString &workspaceId, - bool hidden, bool force) -{ - if (!appDescription.isValid()) - { - - return nullptr; - } - - if (!hasRightForApp(appDescription) && !force) - return nullptr; - - - const bool bDesiredAppHasSituationMapInstance = LtsPluginManager::LtsPluginManager::hasSituationMapInstance(appDescription, true); - if (bDesiredAppHasSituationMapInstance) - { - const int curInstances = getNumberOfSituationMapInstances(); - - if (curInstances >= 3 && !force) - { - if (QMessageBox::Yes != QMessageBox::question(nullptr, tr("App Manager"), - tr("Are you sure you want to launch another resource-intensive app?
" - "Please note that you are currently running three apps containing the resource-intensive map componment.
" - "It may lead to potential performance issues."))) - { - return nullptr; - } - } - } - - - auto *pluginManager = new LtsPluginManager::LtsPluginManager(); - - connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalQuit, - this, &LtsAppManager::slotQuit); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalSoftQuit, - this, &LtsAppManager::slotSoftQuit); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalLogout, - this, &LtsAppManager::slotLogout); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, - this, &LtsAppManager::signalInitializationFinished); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::destroyed, - this, &LtsAppManager::slotAppDestroyed); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::hideAllOtherBLWs, - this, &LtsAppManager::hideAllOtherBLWs); - connect(pluginManager, &LtsPluginManager::LtsPluginManager::taskSwitch, - this, &LtsAppManager::taskSwitch); -#ifdef LTS_DEBUG - connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalStillAlive, - this, [this]() - { - auto *app = qobject_cast(sender()); - if (!app) - return; - - for (auto i = m_appList.constBegin(); i != m_appList.constEnd(); ++i) - { - if (*i == app) - return; - } - - - }); -#endif - - - - - - - - - - - - - - - - - - if (!pluginManager->startApp(appDescription, false, connParams, workspaceId, hidden)) - { - QMessageBox::critical(nullptr, tr("App Manager"), tr("The Plugin Manager failed to start the app due to plugin errors.\nPlease contact support.")); - pluginManager->deleteLater(); - - return nullptr; - } - - m_appList.append(pluginManager); - - return pluginManager; -} - - -bool LtsAppManager::hasApp(LtsAppDescriptor::App appId) const -{ - return (std::find_if(m_availableApps.constBegin(), m_availableApps.constEnd(), [appId](const LtsAppDescriptor::AppDescription &app) - { - bool ret = (app.id == appId); - - return ret; - }) != m_availableApps.constEnd()); -} - -int LtsAppManager::getNumberOfSituationMapInstances() const -{ - int numberOfSituationMapInstances = 0; - for (const LtsPluginManager::LtsPluginManager *app : qAsConst(m_appList)) - { - if (!app) - continue; - - if (app->hasSituationMapInstance()) - ++numberOfSituationMapInstances; - } - - return numberOfSituationMapInstances; -} - -LtsAppDescriptor::AppDescription LtsAppManager::getApp(LtsAppDescriptor::App appId) const -{ - for (const LtsAppDescriptor::AppDescription &app : m_availableApps) - { - - - if (app.id == appId) - return app; - } - - - return LtsAppDescriptor::AppDescription(); -} - - -void LtsAppManager::setMainApp(LtsAppDescriptor::App appId) -{ - m_app = getApp(appId); -} - - -void LtsAppManager::saveState() -{ - - - if (LtsSessionData::LtsSessionData::getInstance()->getOperatorId() == 0) - return; - - - QStringList unsavedWorkspaces = LtsUtils::OperatorSettings::getInstance()->identifiers(QStringLiteral("UnsavedWorkspace")); - for (auto i = unsavedWorkspaces.constBegin(); i != unsavedWorkspaces.constEnd(); ++i) - LtsUtils::OperatorSettings::getInstance()->removeValue(QStringLiteral("UnsavedWorkspace"), *i); - - QStringList unsavedFilterPreset = LtsUtils::OperatorSettings::getInstance()->identifiers(QStringLiteral("UnsavedFilterPreset")); - for (auto i = unsavedFilterPreset.constBegin(); i != unsavedFilterPreset.constEnd(); ++i) - LtsUtils::OperatorSettings::getInstance()->removeValue(QStringLiteral("UnsavedFilterPreset"), *i); - - - QJsonArray root; - for (const LtsPluginManager::LtsPluginManager *app : qAsConst(m_appList)) - { - const QVector sessions = app->sessions(); - - const auto appId = app->appDescription().id; - - if (appId == LtsAppDescriptor::App::Login || sessions.count() < 1) - continue; - - - QJsonObject appState; - appState[QStringLiteral("id")] = static_cast(appId); - - QJsonArray sessionStates; - - for (LtsSession::LtsSession *session : sessions) - { - if (session->baseLayout() != nullptr && session->baseLayout()->isSecondaryWidget()) - continue; - - QJsonObject sessionState; - - int screenId = session->screenId(); - - - - int screenContainerId = session->screenContainerId(); - - - - sessionState[QStringLiteral("screenId")] = screenId; - sessionState[QStringLiteral("screenContainerId")] = screenContainerId; - sessionState[QStringLiteral("exerciseId")] = session->exerciseId(); - sessionState[QStringLiteral("partitionId")] = session->partitionId(); - sessionState[QStringLiteral("partitionType")] = int(session->partitionType()); - sessionState[QStringLiteral("savegame")] = session->getSavegame(); - - - const QString newId(QUuid::createUuid().toString()); - sessionState[QStringLiteral("workspaceId")] = newId; - - std::shared_ptr ifcOpm; - if (session->getForeignGuiInterface(ifcOpm)) - { - - ifcOpm->callWorkspaceSaveUnsaved(newId); - } - - - - sessionStates.append(sessionState); - } - - if (!sessionStates.isEmpty()) - { - appState[QStringLiteral("sessions")] = sessionStates; - root.append(appState); - } - } - - QJsonDocument doc(root); - - - LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("app"), QStringLiteral("LastAppStates"), doc.toJson(QJsonDocument::Compact)); -} - - -void LtsAppManager::slotLogout() -{ - for (LtsPluginManager::LtsPluginManager *app : qAsConst(m_appList)) - { - const QVector sessions = app->sessions(); - for (LtsSession::LtsSession *session : sessions) - { - if (session->notifyUnsavedChanges() == false ) - return; - } - } - - - saveState(); - - const QVector apps = m_appList; - - - if (apps.isEmpty() && m_login != nullptr) - m_login->logoff(); - - - for (LtsPluginManager::LtsPluginManager *app : apps) - app->deleteLater(); - - - -} - - -void LtsAppManager::slotAppDestroyed() -{ - LtsPluginManager::LtsPluginManager *app = static_cast(sender()); - - m_appList.removeOne(app); - - - if (m_appList.isEmpty() && m_login != nullptr) - { - - m_login->logoff(); - } -} - - -void LtsAppManager::slotQuit() -{ - - - QObject *object = sender(); - - if (!object) - qCritical() << "ERROR: LtsAppManager: slot called without object - so it cannot be deleted from the app assignments!"; - - LtsPluginManager::LtsPluginManager *app = qobject_cast(object); - - if (app == nullptr) - { - qCritical() << "ERROR: LtsAppManager: quit requested for invalid app"; - return; - } - - if (m_appList.count() == 1) - { - m_appList.removeAll(app); - saveState(); - } - - app->deleteLater(); - -} - - -void LtsAppManager::slotSoftQuit() -{ - - - QObject *object = sender(); - - - - - LtsPluginManager::LtsPluginManager *app = qobject_cast(object); - - if (app == nullptr) - { - qCritical() << "ERROR: LtsAppManager: quit requested for invalid app"; - return; - } - - app->_softQuit(); - - if (m_appList.count() == 1) - { - m_appList.removeAll(app); - saveState(); - } - - - -} - - -bool LtsAppManager::otherSessionsResponsibility(LtsSession::LtsSession *thisSession, const QVector &requiredAppTypes, - const QVector &requiredPartitionTypes, const QString &menuGroup, - const QString &identifier, LtsWidgets::LayoutArea area) -{ - const QVector apps = LtsAppManager::LtsAppManager::getInstance()->getAppList(); - QVector worthyApps; - - for (LtsPluginManager::LtsPluginManager *app : apps) - { - if (!requiredAppTypes.contains(static_cast(app->appDescription().id))) - continue; - - worthyApps.append(app); - } - - - - for (const LtsPluginManager::LtsPluginManager *app : qAsConst(worthyApps)) - { - const QVector sessions = app->sessions(); - for (const LtsSession::LtsSession *session : sessions) - { - if (session->widgetExists(menuGroup, identifier, area)) - return true; - } - } - - - - LtsMultiMonitor::LtsMultiMonitor *mm = LtsMultiMonitor::LtsMultiMonitor::getInstance(); - LtsMultiMonitor::LtsScreenBase *mainScreen = mm->mainScreen(); - - for (const LtsPluginManager::LtsPluginManager *app : qAsConst(worthyApps)) - { - const QVector sessions = app->sessions(); - for (LtsSession::LtsSession *session : sessions) - { - if (session != thisSession - && mm->widgetsScreen(session->baseLayoutWidget()) == mainScreen - && requiredPartitionTypes.contains(session->partitionType())) - return true; - } - } - - return false; -} - -} diff --git a/Lts/Common/LtsAppManager/lts_app_manager.h b/Lts/Common/LtsAppManager/lts_app_manager.h deleted file mode 100755 index 3b068345f..000000000 --- a/Lts/Common/LtsAppManager/lts_app_manager.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifndef LTSAPPMANAGER_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSAPPMANAGER_EXPORT -# else -# if defined(MAKE_LTSAPPMANAGER_LIB) -# define LTSAPPMANAGER_EXPORT Q_DECL_EXPORT -# else -# define LTSAPPMANAGER_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - - -namespace LtsPluginManager -{ -class LtsPluginManager; -} - -namespace LtsLogin -{ -class LtsLogin; -} - -namespace LtsSession -{ -class LtsSession; -struct DdsConnectionParams; -} - -namespace DeveloperTools -{ -class DevDialog; -} - -class QTcpSocket; - - -namespace LtsAppManager -{ - -class LTSAPPMANAGER_EXPORT LtsAppManager : public QObject -{ - Q_OBJECT - -public: - static LtsAppManager* getInstance(); - - void showLogin(); - - - LtsPluginManager::LtsPluginManager *createApp(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams &connParams, - const QString &workspaceId = QString(), bool hidden = false, bool force = false); - bool startAppOrLetUserChoose(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams &connParams, bool force = false); - void setLoginPluginIid(const QString &loginPluginIid) { m_loginPluginIid = loginPluginIid; } - void setMainApp(LtsAppDescriptor::App appId = LtsAppDescriptor::App::Login); - QVector getAppList() const { return m_appList ;} - LtsAppDescriptor::AppDescriptionVector getAvailableApps() const { return m_availableApps; } - void setAvailableApps(const LtsAppDescriptor::AppDescriptionVector &availableApps); - bool hasRightForApp(const LtsAppDescriptor::AppDescription &appDescription) const; - LtsAppDescriptor::AppDescription getApp(LtsAppDescriptor::App appId) const; - bool hasApp(LtsAppDescriptor::App appId) const; - int getNumberOfSituationMapInstances() const; - void debugSendMessage(const QString &msg); - - - - bool otherSessionsResponsibility(LtsSession::LtsSession *thisSession, const QVector &requiredAppTypes, - const QVector &requiredPartitionTypes, const QString &menuGroup, - const QString &identifier, LtsWidgets::LayoutArea area); - -private: - friend struct LtsAppManagerHandler; - friend class DeveloperTools::DevDialog; - - LtsAppManager(); - ~LtsAppManager(); - - bool startAppOrLetUserChoose(const LtsAppDescriptor::AppDescription &appDescription, const LtsSession::DdsConnectionParams &connParams, bool force = false); - LtsPluginManager::LtsPluginManager *createApp(const LtsAppDescriptor::AppDescription &appDescription, - const LtsSession::DdsConnectionParams &connParams, - const QString &workspaceId = QString(), - bool hidden = false, bool force = false); - void saveState(); - - LtsAppDescriptor::AppDescriptionVector m_availableApps; - QVector m_appList; - LtsLogin::LtsLogin *m_login; - LtsAppDescriptor::AppDescription m_app; - QString m_loginPluginIid; - -public slots: - void slotLogout(); - -private slots: - void slotQuit(); - void slotSoftQuit(); - void slotShowLogin(); - void slotAppDestroyed(); - void slotStartDebugServer(); - void hideAllOtherBLWs(QObject *_caller); - void taskSwitch(QObject *_caller); - -signals: - void signalLoginDone(); - void signalLogoffDone(); - void signalShowLogin(); - void signalInitializationFinished(); - void signalCommand(QTcpSocket *client, const QStringList argList); -}; - -} diff --git a/Lts/Common/LtsBotan/CMakeLists.txt b/Lts/Common/LtsBotan/CMakeLists.txt deleted file mode 100755 index ff9f098c7..000000000 --- a/Lts/Common/LtsBotan/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -find_package(Botan2) - -if (BOTAN_FOUND AND CMAKE_VERSION VERSION_GREATER 3.15.9) # botan doesn't use cmake, make it a cmake target | imported targets don't work correctly with 3.10 - add_library(LtsBotan STATIC IMPORTED GLOBAL) - set_target_properties(LtsBotan PROPERTIES IMPORTED_LOCATION ${BOTAN_LIBRARIES}) - set_target_properties(LtsBotan PROPERTIES INCLUDE_DIRECTORIES ${BOTAN_INCLUDE_DIRS}) - target_compile_definitions(LtsBotan INTERFACE BOTAN_FOUND) - target_include_directories(LtsBotan SYSTEM INTERFACE ${BOTAN_INCLUDE_DIRS}) - set(LTS_BOTAN LtsBotan) - - message(STATUS "BOTAN_INCLUDE_DIRS = ${BOTAN_INCLUDE_DIRS}, BOTAN_LIBRARIES = ${BOTAN_LIBRARIES}, LTS_BOTAN = ${LTS_BOTAN}") -endif() diff --git a/Lts/Common/LtsCommandLineParser/CMakeLists.txt b/Lts/Common/LtsCommandLineParser/CMakeLists.txt deleted file mode 100755 index 8e927d952..000000000 --- a/Lts/Common/LtsCommandLineParser/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -NGL_ADD_LIBRARY2(ALL LtsCommandLineParser SHARED - LtsCommandLineParser.cpp - LtsCommandLineParser.h -) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsCommandLineParser - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsCommandLineParser - PRIVATE - ngltools - Qt5::Network - PUBLIC - Qt5::Core -) diff --git a/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.cpp b/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.cpp deleted file mode 100755 index 05b334738..000000000 --- a/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.cpp +++ /dev/null @@ -1,183 +0,0 @@ - - - -#include "LtsCommandLineParser.h" -#include -#include -#include -#include -#include - -namespace LtsCommandLineParser -{ -static constexpr int s_maxDomainId = 232; - -LtsCommandLineParser::LtsCommandLineParser(ProvideCommandLineParameterOptions opts) - : QCommandLineParser() - , m_enabledOptions(opts) -{ - if (NGL::isAppInBuildBasePath() || m_enabledOptions.testFlag(CreateDomainIdFromIp)) - { - const QList addresses = QNetworkInterface::allAddresses(); - for (const QHostAddress &address : addresses) - { - if (address.isLoopback() || address.isNull()) - continue; - if (address.protocol() != QAbstractSocket::IPv4Protocol) - continue; - - - m_generatedDomainId = (address.toIPv4Address() & 0x000000ff) % 232; - if (m_generatedDomainId == 0) - m_generatedDomainId = s_maxDomainId; - break; - } - } - - if (m_enabledOptions.testFlag(ExerciseId)) - addOption({{QStringLiteral("e"), QStringLiteral("exerciseid")}, - QStringLiteral("Default: -1 (invalid, get from lts-cc.conf)"), - QStringLiteral("exerciseId"), - QStringLiteral("-1") - }); - if (m_enabledOptions.testFlag(DomainId)) - addOption({{QStringLiteral("d"), QStringLiteral("domainid")}, - QStringLiteral("Default: ") + QString::number(m_generatedDomainId), - QStringLiteral("domainId"), - QString::number(m_generatedDomainId) - }); - if (m_enabledOptions.testFlag(CustomConfigFile)) - { - const QString cf(NGL::findProjectConfigFile()); - addOption({{QStringLiteral("i"), QStringLiteral("ini")}, - QStringLiteral("Default: ") + cf, - QStringLiteral("filename"), - cf - }); - } - if (m_enabledOptions.testFlag(TcpConnectionUrl)) - { - const QString defaultTcpUrl(QStringLiteral("EXCTDS01:7400")); - addOption({{QStringLiteral("t"), QStringLiteral("tcpurl")}, - QStringLiteral("Default: ") + defaultTcpUrl, - QStringLiteral("hostname:port"), - defaultTcpUrl - }); - } - addHelpOption(); - - if (!m_enabledOptions.testFlag(NoVersion)) - addVersionOption(); -} - -int LtsCommandLineParser::domainId() const -{ - if (m_domainId == 0 && m_generatedDomainId != 0 && !m_domainIdSetByFunction) - { -#ifdef WIN32 - - fprintf(stdout, "\n\nAttention: using a default domain id != 0 because executable is run inside builddir - default domainid is %d\n\n", m_generatedDomainId); -#else - fprintf(stdout, "\n\n\e[31mAttention: using a default domain id != 0 because executable is run inside builddir - default domainid is %d\e[0m\n\n", m_generatedDomainId); -#endif - return m_generatedDomainId; - } - return m_domainId; -} - -void LtsCommandLineParser::setDomainId(int id) -{ - m_domainIdSetByFunction = true; - m_domainId = id; -} - -bool LtsCommandLineParser::iniFileSet() const -{ - if (!m_enabledOptions.testFlag(CustomConfigFile)) - return false; - return isSet(QStringLiteral("i")); -} - -bool LtsCommandLineParser::useTcpConnection() const -{ - if (!m_enabledOptions.testFlag(TcpConnectionUrl)) - return false; - return isSet(QStringLiteral("t")); -} - -QUrl LtsCommandLineParser::tcpConnectionUrl() const -{ - if (!m_enabledOptions.testFlag(TcpConnectionUrl)) - return QUrl(); - if (!useTcpConnection()) - return QUrl(); - QString str = value(QStringLiteral("t")); - - str.replace(QLatin1String("tcpv4_lan"), QLatin1String("tcpv4lan")); - QUrl url = QUrl::fromUserInput(str); - if (url.scheme() == QLatin1String("http") || - url.scheme() == QLatin1String("file")) - url.setScheme(QStringLiteral("tcpv4lan")); - return url; -} - -bool LtsCommandLineParser::init(bool bParseHelp) -{ - if (!parse(QCoreApplication::arguments())) - { - fprintf(stdout, "\nBad option: %s \n", qPrintable(errorText())); - if (!m_unitModuleTestMode) - return false; - } - - if (m_enabledOptions.testFlag(ExerciseId)) - m_exerciseId = value(QStringLiteral("e")).toInt(); - if (m_enabledOptions.testFlag(DomainId)) - m_domainId = value(QStringLiteral("d")).toInt(); - if (m_enabledOptions.testFlag(CustomConfigFile)) - m_iniFile = value(QStringLiteral("i")); - - if (bParseHelp && isSet(QStringLiteral("h"))) - showHelp(); - if (!m_unitModuleTestMode && isSet(QStringLiteral("v"))) - showVersion(); - if (!domainIdProvided()) - { - const QString iniFile = m_iniFile.isEmpty() ? NGL::findProjectConfigFile() : m_iniFile; - if (!iniFile.isEmpty()) - { - QSettings s(iniFile, QSettings::IniFormat); - bool bOk; - int domainId = s.value(QStringLiteral("Global/DomainId"), m_domainId).toInt(&bOk); - if (bOk && domainId > 0 && domainId <= s_maxDomainId) - m_domainId = domainId; - } - } - return true; -} - -void LtsCommandLineParser::showHelp(int exitCode) -{ - if (!m_additionalHelpStr.isEmpty()) - fprintf(stdout, "%s", qPrintable(m_additionalHelpStr)); - - - QCommandLineParser::showHelp(exitCode); -} - -bool LtsCommandLineParser::domainIdProvided() const -{ - if (!m_enabledOptions.testFlag(DomainId)) - return false; - return isSet(QStringLiteral("d")); -} - -bool LtsCommandLineParser::exerciseIdProvided() const -{ - if (!m_enabledOptions.testFlag(ExerciseId)) - return false; - return isSet(QStringLiteral("e")); -} - - -} diff --git a/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.h b/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.h deleted file mode 100755 index 69d80a1c3..000000000 --- a/Lts/Common/LtsCommandLineParser/LtsCommandLineParser.h +++ /dev/null @@ -1,56 +0,0 @@ - - - -#pragma once - -#include -#include - -class QUrl; -namespace LtsCommandLineParser -{ -class LTSCOMMANDLINEPARSER_EXPORT LtsCommandLineParser : public QCommandLineParser -{ -public: - enum ProvideCommandLineParameterOptions - { - None = 0x0000, - CreateDomainIdFromIp = 0x0001, - DomainId = 0x0002, - ExerciseId = 0x0004, - CustomConfigFile = 0x0008, - TcpConnectionUrl = 0x0010, - NoVersion = 0x0020 - }; - - explicit LtsCommandLineParser(ProvideCommandLineParameterOptions opts = ProvideCommandLineParameterOptions(CreateDomainIdFromIp|DomainId|ExerciseId|CustomConfigFile)); - virtual ~LtsCommandLineParser() = default; - int domainId() const; - void setDomainId(int id); - int exerciseId() const { return m_exerciseId; } - QString iniFile() const { return m_iniFile; } - bool iniFileSet() const; - bool useTcpConnection() const; - QUrl tcpConnectionUrl() const; - virtual bool init(bool bParseHelp = true); - - void showHelp(int exitCode = 0); - bool domainIdProvided() const; - bool exerciseIdProvided() const; - - void setUnitModuleTestMode(bool onOff) { m_unitModuleTestMode = onOff; } - -protected: - QString m_additionalHelpStr; - -private: - QFlags m_enabledOptions; - int m_exerciseId{0}; - int m_domainId{0}; - int m_generatedDomainId{0}; - bool m_domainIdSetByFunction{false}; - bool m_unitModuleTestMode{false}; - QString m_iniFile; -}; - -} diff --git a/Lts/Common/LtsCoordTrans/CMakeLists.txt b/Lts/Common/LtsCoordTrans/CMakeLists.txt deleted file mode 100755 index 0c7a04a83..000000000 --- a/Lts/Common/LtsCoordTrans/CMakeLists.txt +++ /dev/null @@ -1,83 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Christian Ehrlicher, SLSE, 2016-08-31 -# - -FIND_PACKAGE(GeographicLib) -NGL_ADD_LIBRARY2(LtsCoordTrans WS AAR SV HLA SHARED - lts_coord_trans.h - lts_coord_trans.cpp -) -NGL_TARGET_LINK_LIBRARIES(LtsCoordTrans - PRIVATE - Qt5::Core -) -if (GeographicLib_FOUND) - NGL_TARGET_LINK_LIBRARIES(LtsCoordTrans - PRIVATE GeographicLib - ) -else() - message(STATUS "GeographicLib not found; building LtsCoordTrans without it") -endif() -NGL_TARGET_INCLUDE_DIRECTORIES(LtsCoordTrans - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -SET(ltsCoordWidget_SRCS - lts_coord_widget.cpp - lts_coord_widget.h - lts_coord_widget_p.cpp - lts_coord_widget_p.h -) -NGL_WRAP_UI(ltsCoordWidget_SRCS - lts_coord_widget_mgrs.ui - lts_coord_widget_wgs84.ui -) -NGL_ADD_LIBRARY2(ltsCoordWidget WS AAR SV SHARED ${ltsCoordWidget_SRCS}) -NGL_TARGET_LINK_LIBRARIES(ltsCoordWidget - PRIVATE - LtsCoordTrans - PUBLIC - Qt5::Core - Qt5::Widgets -) -NGL_TARGET_INCLUDE_DIRECTORIES(ltsCoordWidget - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -IF(TARGET LtsCoordTrans) - SET(_tools lonlat2mgrs mgrs2lonlat) - FOREACH(_tool ${_tools}) - NGL_ADD_EXECUTABLE(ALL ${_tool} ${_tool}.cpp) - NGL_TARGET_LINK_LIBRARIES(${_tool} - Qt5::Core - GeographicLib - ) - NGL_INSTALL_TARGET(${_tool}) - ENDFOREACH() -ENDIF() - -IF(BUILD_TESTING AND TARGET ltsCoordWidget) - ADD_EXECUTABLE(lts_coord_widget_test lts_coord_widget_test.cpp) - TARGET_LINK_LIBRARIES(lts_coord_widget_test - ltsCoordWidget - LtsCoordTrans - ) -ENDIF() - -IF(BUILD_TESTING AND TARGET LtsCoordTrans) - ADD_EXECUTABLE(lts_coord_trans_unit_test - lts_coord_trans_unit_test.cpp - lts_coord_trans_unit_test.h - ) - TARGET_LINK_LIBRARIES(lts_coord_trans_unit_test - LtsCoordTrans - Qt5::Test - GeographicLib - ) - ADD_TEST(NAME lts_coord_trans_unit_test COMMAND $) -ENDIF() diff --git a/Lts/Common/LtsCoordTrans/lonlat2mgrs.cpp b/Lts/Common/LtsCoordTrans/lonlat2mgrs.cpp deleted file mode 100755 index 37c15c46a..000000000 --- a/Lts/Common/LtsCoordTrans/lonlat2mgrs.cpp +++ /dev/null @@ -1,67 +0,0 @@ - - - -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - if (argc < 3 || argc > 5) - { - fprintf(stderr, "Usage: %s []\n", argv[0]); - fprintf(stderr, "Usage: %s -r []\n", argv[0]); - return -1; - } - - bool lonLatInRad = false; - double lon = atof(argv[1]); - double lat = atof(argv[2]); - int prec = 5; - if (argc >= 4) - { - int precArgId = -1; - if (strcmp(argv[3], "-r") == 0) - { - lonLatInRad = true; - if (argc == 5) - precArgId = 4; - } - else - precArgId = 3; - if (precArgId > 0) - prec = atoi(argv[precArgId]); - } - - std::string mgrs; - - int zone; - bool northp; - double x, y; - try - { - const double latDeg = lonLatInRad ? lat * 180 / M_PI : lat; - const double lonDeg = lonLatInRad ? lon * 180 / M_PI : lon; - GeographicLib::UTMUPS::Forward(latDeg, lonDeg, zone, northp, x, y); - GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs); - } - catch (...) - { - fprintf(stderr, "Error converting %9.6f, %9.6f to MGRS coords\n", lat, lon); - return -2; - } - std::string part1, part2, part3, part4; - part1 = mgrs.substr(0, 3); - part2 = mgrs.substr(3, 2); - if (prec > 0) - { - part3 = mgrs.substr(5, prec); - part4 = mgrs.substr(5 + prec, prec); - } - fprintf(stdout, "%13.10f, %13.10f = %s %s %s %s (UTM: %d %.2f, %.2f %c)\n", - lon, lat, - part1.c_str(), part2.c_str(), part3.c_str(), part4.c_str(), - zone, x, y, northp ? 'N' : 'S'); - return 0; -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_trans.cpp b/Lts/Common/LtsCoordTrans/lts_coord_trans.cpp deleted file mode 100755 index 517f1fe79..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_trans.cpp +++ /dev/null @@ -1,445 +0,0 @@ - - - -#include "lts_coord_trans.h" - -#include -#include -#include -#include -#include -#include -#include - - -#include - -namespace LtsCoordTrans -{ - -static long s_conversion_counter_MGRS_Forward = 0; -static long s_conversion_counter_MGRS_Reverse = 0; - -static long s_conversion_counter_WGS84_Forward = 0; -static long s_conversion_counter_WGS84_Reverse = 0; - -static long s_conversion_counter_UTMUPS_Forward = 0; -static long s_conversion_counter_UTMUPS_Reverse = 0; - - -inline void incrementCounter(long &counterRef) -{ -#ifdef LTS_DEBUG - ++counterRef; -#endif -} - - -static inline std::string cleanupMgrsString(const QString &in) -{ - const QByteArray ba(in.toLatin1()); - std::string ret; - ret.reserve(ba.size()); - for (const char c : ba) - { - if (c != ' ') - ret += c; - } - return ret; -} - -QString UtmCoordinate::mgrs(int prec, bool withSpaces) const -{ - if (prec < 0) - prec = 0; - if (prec > 11) - prec = 11; - - std::string mgrs; - try - { - GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs); - incrementCounter(s_conversion_counter_MGRS_Forward); - } - catch (const std::exception &e) - { - fprintf(stderr, "Error while calculating mgrs from utm %d, %9.6f, %9.6f: %s\n", zone, x, y, e.what()); - mgrs.clear(); - } - - QString ret; - if (withSpaces && mgrs.size() == static_cast(5 + 2 * prec)) - { - - const char *ptr = mgrs.c_str(); - - ret = QString::fromLatin1(ptr, 3) + QLatin1Char(' ') + - QString::fromLatin1(ptr + 3, 2); - if (prec > 0) - { - ret += QLatin1Char(' ') + QString::fromLatin1(ptr + 5, prec) + - QLatin1Char(' ') + QString::fromLatin1(ptr + 5 + prec, prec); - } - } - else - ret = QString::fromLatin1(mgrs.c_str(), mgrs.size()); - return ret; -} - -QPointF UtmCoordinate::toPoint() const -{ - return {x,y}; -} - -QPointF UtmCoordinate::geographic(double offsetX, double offsetY) const -{ - QPointF ret; - try - { - GeographicLib::UTMUPS::Reverse(zone, northp, x + offsetX, y + offsetY, ret.ry(), ret.rx()); - incrementCounter(s_conversion_counter_UTMUPS_Reverse); - } - catch (const std::exception &e) - { - fprintf(stderr, "Error while calculating geographic from utm %d, %9.6f, %9.6f: %s\n", zone, x + offsetX, y + offsetY, e.what()); - ret = QPointF(); - } - return ret * deg2rad(); -} - -QPointF UtmCoordinate::geographic(const QPointF &offset) const -{ - return geographic(offset.x(), offset.y()); -} - -QPointF UtmCoordinate::relativeTo(const UtmCoordinate &other) const -{ - if (zone == other.zone) - return QPointF(x - other.x, y - other.y); - - UtmCoordinate otherInMyZone = fromGeographic(other.geographic(), zone); - return QPointF(x - otherInMyZone.x, y - otherInMyZone.y); -} - -UtmCoordinate UtmCoordinate::fromGeographic(double lon, double lat, int setZone) -{ - - lon *= rad2deg(); - lat *= rad2deg(); - - UtmCoordinate ret; - try - { - - GeographicLib::UTMUPS::Forward(lat, lon, ret.zone, ret.northp, ret.x, ret.y, setZone); - incrementCounter(s_conversion_counter_UTMUPS_Forward); - } - catch (const std::exception &e) - { - fprintf(stderr, "Error while calculating mgrs base point from %9.6f, %9.6f: %s\n", lat, lon, e.what()); - ret = UtmCoordinate(); - } - return ret; -} - -UtmCoordinate UtmCoordinate::fromGeographic(const QPointF &lonLat, int setZone) -{ - return fromGeographic(lonLat.x(), lonLat.y(), setZone); -} - -UtmCoordinate UtmCoordinate::fromMgrs(const QString &mgrs) -{ - UtmCoordinate utm; - try - { - int prec; - - GeographicLib::MGRS::Reverse(cleanupMgrsString(mgrs), utm.zone, utm.northp, utm.x, utm.y, prec, true); - incrementCounter(s_conversion_counter_MGRS_Reverse); - - } - catch (const std::exception &e) - { - fprintf(stderr, "Error while converting mgrs coordinate %s to utm: %s\n", qPrintable(mgrs), e.what()); - utm = UtmCoordinate(); - } - return utm; -} - -QString mgrsFromGeographic(double lon, double lat, int prec, bool withSpaces) -{ - return UtmCoordinate::fromGeographic(lon, lat, -1).mgrs(prec, withSpaces); -} - -QPointF geographicFromMgrs(const QString &mgrs) -{ - return UtmCoordinate::fromMgrs(mgrs).geographic(); -} - -QVector &staticDefaultSquaresHelper() -{ - static QVector s_defaultMgrsSquares; - return s_defaultMgrsSquares; -} -UtmCoordinate &staticPreferedMgrsSquareHelper() -{ - static UtmCoordinate s_preferedMgrsSquare; - return s_preferedMgrsSquare; -} -QVector &staticMgrsSquaresWithOffsetsHelper() -{ - static QVector s_mgrsSquaresWithOffsets; - return s_mgrsSquaresWithOffsets; -} - - -QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter) -{ - double lonMin, lonMax, latMin, latMmax; - rect_LatLon_radiant.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax); - - - - - - LtsCoordTrans::UtmCoordinate basePoint; - { - - const QPointF centerRad = rect_LatLon_radiant.center(); - QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1); - mgrs += QStringLiteral("00000000000000"); - - - basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs); - } - - const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone)); - const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone)); - - int offsetX = marginMeter.width(); - int offsetY = marginMeter.height(); - - const QPointF topLeft(lowerLeft.geographic(-offsetX, -offsetY)); - const QPointF bottomRight(upperRight.geographic(offsetX, offsetY)); - - QRectF res = QRectF(topLeft, bottomRight); - return res; -} - - -QVector getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin) -{ - QVector ret = getMgrsSquaresInternal(rect); - - if (setAsDefaultSquares) - staticDefaultSquaresHelper() = ret; - - if (setAsPreferedSquare) - staticPreferedMgrsSquareHelper() = LtsCoordTrans::UtmCoordinate::fromGeographic(rect.center()); - - if (setAsSquareWithMargin) - staticMgrsSquaresWithOffsetsHelper() = ret; - - - return ret; -} - -QVector getMgrsSquaresInternal(const QRectF &rect) -{ - QVector ret; - QVector tmpMgrsSquares; - - - double lonMin, lonMax, latMin, latMmax; - rect.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax); - - - bool withInSingleUtmSquare = true; - { - const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax)); - const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin)); - withInSingleUtmSquare = upperRight.zone == lowerLeft.zone; - } - - - - - - LtsCoordTrans::UtmCoordinate basePoint; - { - - QPointF centerRad = rect.center(); - QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1); - mgrs += QStringLiteral("00000000000000"); - - - basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs); - } - - const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone)); - const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone)); - - const double maxX(upperRight.x - lowerLeft.x); - const double maxY(upperRight.y - lowerLeft.y); - - - - const int increment = withInSingleUtmSquare ? 10000 : 1000; - for (double x = 0; x <= maxX + increment; x += increment) - { - const double xOfs = std::min(x, maxX); - for (double y = 0; y <= maxY + increment; y += increment) - { - const double yOfs = std::min(y, maxY); - - const UtmCoordinate utm(UtmCoordinate::fromGeographic(lowerLeft.geographic(xOfs, yOfs))); - const QString mgrs(utm.mgrs(0)); - - if (!tmpMgrsSquares.contains(mgrs)) - { - tmpMgrsSquares += mgrs; - ret += utm; - } - } - } - return ret; -} - -QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter) -{ - - const QVector mainUtms = getMgrsSquaresInternal(geoRect); - QStringList mainSquares; - mainSquares.reserve(mainUtms.count()); - for (const auto &utm : mainUtms) - mainSquares.append(utm.mgrs(0)); - - - const QRectF rectWithOffset = addMarginToArea(geoRect,offsetMeter); - - QVector mainUtmsWithOffset = getMgrsSquaresInternal(rectWithOffset); - QStringList mainSquaresWithOffset; - mainSquaresWithOffset.reserve(mainUtmsWithOffset.count()); - for (const auto &utm : qAsConst(mainUtmsWithOffset)) - mainSquaresWithOffset.append(utm.mgrs(0)); - - - QStringList surroundingSquares; - surroundingSquares.reserve(mainSquaresWithOffset.count()); - for (const auto & mgrs0 : mainSquaresWithOffset) - { - if (!mainSquares.contains(mgrs0)) - surroundingSquares.append(mgrs0); - } - - return surroundingSquares; -} - -QVector defaultMgrsSquareWithOffsets() -{ - return staticMgrsSquaresWithOffsetsHelper(); -} - - -void setDefaultMgrsSquares(const QVector &squares) -{ - QVector coords; - coords.reserve(squares.size()); - for (const QString &s : squares) - coords += UtmCoordinate::fromMgrs(s + QStringLiteral("00000000000000")); - staticDefaultSquaresHelper() = coords; -} - -QVector defaultMgrsSquares() -{ - return staticDefaultSquaresHelper(); -} - -UtmCoordinate preferedMgrsSquare() -{ - return staticPreferedMgrsSquareHelper(); -} - - -bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut) -{ - - lon *= rad2deg(); - lat *= rad2deg(); - - try - { - GeographicLib::Geocentric::WGS84().Forward(lat, lon, altitude, xOut, yOut, zOut); - incrementCounter(s_conversion_counter_WGS84_Forward); - } - catch (...) - { - xOut = 0; - yOut = 0; - zOut = 0; - return false; - } - return true; -} - -bool geocentric2LonLatAlt(double x, double y, double z, - double &lon, double &lat, double &altitude) -{ - - - try - { - GeographicLib::Geocentric::WGS84().Reverse(x, y, z, lat, lon, altitude); - incrementCounter(s_conversion_counter_WGS84_Reverse); - } - catch (...) - { - lon = 0; - lat = 0; - altitude = 0; - return false; - } - - lon *= deg2rad(); - lat *= deg2rad(); - return true; -} - -double distance(const QPointF &p1, const QPointF &p2) -{ - return distance(p1.x(), p1.y(), p2.x(), p2.y()); -} - -double squareDistance(const QPointF &p1, const QPointF &p2) -{ - return squareDistance(p1.x(), p1.y(), p2.x(), p2.y()); -} - -std::ostream &operator<< (std::ostream &stream, - const UtmCoordinate &coord) -{ - - stream << "UtmCoordinate(" << std::setprecision(8) << coord.x - << "#" << std::setprecision(8) << coord.y - << " zone=" << coord.zone << ")"; - return stream; -} - -void dumpConversionCounter() -{ - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Forward : - mgrs : %ld\n" , s_conversion_counter_MGRS_Forward); - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Reverse - fromMgrs : %ld\n" , s_conversion_counter_MGRS_Reverse); - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Forward - lonLatAltToGeocentric : %ld\n" , s_conversion_counter_WGS84_Forward); - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Reverse - geocentric2LonLatAlt : %ld\n" , s_conversion_counter_WGS84_Reverse); - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Forward - fromGeographic : %ld\n" , s_conversion_counter_UTMUPS_Forward); - fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Reverse - geographic : %ld\n" , s_conversion_counter_UTMUPS_Reverse); -} - -long getConversionCounter_MGRS_Forward() { return s_conversion_counter_MGRS_Forward; } -long getConversionCounter_MGRS_Reverse() { return s_conversion_counter_MGRS_Reverse; } -long getConversionCounter_WGS84_Forward() { return s_conversion_counter_WGS84_Forward; } -long getConversionCounter_WGS84_Reverse() { return s_conversion_counter_WGS84_Reverse; } -long getConversionCounter_UTMUPS_Forward() { return s_conversion_counter_UTMUPS_Forward; } -long getConversionCounter_UTMUPS_Reverse() { return s_conversion_counter_UTMUPS_Reverse; } - -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_trans.h b/Lts/Common/LtsCoordTrans/lts_coord_trans.h deleted file mode 100755 index 8589a0ea8..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_trans.h +++ /dev/null @@ -1,249 +0,0 @@ - - - -#pragma once - -#include - -#include -#include -#include -class QPointF; -class QRectF; -class QSize; -class QString; - -namespace LtsCoordTrans -{ - - - - -inline Q_CONSTEXPR double pi() { return M_PI; } -inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; } -inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); } - -struct LTSCOORDTRANS_EXPORT UtmCoordinate -{ - int zone = -1; - bool northp = true; - double x = 0.; - double y = 0.; - - UtmCoordinate() = default; - UtmCoordinate(int zone_, double x_, double y_, bool northp_ = true) - : zone(zone_) - , northp(northp_) - , x(x_) - , y(y_) - {} - - - - bool isValid() const { return zone > 0 && zone <= 60; } - - - - QString mgrs(int prec = 5, bool withSpaces = true) const; - - QPointF toPoint() const; - - - - QPointF geographic(double offsetX = 0., double offsetY = 0.) const; - - - - QPointF geographic(const QPointF &offset) const; - - - - QPointF relativeTo(const UtmCoordinate &other) const; - - - - static UtmCoordinate fromGeographic(double lon, double lat, int setZone = -1); - - - - static UtmCoordinate fromGeographic(const QPointF &lonLat, int setZone = -1); - - - - static UtmCoordinate fromMgrs(const QString &mgrs); -}; - - - -LTSCOORDTRANS_EXPORT QString mgrsFromGeographic(double lon, double lat, int prec = 5, bool withSpaces = true); - - - -LTSCOORDTRANS_EXPORT QPointF geographicFromMgrs(const QString &mgrs); - - - -LTSCOORDTRANS_EXPORT QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter); - - - -LTSCOORDTRANS_EXPORT QVector getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin = false); - - - -QVector getMgrsSquaresInternal(const QRectF &rect); - - - -QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter); - - - -LTSCOORDTRANS_EXPORT QVector defaultMgrsSquareWithOffsets(); - - - -LTSCOORDTRANS_EXPORT void setDefaultMgrsSquares(const QVector &squares); - - - -LTSCOORDTRANS_EXPORT QVector defaultMgrsSquares(); - - - -LTSCOORDTRANS_EXPORT UtmCoordinate preferedMgrsSquare(); - - - -LTSCOORDTRANS_EXPORT bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut); - - - -LTSCOORDTRANS_EXPORT bool geocentric2LonLatAlt(double x, double y, double z, - double &lon, double &lat, double &altitude); - - - - -inline double squareDistance(const UtmCoordinate &p1, const UtmCoordinate &p2) -{ - if (p1.zone != p2.zone) - { - Q_ASSERT(false); - return std::nan(""); - } - return std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2); -} - - - -LTSCOORDTRANS_EXPORT double squareDistance(const QPointF &p1, const QPointF &p2); - - - -inline double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2) -{ - const auto p1(UtmCoordinate::fromGeographic(lon1, lat1)); - const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone)); - return squareDistance(p1, p2); -} - - - -inline double squareDistance(const UtmCoordinate &p1, const double lon2, const double lat2) -{ - const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone)); - return squareDistance(p1, p2); -} - - - -inline double distance(const UtmCoordinate &p1, const UtmCoordinate &p2) -{ - if (p1.zone != p2.zone) - { - Q_ASSERT(false); - return std::nan(""); - } - return sqrt(squareDistance(p1, p2)); -} - - - -LTSCOORDTRANS_EXPORT double distance(const QPointF &p1, const QPointF &p2); - - - -inline double distance(const double lon1, const double lat1, const double lon2, const double lat2) -{ - return sqrt(squareDistance(lon1, lat1, lon2, lat2)); -} - -inline double distance(const UtmCoordinate &p1, const double lon2, const double lat2) -{ - return sqrt(squareDistance(p1, lon2, lat2)); -} - - -LTSCOORDTRANS_EXPORT void dumpConversionCounter(); - -LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Forward(); -LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Reverse(); -LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Forward(); -LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Reverse(); -LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Forward(); -LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Reverse(); - - -LTSCOORDTRANS_EXPORT std::ostream &operator<< (std::ostream &stream, - const UtmCoordinate &coord); - - - - - - - -class LTSCOORDTRANS_EXPORT UtmCoordinate3D : public LtsCoordTrans::UtmCoordinate -{ - using BaseClass = LtsCoordTrans::UtmCoordinate; - -public: - UtmCoordinate3D() - { - reset(); - } - - explicit UtmCoordinate3D(const LtsCoordTrans::UtmCoordinate &utm2d, double _zValue = std::nan("")) - :BaseClass() - { - zone = utm2d.zone; - northp = utm2d.northp; - x = utm2d.x; - y = utm2d.y; - zValue = _zValue; - } - - void reset() - { - zone = -1; - northp = true; - - - - - - - x = 0.; - y = 0.; - zValue = std::nan(""); - } - - double z() const { return zValue; } - -protected: - double zValue{std::nan("")}; -}; - - -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.cpp b/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.cpp deleted file mode 100755 index 90061f05b..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.cpp +++ /dev/null @@ -1,469 +0,0 @@ - - - -#include "lts_coord_trans_unit_test.h" -#include "lts_coord_trans.h" -#include -#include -#include - -QTEST_MAIN(coordtrans_test) - -void coordtrans_test::initTestCase() -{ - qDebug("sizeof(UtmCoordinate) = %lu", sizeof(LtsCoordTrans::UtmCoordinate)); -} - -void coordtrans_test::cleanupTestCase() -{} - -void coordtrans_test::testFromGeographic() -{ - - - - LtsCoordTrans::UtmCoordinate utm; - QPointF lonlat; - - lonlat = QPointF(8.6264954126, 53.1138437183) * LtsCoordTrans::deg2rad(); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1); - QCOMPARE(utm.mgrs(0), QStringLiteral("32U MD")); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y()); - QCOMPARE(utm.mgrs(8), QStringLiteral("32U MD 74999999 84999999")); - - lonlat = QPointF(11.3003696521, 52.8224535191) * LtsCoordTrans::deg2rad(); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1); - QCOMPARE(utm.mgrs(0), QStringLiteral("32U PD")); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y()); - QCOMPARE(utm.mgrs(5), QStringLiteral("32U PD 55000 55000")); - - lonlat = QPointF(54.8821607021, 24.2631973752) * LtsCoordTrans::deg2rad(); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1); - QCOMPARE(utm.mgrs(0), QStringLiteral("40R BM")); - utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y()); - QCOMPARE(utm.mgrs(5), QStringLiteral("40R BM 85000 85000")); - - - - utm = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("40R BM 00000 00000")); - QCOMPARE(utm.mgrs(0), QStringLiteral("40Q BM")); -} - -void coordtrans_test::testMgrsFromGeographic() -{ - - QString mgrs; - QPointF lonlat; - - lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad(); - mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9); - QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000")); - - mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 8, false); - QCOMPARE(mgrs, QStringLiteral("32UMD7450000084000000")); -} - -void coordtrans_test::testRelativeMetricalOffet_data() -{ - - QTest::addColumn("offset"); - QTest::addColumn("correctMgrs"); - - - QTest::newRow("set 1") << QPoint( 5, 5) << QStringLiteral("32U PD 99995 99995"); - - - QTest::newRow("set 2") << QPoint(100, 100) << QStringLiteral("32U QE 00090 00090"); - QTest::newRow("set 3") << QPoint(500, 500) << QStringLiteral("32U QE 00490 00490"); -} - -void coordtrans_test::testRelativeMetricalOffet() -{ - QFETCH(QPoint, offset); - QFETCH(QString, correctMgrs); - - const LtsCoordTrans::UtmCoordinate &utmRef = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 99990 99990")); - qDebug() << "utmRef = " << utmRef.mgrs(); - - { - LtsCoordTrans::UtmCoordinate utmRaw; - utmRaw.x = utmRef.x + offset.x(); - utmRaw.y = utmRef.y + offset.y(); - utmRaw.zone = utmRef.zone; - qDebug() << "offset = " << offset << " - utmRaw = " << utmRaw.mgrs(); - - QCOMPARE(utmRaw.mgrs(), correctMgrs); - } - - - - - { - const LtsCoordTrans::UtmCoordinate utmSimple(utmRef.zone, - utmRef.x + offset.x(), - utmRef.y + offset.y()); - - qDebug() << "offset = " << offset << " - utmSimple = " << utmSimple.mgrs(); - QCOMPARE(utmSimple.mgrs(), correctMgrs); - } - -} - -QRectF createArea(const QString &base, - int xCenter, int yCenter, int width, int height) -{ - - QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height); - LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base))); - const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top())); - const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom())); - - - - QRectF rect (topLeft, bottomRight); - return rect; -} - - -void coordtrans_test::testAddMarginToArea() -{ - - { - qDebug() << "Altmark"; - QRectF area = createArea(QStringLiteral("32U PD 0000000000"), 50000, 10000, 30000, 30000); - QRectF area1Increased = LtsCoordTrans::addMarginToArea(area, QSize(5000, 20000)); - - qDebug() << "topLeft : " << qSetRealNumberPrecision(15) << area.topLeft() << " - degree:" << area.topLeft()*LtsCoordTrans::rad2deg(); - qDebug() << "topLeftInc : " << qSetRealNumberPrecision(15) << area1Increased.topLeft() << " - degree:" << area1Increased.topLeft()*LtsCoordTrans::rad2deg(); - - qDebug() << "bottomRight : " << qSetRealNumberPrecision(15) << area.bottomRight() << " - degree:" << area.bottomRight()*LtsCoordTrans::rad2deg(); - qDebug() << "bottomRightInc : " << qSetRealNumberPrecision(15) << area1Increased.bottomRight() << " - degree:" << area1Increased.bottomRight()*LtsCoordTrans::rad2deg(); - - QVERIFY2( area1Increased.topLeft().x() < area.topLeft().x(), "addMarginToArea topLeft X failed"); - QVERIFY2( area1Increased.topLeft().y() < area.topLeft().y(), "addMarginToArea topLeft Y failed"); - - QVERIFY2( area1Increased.bottomRight().x() > area.bottomRight().x(), "addMarginToArea bottomRight X failed"); - QVERIFY2( area1Increased.bottomRight().y() > area.bottomRight().y(), "addMarginToArea bottomRight Y failed"); - } - - - { - qDebug() << "Luxemburg"; - QRectF area = createArea(QStringLiteral("32U LA 0000000000"), 100, 100, 5000, 5000); - QRectF area1Increased = LtsCoordTrans::addMarginToArea(area, QSize(50000, 20000)); - - qDebug() << "topLeft : " << qSetRealNumberPrecision(15) << area.topLeft() << " - degree:" << area.topLeft()*LtsCoordTrans::rad2deg(); - qDebug() << "topLeftInc : " << qSetRealNumberPrecision(15) << area1Increased.topLeft() << " - degree:" << area1Increased.topLeft()*LtsCoordTrans::rad2deg(); - - qDebug() << "bottomRight : " << qSetRealNumberPrecision(15) << area.bottomRight() << " - degree:" << area.bottomRight()*LtsCoordTrans::rad2deg(); - qDebug() << "bottomRightInc : " << qSetRealNumberPrecision(15) << area1Increased.bottomRight() << " - degree:" << area1Increased.bottomRight()*LtsCoordTrans::rad2deg(); - - QVERIFY2( area1Increased.topLeft().x() < area.topLeft().x(), "addMarginToArea topLeft X failed"); - QVERIFY2( area1Increased.topLeft().y() < area.topLeft().y(), "addMarginToArea topLeft Y failed"); - - QVERIFY2( area1Increased.bottomRight().x() > area.bottomRight().x(), "addMarginToArea bottomRight X failed"); - QVERIFY2( area1Increased.bottomRight().y() > area.bottomRight().y(), "addMarginToArea bottomRight Y failed"); - } -} - -void coordtrans_test::testIncreaseRect() -{ - - QPointF lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad(); - QString mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9); - QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000")); - - const LtsCoordTrans::UtmCoordinate centerPoint = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y()); - - LtsCoordTrans::UtmCoordinate topLeftUtm; - LtsCoordTrans::UtmCoordinate topLeftUtm2; - - - { - QPointF topLeftGeo = centerPoint.geographic(-1111, 2222); - QPointF bottomRightGeo = centerPoint.geographic( 3000, -4000); - - topLeftUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(topLeftGeo); - const LtsCoordTrans::UtmCoordinate bottomRightUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(bottomRightGeo); - qDebug() << "topLeftUtm: " << topLeftUtm.x << " - " << qSetRealNumberPrecision(15) << topLeftUtm.y; - qDebug() << "bottomRightUtm: " << bottomRightUtm.x << " - " << qSetRealNumberPrecision(15) << bottomRightUtm.y; - } - - - { - topLeftUtm2.x = centerPoint.x - 1111; - topLeftUtm2.y = centerPoint.y + 2222; - - LtsCoordTrans::UtmCoordinate bottomRightUtm2; - bottomRightUtm2.x = centerPoint.x + 3000; - bottomRightUtm2.y = centerPoint.y - 4000; - - qDebug() << "topLeftUtm2: " << topLeftUtm2.x << " - " << qSetRealNumberPrecision(15) << topLeftUtm2.y; - qDebug() << "bottomRightUtm2: " << bottomRightUtm2.x << " - " << qSetRealNumberPrecision(15) << bottomRightUtm2.y; - } - - QVERIFY2(qFuzzyCompare(topLeftUtm.x, topLeftUtm2.x), "testIncreaseRect x failed"); - QVERIFY2(qFuzzyCompare(topLeftUtm.y, topLeftUtm2.y), "testIncreaseRect y failed"); -} - -void coordtrans_test::testIncreaseRect2() -{ - - QPointF lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad(); - QString mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9); - QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000")); - - QRectF myRect; - QRectF myRectIncreased; - - { - const LtsCoordTrans::UtmCoordinate centerPoint = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y()); - - QPointF topLeftGeo = centerPoint.geographic(-3000, 2000); - QPointF bottomRightGeo = centerPoint.geographic( 3000, -2000); - myRect = QRectF(topLeftGeo, bottomRightGeo).normalized(); - - qDebug() << "myRect : " << myRect; - qDebug() << "myRect center : " << myRect.center(); - qDebug() << "myRect height : " << myRect.height(); - } - - - { - const QPointF centerGeo = myRect.center(); - const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerGeo); - - QPointF topLeftGeo = centerUtm.geographic(-6000, 4000); - QPointF bottomRightGeo = centerUtm.geographic( 6000, -4000); - - myRectIncreased = QRectF(topLeftGeo, bottomRightGeo).normalized(); - - qDebug() << "myRectIncreased : " << myRectIncreased; - qDebug() << "myRectIncreased center: " << myRectIncreased.center(); - qDebug() << "myRectIncreased height: " << myRectIncreased.height(); - - } - - - QVERIFY2( (int)(myRectIncreased.center().x()*10000) == (int)(myRect.center().x()*10000), "testIncreaseRect2 centerX failed"); - QVERIFY2( (int)(myRectIncreased.center().y()*10000) == (int)(myRect.center().y()*10000), "testIncreaseRect2 centerY failed"); - - QVERIFY2(myRectIncreased.width() > myRect.width(), "testIncreaseRect2 width failed"); - QVERIFY2(myRectIncreased.height() > myRect.height(), "testIncreaseRect2 height failed"); - -} - - - -void coordtrans_test::testGeographicFromMgrs() -{ - - QString mgrs(QStringLiteral("32UPD7654398765")); - - - QPointF geographic = LtsCoordTrans::geographicFromMgrs(mgrs); - QCOMPARE(geographic, QPointF(11.6436815550, 53.2088411454) * LtsCoordTrans::deg2rad()); -} - -void coordtrans_test::testMgrsSquares() -{ - - - - - checkArea(QStringLiteral("32U PD 0000000000"), 50000, 10000, 30000, 30000, { QStringLiteral("32U PC"), - QStringLiteral("32U PD") - }); - checkArea(QStringLiteral("32U MD 0000000000"), 50000, 10000, 30000, 30000, { QStringLiteral("32U MC"), - QStringLiteral("32U MD") - }); - checkArea(QStringLiteral("32U MD 0000000000"), 89999, 89999, 21000, 21000, { QStringLiteral("32U MD"), - QStringLiteral("32U ME"), - QStringLiteral("32U ND"), - QStringLiteral("32U NE") - }); - - checkArea(QStringLiteral("39Q XG 0000000000"), 40000, 35000, 60000, 70000, { QStringLiteral("39Q XG"), - QStringLiteral("39R XG") - }); - checkArea(QStringLiteral("40R CP 0000000000"), 94000, 9000, 7000, 16000, { QStringLiteral("40R CP") }); - checkArea(QStringLiteral("40R DN 0000000000"), 500, 95500, 12000, 7000, { QStringLiteral("40R CN"), - QStringLiteral("40R DN") - }); - checkArea(QStringLiteral("40R DN 0000000000"), 500, 96760, 30000, 30000, { QStringLiteral("40R CN"), - QStringLiteral("40R CP"), - QStringLiteral("40R DN"), - QStringLiteral("40R DP") - }); - checkArea(QStringLiteral("40R BM 0000000000"), 59000, 59500, 46000, 40000, { QStringLiteral("40Q BM"), - QStringLiteral("40R BM") - }); - - - - checkArea(QStringLiteral("32U LA 0000000000"), 10000, 10000, 5000, 5000, { QStringLiteral("32U LA"), - }); - - - checkArea(QStringLiteral("32U LA 0000000000"), 100, 100, 60000, 60000, { QStringLiteral("31U GQ"), - QStringLiteral("31U GR"), - QStringLiteral("32U KA"), - QStringLiteral("32U KV"), - QStringLiteral("32U LA"), - QStringLiteral("32U LV") - }); - - checkArea(QStringLiteral("32U LA 0000000000"), 100, 100,150000,150000, { QStringLiteral("31U FQ"), - QStringLiteral("31U FR"), - QStringLiteral("31U GQ"), - QStringLiteral("31U GR"), - QStringLiteral("32U KA"), - QStringLiteral("32U KV"), - QStringLiteral("32U LA"), - QStringLiteral("32U LV") - }); - - checkArea(QStringLiteral("32U LA 0000000000"), 100, 100,200000,200000, { QStringLiteral("31U FP"), - QStringLiteral("31U FQ"), - QStringLiteral("31U FR"), - QStringLiteral("31U GP"), - QStringLiteral("31U GQ"), - QStringLiteral("31U GR"), - QStringLiteral("31U GS"), - QStringLiteral("32U KA"), - QStringLiteral("32U KB"), - QStringLiteral("32U KV"), - QStringLiteral("32U LA"), - QStringLiteral("32U LB"), - QStringLiteral("32U LV"), - QStringLiteral("32U MA"), - QStringLiteral("32U MB"), - QStringLiteral("32U MV") - }); - -} - - -QVector checkAreaGetMgrsSquares(const QString &base, - int xCenter, int yCenter, int width, int height) -{ - - QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height); - LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base))); - const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top())); - const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom())); - - - - const auto squares = LtsCoordTrans::getMgrsSquares(QRectF(topLeft, bottomRight), false, false); - - QStringList src; - src.reserve(squares.size()); - for (const auto &utm : squares) - src += utm.mgrs(0); - src.sort(); - - - return squares; -} - -void coordtrans_test::testMgrsSquaresWithSurrounding() -{ - - - - - - - - - - - - - - - - - int initialSize = 5000; - - - QVector squares1 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, initialSize, initialSize); - - QVERIFY2(squares1.size() == 1, "checkAreaGetMgrsSquares failed"); - - - - - int biggerSize = initialSize + (1*110000); - QVector squares9 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, biggerSize, biggerSize); - - QVERIFY2(squares9.size() == 9, "checkAreaGetMgrsSquares biggerSize failed"); - - int muchBiggerSize = initialSize + (3*110000); - QVector square33 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, muchBiggerSize, muchBiggerSize); - - QVERIFY2(square33.size() == 33, "checkAreaGetMgrsSquares muchBiggerSize failed"); - - - QStringList string1; - string1.reserve(squares1.size()); - for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(squares1)) - string1.append(item.mgrs(0)); - - QStringList string9; - string9.reserve(squares9.size()); - for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(squares9)) - string9.append(item.mgrs(0)); - - QStringList string33; - string33.reserve(square33.size()); - for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(square33)) - string33.append(item.mgrs(0)); - - { - QStringList surroundingStrings; - surroundingStrings.reserve(squares9.size()); - for (const QString &str : qAsConst(string9)) - if (!string1.contains(str)) - surroundingStrings.append(str); - - QVERIFY2(surroundingStrings.size() == 8, "checkAreaGetMgrsSquares squaresDiff failed"); - } - - { - QStringList surroundingStrings; - surroundingStrings.reserve(string33.size()); - for (const QString &str : qAsConst(string33)) - if (!string1.contains(str)) - surroundingStrings.append(str); - - QVERIFY2(surroundingStrings.size() == 32, "checkAreaGetMgrsSquares squaresDiff failed"); - } - -} - -void coordtrans_test::checkArea(const QString &base, - int xCenter, int yCenter, int width, int height, - const QStringList &coords) -{ - qDebug() << "checkArea : base: " << base; - QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height); - LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base))); - const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top())); - const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom())); - qDebug() << "TopLeft: " << topLeft * LtsCoordTrans::rad2deg(); - qDebug() << "BottomRight: " << bottomRight * LtsCoordTrans::rad2deg(); - - const auto squares = LtsCoordTrans::getMgrsSquares(QRectF(topLeft, bottomRight), false, false); - - QStringList src; - src.reserve(squares.size()); - for (const auto &utm : squares) - src += utm.mgrs(0); - src.sort(); - qDebug() << "src: '" << src << "' expected: '" << coords << "'"; - QCOMPARE(src, coords); -} - - diff --git a/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.h b/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.h deleted file mode 100755 index 9bc7183d2..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_trans_unit_test.h +++ /dev/null @@ -1,34 +0,0 @@ - - -#pragma once - -#include -#include - -class coordtrans_test : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void initTestCase(); - void cleanupTestCase(); - void testFromGeographic(); - void testMgrsFromGeographic(); - - void testRelativeMetricalOffet_data(); - void testRelativeMetricalOffet(); - - void testAddMarginToArea(); - void testIncreaseRect(); - void testIncreaseRect2(); - - void testGeographicFromMgrs(); - void testMgrsSquares(); - - void testMgrsSquaresWithSurrounding(); - -private: - void checkArea(const QString &base, - int xCenter, int yCenter, int width, int height, - const QStringList &coords); - -}; diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget.cpp b/Lts/Common/LtsCoordTrans/lts_coord_widget.cpp deleted file mode 100755 index c64012c0f..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget.cpp +++ /dev/null @@ -1,196 +0,0 @@ - - - -#include "lts_coord_widget.h" -#include "lts_coord_widget_p.h" - -#include -#include - -namespace LtsCoordWidget -{ - -LtsCoordWidget::LtsCoordWidget(DisplayType dt, QWidget *parent) - : QWidget(parent) - , m_displayType(dt) -{ - setFocusPolicy(Qt::StrongFocus); - rebuildUi(); -} - -void LtsCoordWidget::setCoordinate(const QPointF &geoCoord, bool bUseAsDefaultValue) -{ - if (m_coordinate != geoCoord) - emit coordinateChanged(geoCoord); - if (bUseAsDefaultValue) - m_defaultCoordinate = geoCoord; - if (m_inputWidgetHandler) - m_inputWidgetHandler->setCoordinate(geoCoord); -} - -void LtsCoordWidget::clear() -{ - if (m_inputWidgetHandler) - m_inputWidgetHandler->clear(); -} - -void LtsCoordWidget::setCoordinate(double lon, double lat, bool bUseAsDefaultValue) -{ - setCoordinate(QPointF(lon, lat), bUseAsDefaultValue); -} - -QPointF LtsCoordWidget::coordinate() const -{ - if (!m_inputWidgetHandler) - return QPointF(); - return m_inputWidgetHandler->coordinate(); -} - - -LtsCoordWidget::DisplayType LtsCoordWidget::displayType() const -{ - return m_displayType; -} - -void LtsCoordWidget::setDisplayType(LtsCoordWidget::DisplayType dt) -{ - if (m_displayType == dt) - return; - m_displayType = dt; - rebuildUi(); -} - -bool LtsCoordWidget::withResetButton() const -{ - return m_bWithResetButton; -} - -void LtsCoordWidget::setWithResetButton(bool bWith) -{ - if (m_bWithResetButton == bWith) - return; - m_bWithResetButton = bWith; - if (m_inputWidgetHandler) - m_inputWidgetHandler->resetButton()->setVisible(m_bWithResetButton); -} - -bool LtsCoordWidget::showToolTip() const -{ - return m_bShowToolTip; -} - -void LtsCoordWidget::setShowToolTip(bool bShow) -{ - if (m_bShowToolTip == bShow) - return; - m_bShowToolTip = bShow; - - updateToolTip(); -} - -bool LtsCoordWidget::mgrsSquareEditable() const -{ - return m_bMgrsSquareEditable; -} - -void LtsCoordWidget::setMgrsSquareEditable(bool bEditable) -{ - if (m_bMgrsSquareEditable == bEditable) - return; - m_bMgrsSquareEditable = bEditable; - if (m_inputWidgetHandler) - m_inputWidgetHandler->setSquaresEditable(bEditable); -} - -void LtsCoordWidget::setWithSurroundingMgrsSquares(bool bEnable) -{ - if (m_inputWidgetHandler) - m_inputWidgetHandler->setWithSurroundingSquare(bEnable); -} - -void LtsCoordWidget::setMgrsSquares(const QVector &geoCoords) -{ - if (m_inputWidgetHandler) - m_inputWidgetHandler->setMgrsSquares(geoCoords); -} - - -bool LtsCoordWidget::event(QEvent *event) -{ - if (event->type() == QEvent::ToolTip) - updateToolTip(); - return QWidget::event(event); -} - - -void LtsCoordWidget::rebuildUi() -{ - - - - - if (m_inputWidgetHandler) - { - m_inputWidgetHandler->cleanup(); - delete m_inputWidgetHandler; - delete layout(); - } - - switch (m_displayType) - { - case DisplayType::MGRS5: - m_inputWidgetHandler = new MgrsEdit(5, this); - break; - case DisplayType::MGRS7: - m_inputWidgetHandler = new MgrsEdit(7, this); - break; - case DisplayType::UTM: - - qt_assert("DisplayType::UTM not yet implemented", __FILE__, __LINE__); - break; - case DisplayType::WGS84: - m_inputWidgetHandler = new Wgs84Edit(this); - break; - } - if (!m_inputWidgetHandler) - return; - - m_inputWidgetHandler->setSquaresEditable(m_bMgrsSquareEditable); - m_inputWidgetHandler->resetButton()->setVisible(m_bWithResetButton); - connect(m_inputWidgetHandler->resetButton(), &QToolButton::clicked, - this, &LtsCoordWidget::onResetToDefaultCoordinate); - connect(m_inputWidgetHandler, &LtsCoordInputWidgetHandler::coordinateChanged, - this, &LtsCoordWidget::coordinateChanged); - connect(m_inputWidgetHandler, &LtsCoordInputWidgetHandler::invalidCoordinate, - this, &LtsCoordWidget::invalidCoordinate); - if (!m_defaultCoordinate.isNull()) - m_inputWidgetHandler->setCoordinate(m_defaultCoordinate); -} - -void LtsCoordWidget::updateToolTip() -{ - QString tt; - if (m_bShowToolTip) - { - const QPointF p(coordinate()); - if (!p.isNull()) - { - const QString mgrs = LtsCoordTrans::mgrsFromGeographic(p.x(), p.y()); - const QPointF p2(p * LtsCoordTrans::rad2deg()); - tt = tr("MGRS: %1\nlat/lon: %2/%3").arg(mgrs).arg(p2.y(), 0, 'f', 10).arg(p2.x(), 0, 'f', 10); - } - } - setToolTip(tt); -} - - -void LtsCoordWidget::onResetToDefaultCoordinate() -{ - if (!m_inputWidgetHandler) - return; - if (m_defaultCoordinate.isNull()) - m_inputWidgetHandler->clear(); - m_inputWidgetHandler->setCoordinate(m_defaultCoordinate); -} - -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget.h b/Lts/Common/LtsCoordTrans/lts_coord_widget.h deleted file mode 100755 index 92c2f1331..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget.h +++ /dev/null @@ -1,78 +0,0 @@ - - -#pragma once - -#include -#include -#include - -class QToolButton; - -namespace LtsCoordWidget -{ -class LtsCoordInputWidgetHandler; -class LTSCOORDWIDGET_EXPORT LtsCoordWidget : public QWidget -{ - Q_OBJECT -public: - enum class DisplayType - { - MGRS5, - MGRS7, - UTM, - WGS84, - MGRS = DisplayType::MGRS5 - }; - Q_ENUM(DisplayType) - Q_PROPERTY(DisplayType displayType READ displayType WRITE setDisplayType) - Q_PROPERTY(bool withResetButton READ withResetButton WRITE setWithResetButton) - Q_PROPERTY(bool showToolTip READ showToolTip WRITE setShowToolTip) - Q_PROPERTY(bool mgrsSquareEditable READ mgrsSquareEditable WRITE setMgrsSquareEditable) - - - - explicit LtsCoordWidget(QWidget *parent = nullptr) - : LtsCoordWidget(LtsCoordWidget::DisplayType::MGRS, parent) - {} - - - - explicit LtsCoordWidget(DisplayType dt, QWidget *parent = nullptr); - - QPointF coordinate() const; - DisplayType displayType() const; - bool withResetButton() const; - bool showToolTip() const; - bool mgrsSquareEditable() const; - bool event(QEvent *event) override; -public Q_SLOTS: - - - void setCoordinate(const QPointF &geoCoord, bool bUseAsDefaultValue = true); - void setCoordinate(double lon, double lat, bool bUseAsDefaultValue = true); - void setDisplayType(LtsCoordWidget::DisplayType dt); - void setWithResetButton(bool bWith); - void setShowToolTip(bool bShow); - void setMgrsSquareEditable(bool bEditable); - void setWithSurroundingMgrsSquares(bool bEnable); - void setMgrsSquares(const QVector &geoCoords); - void clear(); - -Q_SIGNALS: - void coordinateChanged(const QPointF &newGeoCoord); - void invalidCoordinate(); -private: - void rebuildUi(); - void updateToolTip(); -private Q_SLOTS: - void onResetToDefaultCoordinate(); -private: - bool m_bWithResetButton = false; - bool m_bShowToolTip = true; - bool m_bMgrsSquareEditable = false; - LtsCoordWidget::DisplayType m_displayType = LtsCoordWidget::DisplayType::MGRS; - LtsCoordInputWidgetHandler *m_inputWidgetHandler = nullptr; - QPointF m_coordinate; - QPointF m_defaultCoordinate; -}; -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget_mgrs.ui b/Lts/Common/LtsCoordTrans/lts_coord_widget_mgrs.ui deleted file mode 100755 index 1ad1ddc85..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget_mgrs.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - Mgrs - - - - 0 - 0 - 322 - 32 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Pos X - - - - - - - Pos Y - - - - - - - - :/style/arrow_back.svg:/style/arrow_back.svg - - - - - - - - diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget_p.cpp b/Lts/Common/LtsCoordTrans/lts_coord_widget_p.cpp deleted file mode 100755 index 57011cfc5..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget_p.cpp +++ /dev/null @@ -1,325 +0,0 @@ - - - -#include "lts_coord_widget_p.h" - - -namespace LtsCoordWidget -{ - - - -void LtsCoordInputWidgetHandler::onCoordinateDataChanged() -{ - const QPointF coord(coordinate()); - if (coord.isNull()) - { - emit invalidCoordinate(); - return; - } - emit coordinateChanged(coord); -} - - - - -MgrsEdit::MgrsEdit(int precision, QWidget *widget) - : LtsCoordInputWidgetHandler(widget) - , m_precision(precision) - , m_ui(new Ui::Mgrs) -{ - m_ui->setupUi(widget); - - if (m_precision < 0) - m_precision = 0; - if (m_precision > 11) - m_precision = 11; - const int upperLimit = pow(10, m_precision) - 1; - m_ui->leEast->setValidator(new QIntValidator(0, upperLimit, m_ui->leEast)); - m_ui->leNorth->setValidator(new QIntValidator(0, upperLimit, m_ui->leNorth)); - setDefaultMgrsSquares(); - - connect(m_ui->cbxZone, QOverload::of(&QComboBox::currentIndexChanged), - this, &MgrsEdit::onCoordinateDataChanged); - connect(m_ui->leEast, &QLineEdit::editingFinished, - this, &MgrsEdit::onCoordinateDataChanged); - connect(m_ui->leNorth, &QLineEdit::editingFinished, - this, &MgrsEdit::onCoordinateDataChanged); -} - -MgrsEdit::~MgrsEdit() -{ - delete m_ui; -} - -bool MgrsEdit::isValid() -{ - - const QString &east = m_ui->leEast->text(); - if (east.size() != m_precision) - return false; - - const QString &north = m_ui->leNorth->text(); - if (north.size() != m_precision) - return false; - - if (m_ui->cbxZone->currentIndex() < 0) - return false; - - if (m_ui->cbxZone->currentText().isEmpty()) - return false; - - return true; -} - -void MgrsEdit::setCoordinate(const QPointF &geoCoord) -{ - const QString mgrsString = LtsCoordTrans::mgrsFromGeographic(geoCoord.x(), geoCoord.y(), m_precision, false); - if (mgrsString.isEmpty()) - { - m_ui->leEast->clear(); - m_ui->leNorth->clear(); - return; - } - - m_ui->leEast->setText(QString()); - m_ui->leNorth->setText(QString()); - - - const QString mgrsSquare = mgrsString.left(3) + QLatin1Char(' ') + mgrsString.mid(3, 2); - QString east = mgrsString.mid(5, m_precision); - QString north = mgrsString.mid(5 + m_precision, m_precision); - int idx = m_ui->cbxZone->findText(mgrsSquare); - if (idx < 0) - { - addMgrsSquare(mgrsSquare); - idx = m_ui->cbxZone->findText(mgrsSquare); - } - m_ui->cbxZone->setCurrentIndex(idx); - m_ui->leEast->setText(east); - m_ui->leNorth->setText(north); -} - -static inline QString getMgrsCoord(QLineEdit *le, int prec) -{ - QString ret = le->text(); - if (ret.isEmpty()) - return ret; - while (ret.size() < prec) - ret += QLatin1Char('0'); - le->setText(ret); - return ret; -} - -QPointF MgrsEdit::coordinate() const -{ - const QString east = getMgrsCoord(m_ui->leEast, m_precision); - const QString north = getMgrsCoord(m_ui->leNorth, m_precision); - if (east.isEmpty() || north.isEmpty() || m_ui->cbxZone->currentIndex() < 0) - return QPointF(); - - const QString mgrsString(m_ui->cbxZone->currentText() + east + north); - return LtsCoordTrans::geographicFromMgrs(mgrsString); -} - -QToolButton *MgrsEdit::resetButton() -{ - return m_ui->tbReset; -} - -void MgrsEdit::cleanup() -{ - - - delete m_ui->horizontalLayout; - delete m_ui->cbxZone; - delete m_ui->leEast; - delete m_ui->leNorth; - delete m_ui->tbReset; -} - -void MgrsEdit::clear() -{ - m_ui->cbxZone->clear(); - m_ui->leEast->clear(); - m_ui->leNorth->clear(); - setDefaultMgrsSquares(); -} - -void MgrsEdit::setSquaresEditable(bool bEditable) -{ - m_ui->cbxZone->setEditable(bEditable); -} - -void MgrsEdit::setWithSurroundingSquare(bool bEnabled) -{ - m_withMgrsSquareOffset = bEnabled; -} - -static QStringList defaultMgrsSquares() -{ - const QVector squares(LtsCoordTrans::defaultMgrsSquares()); - QStringList sl; - sl.reserve(squares.size()); - for (const LtsCoordTrans::UtmCoordinate &c : squares) - sl += c.mgrs(0); - std::sort(sl.begin(), sl.end()); - return sl; -} - -static QString preferedMgrsSquare() -{ - const LtsCoordTrans::UtmCoordinate c(LtsCoordTrans::preferedMgrsSquare()); - return c.mgrs(0); -} - - -void MgrsEdit::setDefaultMgrsSquares() -{ - m_ui->cbxZone->addItems(defaultMgrsSquares()); - m_ui->cbxZone->setCurrentIndex(m_ui->cbxZone->findText(preferedMgrsSquare())); -} - -void MgrsEdit::addMgrsSquare(const QString &newMgrsSquare) -{ - QStringList sl = defaultMgrsSquares(); - sl += newMgrsSquare; - if (m_withMgrsSquareOffset) - { - QStringList squares = utmCoordsToList(LtsCoordTrans::defaultMgrsSquareWithOffsets()); - - for (const auto &sq : qAsConst(squares)) - if (!sl.contains(sq)) - sl.append(sq); - } - std::sort(sl.begin(), sl.end()); - m_ui->cbxZone->clear(); - m_ui->cbxZone->addItems(sl); -} - -QStringList MgrsEdit::utmCoordsToList(const QVector &coords) -{ - QStringList list; - list.reserve(coords.count()); - for (const auto &utm : coords) - list.append(utm.mgrs(0)); - - return list; -} - -void MgrsEdit::setMgrsSquares(const QVector &geoCoords) -{ - QStringList sl = utmCoordsToList(geoCoords); - if (sl.isEmpty()) - return; - - std::sort(sl.begin(), sl.end()); - m_ui->cbxZone->clear(); - m_ui->cbxZone->addItems(sl); -} - - - - -Wgs84Edit::Wgs84Edit(QWidget *widget) - : LtsCoordInputWidgetHandler(widget) - , m_ui(new Ui::Wgs84) -{ - m_ui->setupUi(widget); - - DoubleValidator *v = new DoubleValidator(-180, 180, 9, m_ui->leLon); - v->setNotation(QDoubleValidator::StandardNotation); - m_ui->leLon->setValidator(v); - - v = new DoubleValidator(-180, 180, 9, m_ui->leLat); - v->setNotation(QDoubleValidator::StandardNotation); - m_ui->leLat->setValidator(v); - - connect(m_ui->leLon, &QLineEdit::editingFinished, - this, &Wgs84Edit::onCoordinateDataChanged); - connect(m_ui->leLat, &QLineEdit::editingFinished, - this, &Wgs84Edit::onCoordinateDataChanged); -} - -Wgs84Edit::~Wgs84Edit() -{ - delete m_ui; -} - -bool Wgs84Edit::isValid() -{ - - return (!m_ui->leLon->text().isEmpty()) && (!m_ui->leLat->text().isEmpty()); -} - -void Wgs84Edit::setCoordinate(const QPointF &geoCoord) -{ - const QPointF coord(geoCoord * LtsCoordTrans::rad2deg()); - const QLocale l; - m_ui->leLon->setText(l.toString(coord.x(), 'f', 9)); - m_ui->leLat->setText(l.toString(coord.y(), 'f', 9)); -} - -QPointF Wgs84Edit::coordinate() const -{ - QPointF ret; - const QLocale l; - - - const QString lonStr = m_ui->leLon->text().remove(l.groupSeparator()); - const QString latStr = m_ui->leLat->text().remove(l.groupSeparator()); - if (lonStr.isEmpty() || latStr.isEmpty()) - return ret; - - ret.rx() = l.toDouble(lonStr); - ret.ry() = l.toDouble(latStr); - - return ret * LtsCoordTrans::deg2rad(); -} - -QToolButton *Wgs84Edit::resetButton() -{ - return m_ui->tbReset; -} - -void Wgs84Edit::cleanup() -{ - - - delete m_ui->horizontalLayout; - delete m_ui->leLon; - delete m_ui->leLat; - delete m_ui->tbReset; - delete m_ui->lblLon; - delete m_ui->lblLat; -} - -void Wgs84Edit::clear() -{ - m_ui->leLon->clear(); - m_ui->leLat->clear(); -} - - - - -DoubleValidator::DoubleValidator(double bottom, double top, int decimals, QObject *parent) - : QDoubleValidator(bottom, top, decimals, parent) -{} - -QValidator::State DoubleValidator::validate(QString &input, int &pos) const -{ - const QValidator::State ret = QDoubleValidator::validate(input, pos); - if (ret != QValidator::Acceptable) - return ret; - QLocale l; - const QChar gs(l.groupSeparator()); - if (gs.isNull()) - return ret; - if (input.contains(gs)) - return QValidator::Invalid; - return ret; -} - -} - diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget_p.h b/Lts/Common/LtsCoordTrans/lts_coord_widget_p.h deleted file mode 100755 index 93553568a..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget_p.h +++ /dev/null @@ -1,93 +0,0 @@ - - -#pragma once - -#include -#include -#include -#include - -#include "ui_lts_coord_widget_mgrs.h" -#include "ui_lts_coord_widget_wgs84.h" - -namespace LtsCoordWidget -{ -class LtsCoordInputWidgetHandler : public QObject -{ - Q_OBJECT -public: - explicit LtsCoordInputWidgetHandler(QObject *parent) - : QObject(parent) - {} - - virtual bool isValid() = 0; - virtual void setCoordinate(const QPointF &geoCoord) = 0; - virtual QPointF coordinate() const = 0; - virtual QToolButton *resetButton() = 0; - virtual void cleanup() = 0; - virtual void clear() = 0; - virtual void setSquaresEditable(bool bEditable) = 0; - virtual void setWithSurroundingSquare(bool bEnabled) = 0; - virtual void setMgrsSquares(const QVector &geoCoords) = 0; -protected Q_SLOTS: - void onCoordinateDataChanged(); -Q_SIGNALS: - void coordinateChanged(const QPointF &newGeoCoord); - void invalidCoordinate(); -}; - -class MgrsEdit final : public LtsCoordInputWidgetHandler -{ - Q_OBJECT -public: - explicit MgrsEdit(int precision, QWidget *widget); - ~MgrsEdit(); - - bool isValid() override; - void setCoordinate(const QPointF &geoCoord) override; - QPointF coordinate() const override; - QToolButton *resetButton() override; - void cleanup() override; - void clear() override; - void setSquaresEditable(bool bEditable) override; - void setWithSurroundingSquare(bool bEnabled) override; - void setMgrsSquares(const QVector &geoCoords) override; -private: - void setDefaultMgrsSquares(); - void addMgrsSquare(const QString &newMgrsSquare); - QStringList utmCoordsToList(const QVector &coords); -private: - int m_precision; - bool m_withMgrsSquareOffset=false; - Ui::Mgrs *m_ui; -}; - -class Wgs84Edit final : public LtsCoordInputWidgetHandler -{ - Q_OBJECT -public: - explicit Wgs84Edit(QWidget *widget); - ~Wgs84Edit(); - - bool isValid() override; - void setCoordinate(const QPointF &geoCoord) override; - QPointF coordinate() const override; - QToolButton *resetButton() override; - void cleanup() override; - void clear() override; - void setSquaresEditable(bool bEditable) override { Q_UNUSED(bEditable); } - void setWithSurroundingSquare(bool bEnabled) override { Q_UNUSED(bEnabled); } - void setMgrsSquares(const QVector &geoCoords) override { Q_UNUSED(geoCoords); } -private: - Ui::Wgs84 *m_ui; -}; - -class DoubleValidator final : public QDoubleValidator -{ - Q_OBJECT -public: - DoubleValidator(double bottom, double top, int decimals, QObject *parent = nullptr); - - QValidator::State validate(QString &input, int &pos) const override; -}; -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget_test.cpp b/Lts/Common/LtsCoordTrans/lts_coord_widget_test.cpp deleted file mode 100755 index e9ae4075f..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget_test.cpp +++ /dev/null @@ -1,115 +0,0 @@ - - - -#include -#include -#include -#include -#include -#include -#include "lts_coord_widget.h" - -static void updateLabel(const QPointF &coord, QLabel *lbl, int prec) -{ - QString mgrs = LtsCoordTrans::mgrsFromGeographic(coord.x(), coord.y(), prec); - QString str = QStringLiteral("mgrs: %1, lon: %2, lat: %3") - .arg(mgrs) - .arg(coord.x() * LtsCoordTrans::rad2deg(), 0, 'f', 10) - .arg(coord.y() * LtsCoordTrans::rad2deg(), 0, 'f', 10); - lbl->setText(str); -} - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - - LtsCoordTrans::UtmCoordinate base(LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32UMD00"))); - QPointF topLeft(base.geographic(+50000, -50000)); - QPointF bottomRight(base.geographic(-50000, 50000)); - LtsCoordTrans::getMgrsSquares(QRectF(topLeft, bottomRight), true, true); - - QDialog *dialog(new QDialog); - QVBoxLayout *lay(new QVBoxLayout(dialog)); - - - const QPointF defCoord(QPointF(53.0, 8.0) * LtsCoordTrans::deg2rad()); - - LtsCoordWidget::LtsCoordWidget *wMgrs5(new LtsCoordWidget::LtsCoordWidget(LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS, dialog)); - wMgrs5->setWithResetButton(true); - wMgrs5->setCoordinate(defCoord, true); - lay->addWidget(wMgrs5); - QLabel *lbl5(new QLabel(dialog)); - lay->addWidget(lbl5); - QObject::connect(wMgrs5, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, - lbl5, [lbl5](const QPointF & coord) - { - updateLabel(coord, lbl5, 5); - }); - - - LtsCoordWidget::LtsCoordWidget *wMgrs7(new LtsCoordWidget::LtsCoordWidget(LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS7, dialog)); - wMgrs7->setWithResetButton(true); - wMgrs7->setCoordinate(defCoord, true); - lay->addWidget(wMgrs7); - QLabel *lbl7(new QLabel(dialog)); - lay->addWidget(lbl7); - QObject::connect(wMgrs7, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, - wMgrs7, [lbl7](const QPointF & coord) - { - updateLabel(coord, lbl7, 7); - }); - - - LtsCoordWidget::LtsCoordWidget *w84(new LtsCoordWidget::LtsCoordWidget(LtsCoordWidget::LtsCoordWidget::DisplayType::WGS84, dialog)); - w84->setWithResetButton(true); - w84->setCoordinate(defCoord, true); - lay->addWidget(w84); - QLabel *lbl84(new QLabel(dialog)); - lay->addWidget(lbl84); - QObject::connect(w84, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, - w84, [lbl84](const QPointF & coord) - { - updateLabel(coord, lbl84, 11); - }); - - - QHBoxLayout *hLay = new QHBoxLayout; - lay->addLayout(hLay); - LtsCoordWidget::LtsCoordWidget *wChange(new LtsCoordWidget::LtsCoordWidget(LtsCoordWidget::LtsCoordWidget::DisplayType::WGS84, dialog)); - wChange->setWithResetButton(true); - wChange->setCoordinate(defCoord, true); - hLay->addWidget(wChange); - QPushButton *pbReset(new QPushButton(QStringLiteral("With Reset"), dialog)); - hLay->addWidget(pbReset); - QObject::connect(pbReset, &QPushButton::clicked, - wChange, [wChange]() { wChange->setWithResetButton(!wChange->withResetButton());}); - QPushButton *pbNext(new QPushButton(QStringLiteral("Next Type"), dialog)); - hLay->addWidget(pbNext); - QObject::connect(pbNext, &QPushButton::clicked, - wChange, [wChange]() - { - LtsCoordWidget::LtsCoordWidget::DisplayType dt = wChange->displayType(); - switch (dt) - { - case LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS5: - dt = LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS7; break; - case LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS7: - dt = LtsCoordWidget::LtsCoordWidget::DisplayType::WGS84; break; - case LtsCoordWidget::LtsCoordWidget::DisplayType::WGS84: - dt = LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS5; break; - default: - break; - } - wChange->setDisplayType(dt); - }); - - - QPushButton *pb(new QPushButton(QStringLiteral("Quit"), dialog)); - lay->addWidget(pb); - QObject::connect(pb, &QPushButton::clicked, qApp, QCoreApplication::quit); - - dialog->show(); - int ret = app.exec(); - delete dialog; - return ret; -} diff --git a/Lts/Common/LtsCoordTrans/lts_coord_widget_wgs84.ui b/Lts/Common/LtsCoordTrans/lts_coord_widget_wgs84.ui deleted file mode 100755 index eaf643260..000000000 --- a/Lts/Common/LtsCoordTrans/lts_coord_widget_wgs84.ui +++ /dev/null @@ -1,58 +0,0 @@ - - - Wgs84 - - - - 0 - 0 - 293 - 32 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Lat: - - - - - - - - - - Lon: - - - - - - - - - - - icon:arrow_back.svgicon:arrow_back.svg - - - - - - - - diff --git a/Lts/Common/LtsCoordTrans/mgrs2lonlat.cpp b/Lts/Common/LtsCoordTrans/mgrs2lonlat.cpp deleted file mode 100755 index b74e2b10f..000000000 --- a/Lts/Common/LtsCoordTrans/mgrs2lonlat.cpp +++ /dev/null @@ -1,45 +0,0 @@ - - - -#include -#include -#include - -int main(int argc, char **argv) -{ - if (argc != 2) - { - fprintf(stderr, "Usage: %s \n", argv[0]); - return -1; - } - - std::string mgrs = argv[1]; - mgrs.erase(remove_if(mgrs.begin(), mgrs.end(), isspace), mgrs.end()); - - int prec; - double lonDeg, latDeg; - try - { - int zone; - bool northp; - double x, y; - GeographicLib::MGRS::Reverse(mgrs, zone, northp, x, y, prec, true); - GeographicLib::UTMUPS::Reverse(zone, northp, x, y, latDeg, lonDeg); - - GeographicLib::UTMUPS::Forward(latDeg, lonDeg, zone, northp, x, y); - GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs); - } - catch (...) - { - fprintf(stderr, "Error converting %s to Lon/Lat coords\n", mgrs.c_str()); - return -2; - } - std::string part1, part2, part3, part4; - part1 = mgrs.substr(0, 3); - part2 = mgrs.substr(3, 2); - part3 = mgrs.substr(5, prec); - part4 = mgrs.substr(5 + prec, prec); - fprintf(stdout, "%s %s %s %s = %13.10f, %13.10f ( %13.10f, %13.10f )\n", - part1.c_str(), part2.c_str(), part3.c_str(), part4.c_str(), lonDeg, latDeg, lonDeg / 180. * M_PI, latDeg / 180. * M_PI); - return 0; -} diff --git a/Lts/Common/LtsCoordinate/CMakeLists.txt b/Lts/Common/LtsCoordinate/CMakeLists.txt deleted file mode 100755 index b2455253d..000000000 --- a/Lts/Common/LtsCoordinate/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -NGL_ADD_LIBRARY2(WS SV HLA LtsCoordinate SHARED - lts_coordinate.h - lts_coordinate.cpp - lts_coord_vector.h - lts_coord_vector.cpp -) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsCoordinate - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsCoordinate - PRIVATE - LtsCoordTrans - PUBLIC - ddscore - LtsUtilsCoreHeaderOnly - Qt5::Core -) - -ADD_SUBDIRECTORY(UnitTest_Coordinate) diff --git a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt b/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt deleted file mode 100755 index fe69519af..000000000 --- a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# -# @RDE_HEADER@ -# $Id$ -# $HeadURL$ -# $Author$ -# - -SET(UnitTestSources - lts_coordinate_unit_test.cpp - lts_coordinate_unit_test.h -) - -IF(BUILD_TESTING AND TARGET LtsElevation) -ADD_EXECUTABLE(commonUnitTestCoordinate ${LinkedSources} ${UnitTestSources} ) - -TARGET_LINK_LIBRARIES(commonUnitTestCoordinate - NGLLogger - nglcoreapplication - LtsCoordinate - LtsElevation - LtsCoordTrans - situationMapCommon - situationMapElevationMap - Qt5::Core - Qt5::Gui - Qt5::Test) - -ADD_TEST(NAME commonUnitTestCoordinate COMMAND $) - -ENDIF() diff --git a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp b/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp deleted file mode 100755 index e5dc118ab..000000000 --- a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp +++ /dev/null @@ -1,971 +0,0 @@ - - - -#include "lts_coordinate_unit_test.h" - -#include -#include -#include -#include - -#include -#include -#include - - -Q_DECLARE_METATYPE(LtsUtils::QPoint3D); - - -#include -#include - -static const int s_id{4711}; -static LtsElevation::LtsElevationManager *s_eleMgr{nullptr}; - - - - - - - -class MyLtsElevationManagerBootstrap : public LtsElevation::LtsElevationManager -{ - using BaseClass = LtsElevation::LtsElevationManager; - -public: - static LtsElevationManager *createInstanceUnitTest(int id, const QStringList &filePaths_rough_to_fine) - { - return BaseClass::createInstanceUnitTest(id, filePaths_rough_to_fine); - } - -}; - - - - -UnitTestCoordinate::UnitTestCoordinate() - : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTestCoordinate")) -{} - - -void UnitTestCoordinate::initTestCase() -{ - - - QString filePath(QStringLiteral("/usr/share/arcgis/resources/elevation/Altmark_dt1.tif")); - - s_eleMgr = MyLtsElevationManagerBootstrap::createInstanceUnitTest(s_id, {filePath}); - - const bool bIsValid = s_eleMgr->isValid(); - std::cerr << "myBootstrapMgr->isValid() 1 = " << bIsValid << std::endl; - - - { - s_eleMgr->waitForInitialization(); - const bool bIsValid = s_eleMgr->isValid(); - std::cerr << "myBootstrapMgr->isValid() 2 = " << bIsValid << std::endl; - } - - QVERIFY2(s_eleMgr , "static LtsElevationManager does not exist"); - - - - - using namespace std::placeholders; - LtsCoordTrans::LtsCoordinate::elevationCbFunc elevationCB = std::bind(&LtsElevation::LtsElevationManager::elevation, - s_eleMgr, _1, _2, _3, _4); - LtsCoordTrans::LtsCoordinate::setElevationCallback(elevationCB); - - QVERIFY2(LtsCoordTrans::LtsCoordinate::hasElevationCallback() , "setElevationCallback failed"); -} - -void UnitTestCoordinate::init() -{ - QVERIFY2(s_eleMgr, "static LtsElevationManager does not exist"); - QVERIFY2(LtsCoordTrans::LtsCoordinate::hasElevationCallback() , "setElevationCallback failed"); -} - -void UnitTestCoordinate::test_NaN() -{ - bool equal = qFuzzyCompare(std::nan(""), std::nan("")); - NOTICE_LOG(m_logger) << " qFuzzyCompare(NaN, NaN) = " << equal; - - - QVERIFY2(equal == false, "qFuzzyCompare(NaN, NaN) failed"); -} - -void UnitTestCoordinate::test_coordinate_simple1() -{ - LtsCoordTrans::LtsCoordinate ltsCoord; - - QVERIFY2(ltsCoord.isValid() == false, "isValid failed"); - - const Core::GeoPosition &geoPos = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition = " << geoPos; - QVERIFY2(qFuzzyIsNull(geoPos.lon()) , "geoPosition failed"); - QVERIFY2(qFuzzyIsNull(geoPos.lat()) , "geoPosition failed"); - QVERIFY2(std::isnan( geoPos.z() ) , "geoPosition failed"); - - const QPointF &utm = ltsCoord.toUtmPointF(); - const QPointF &utm2 = ltsCoord.toUtm(); - const LtsCoordTrans::UtmCoordinate3D &utm3D = ltsCoord.toUtmCoordinate3D(); - NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm; - NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm2; - NOTICE_LOG(m_logger) << " UtmCoordinate3D = " << utm3D; - - QVERIFY2(qFuzzyCompare(utm3D.x, 166021.44308053952), "data no equal"); - QVERIFY2(qFuzzyCompare(utm3D.y, 0.0 ), "data no equal"); - QVERIFY2( utm3D.zone == 31 , "data no equal"); - QVERIFY2(utm == utm2 , "data no equal"); - - const Core::GeoPosition &geoPos2 = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition2 = " << geoPos2; - QVERIFY2(qFuzzyIsNull(geoPos2.lon()) , "geoPosition2 failed"); - QVERIFY2(qFuzzyIsNull(geoPos2.lat()) , "geoPosition2 failed"); - QVERIFY2(std::isnan( geoPos2.z() ) , "geoPosition2 failed"); -} - -void UnitTestCoordinate::test_coordinate_simple2() -{ - Core::GeoPosition defaultGeoPos; - LtsCoordTrans::LtsCoordinate ltsCoord(defaultGeoPos); - - QVERIFY2(ltsCoord.isValid() == false, "isValid failed"); - - const Core::GeoPosition &geoPos = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition = " << geoPos; - QVERIFY2(qFuzzyIsNull(geoPos.lon()) , "geoPosition failed"); - QVERIFY2(qFuzzyIsNull(geoPos.lat()) , "geoPosition failed"); - QVERIFY2(qFuzzyIsNull(geoPos.z() ) , "geoPosition failed"); - - const QPointF &utm = ltsCoord.toUtm(); - NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm; - - - const Core::GeoPosition &geoPos2 = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition2 = " << geoPos2; - QVERIFY2(qFuzzyIsNull(geoPos2.lon()) , "geoPosition2 failed"); - QVERIFY2(qFuzzyIsNull(geoPos2.lat()) , "geoPosition2 failed"); - QVERIFY2(qFuzzyIsNull(geoPos2.z() ) , "geoPosition2 failed"); -} - -void UnitTestCoordinate::test_coordinate_simple3() -{ - static const Core::GeoPosition s_invalidPos(std::nan(""), std::nan(""), std::nan("")); - - - LtsCoordTrans::LtsCoordinate ltsCoord(s_invalidPos); - - QVERIFY2(ltsCoord.isValid() == false, "isValid failed"); - - const Core::GeoPosition &geoPos = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition = " << geoPos; - QVERIFY2(std::isnan(geoPos.lon()) , "geoPosition failed"); - QVERIFY2(std::isnan(geoPos.lat()) , "geoPosition failed"); - QVERIFY2(std::isnan(geoPos.z()) , "geoPosition failed"); - - const QPointF &utm = ltsCoord.toUtm(); - NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm; - QVERIFY2(utm.isNull() , "utm failed"); -} - -void UnitTestCoordinate::test_coordinate_simple4() -{ - LtsCoordTrans::LtsCoordinate::resetConversionCounter(); - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 0, "getConversionCounter failed"); - - const LtsCoordTrans::UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const double zValue = 2.3; - - const LtsCoordTrans::LtsCoordinate ltsCoord(utmIn.geographic(), zValue); - - - - const LtsCoordTrans::UtmCoordinate &utmCoord_FirstTry = ltsCoord.utmCoordinate(); - Q_UNUSED(utmCoord_FirstTry); - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed"); - - const LtsCoordTrans::UtmCoordinate &utmCoord_SecondTry = ltsCoord.utmCoordinate(); - Q_UNUSED(utmCoord_SecondTry); - - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed"); -} - - - -void UnitTestCoordinate::test_coordinate_fromUtm() -{ - const LtsCoordTrans::UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const double zValue = 2.3; - NOTICE_LOG(m_logger) << " utmIn = " << utmIn; - - - LtsCoordTrans::LtsCoordinate ltsCoord(utmIn.geographic(), zValue); - - const LtsCoordTrans::UtmCoordinate &utmCoord = ltsCoord.utmCoordinate(); - NOTICE_LOG(m_logger) << " utmCoord = " << utmCoord; - NOTICE_LOG(m_logger) << " utmCoord = " << utmCoord.mgrs(); - QVERIFY2(utmCoord.mgrs() == QStringLiteral("32U PD 70000 10000") , "utmCoordinate failed"); - - - const Core::GeoPosition &geoPos = ltsCoord.geoPosition(); - NOTICE_LOG(m_logger) << " geoPosition = " << geoPos; - - QVERIFY2(qFuzzyCompare(geoPos.lon(), 0.200706392814992), "geoPosition failed"); - QVERIFY2(qFuzzyCompare(geoPos.lat(), 0.914792952478694), "geoPosition failed"); - QVERIFY2(qFuzzyCompare(geoPos.z() , zValue ) , "geoPosition failed"); - - - const QPointF &utmP2D = ltsCoord.toUtm(); - NOTICE_LOG(m_logger) << " utmP2D = " << utmP2D; - QVERIFY2(qFuzzyCompare(utmP2D.x(), 670000.5), "utm1 failed"); - QVERIFY2(qFuzzyCompare(utmP2D.y(), 5810000.5), "utm2 failed"); - - const LtsUtils::QPoint3D &utmP3D = ltsCoord.toUtm(); - NOTICE_LOG(m_logger) << " utmP3D = " << utmP3D; - QVERIFY2(qFuzzyCompare(utmP3D.x(), 670000.5), "utm1 failed"); - QVERIFY2(qFuzzyCompare(utmP3D.y(), 5810000.5), "utm2 failed"); - QVERIFY2(qFuzzyCompare(utmP3D.z() , zValue ), "utm3 failed"); - - const LtsCoordTrans::UtmCoordinate3D &utmCoord3D = ltsCoord.toUtmCoordinate3D(); - NOTICE_LOG(m_logger) << " UtmCoordinate3D = " << utmCoord3D; - QVERIFY2(qFuzzyCompare(utmCoord3D.x, 670000.5), "utmCoord3D failed"); - QVERIFY2(qFuzzyCompare(utmCoord3D.y, 5810000.5), "utmCoord3D failed"); - QVERIFY2(qFuzzyCompare(utmCoord3D.z(), zValue ), "utmCoord3D failed"); - - - const LtsCoordTrans::UtmCoordinate &utmCoord2D = utmCoord3D; - NOTICE_LOG(m_logger) << " utmCoord2D = " << utmCoord2D; - QVERIFY2(qFuzzyCompare(utmCoord2D.x, 670000.5), "utmCoord2D failed"); - QVERIFY2(qFuzzyCompare(utmCoord2D.y, 5810000.5), "utmCoord2D failed"); - - - const LtsCoordTrans::UtmCoordinate &utm = ltsCoord.utmCoordinate(); - QVERIFY2(utm.mgrs() == QStringLiteral("32U PD 70000 10000") , "utmCoordinate failed"); -} - -void UnitTestCoordinate::test_coordinate_fromUtm2() -{ - const LtsCoordTrans::UtmCoordinate &utmInZone32 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LA 10000 00000")); - const double zValue = 2.3; - NOTICE_LOG(m_logger) << " utmInZone32 = " << utmInZone32; - - const QPointF &utmInDefQPointF = utmInZone32.geographic(); - - const int zone31 = 31; - const LtsCoordTrans::UtmCoordinate &utmInZone31 = LtsCoordTrans::UtmCoordinate::fromGeographic(utmInDefQPointF, zone31); - NOTICE_LOG(m_logger) << " utmInZone31 = " << utmInZone31; - - NOTICE_LOG(m_logger) << " utmInZone31.geographic() = " << utmInZone31.geographic(); - NOTICE_LOG(m_logger) << " utmInZone32.geographic() = " << utmInZone32.geographic(); - - QVERIFY2(utmInZone31.geographic() == utmInZone32.geographic(), "geographic failed"); - - { - - LtsCoordTrans::LtsCoordinate ltsFromUtm31(utmInZone31.geographic(), zValue); - LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(), zValue); - - double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32); - NOTICE_LOG(m_logger) << " dist1 = " << dist; - QVERIFY2(qFuzzyCompare(dist, 0), "distance3D"); - } - { - - LtsCoordTrans::LtsCoordinate ltsFromUtm31(utmInZone31.geographic() , zValue); - LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(1, 0), zValue); - - double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32); - NOTICE_LOG(m_logger) << " dist2 = " << dist; - QVERIFY2(fabs(dist-1) < 0.00001, "distance3D"); - } - - { - - - - LtsCoordTrans::LtsCoordinate ltsFromUtm31 = LtsCoordTrans::LtsCoordinate::fromUtmCoordinate(utmInZone31, zValue); - LtsCoordTrans::LtsCoordinate ltsFromUtm32 = LtsCoordTrans::LtsCoordinate::fromUtmCoordinate(utmInZone32, zValue); - - double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32); - NOTICE_LOG(m_logger) << " dist3 = " << dist; - QVERIFY2(qFuzzyCompare(dist, 0), "distance3D"); - } - - { - LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(), zValue); - - const QPointF &utmP2D_def = ltsFromUtm32.toUtm(); - const QPointF &utmP2D_31 = ltsFromUtm32.toUtm(31); - NOTICE_LOG(m_logger) << " utmP2D_def = " << utmP2D_def; - NOTICE_LOG(m_logger) << " utmP2D_31 = " << utmP2D_31; - - LtsCoordTrans::LtsCoordinate lts1 = LtsCoordTrans::LtsCoordinate::fromUtm(utmP2D_def, 32, 0.0); - LtsCoordTrans::LtsCoordinate lts2 = LtsCoordTrans::LtsCoordinate::fromUtm(utmP2D_31, 31, 0.0); - NOTICE_LOG(m_logger) << " lts1 = " << lts1; - NOTICE_LOG(m_logger) << " lts2 = " << lts2; - - double dist = lts1.distanceUtm3D(lts2); - NOTICE_LOG(m_logger) << " dist4 = " << dist; - QVERIFY2(qFuzzyCompare(dist, 0), "distance3D"); - } -} - - -void UnitTestCoordinate::test_coordinate_elevationAt() -{ - const double zValue = 1.23; - - const LtsCoordTrans::UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - NOTICE_LOG(m_logger) << " utmIn = " << utmIn; - - LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic(), zValue); - NOTICE_LOG(m_logger) << " ltsFromUtm1 = " << ltsFromUtm; - QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), zValue) , "geoPosition failed"); - - - const double ele = ltsFromUtm.elevationAt(); - QVERIFY2(qFuzzyCompare(ele, 75), "elevationAt failed"); - - NOTICE_LOG(m_logger) << " ltsFromUtm2 = " << ltsFromUtm; - QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), zValue) , "geoPosition failed"); - - - ltsFromUtm.setZValueAccordingElevation(); - NOTICE_LOG(m_logger) << " ltsFromUtm3 = " << ltsFromUtm; - QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), 75) , "setZValueAccordingElevation failed"); - - - const LtsUtils::QPoint3D &utmP3D = ltsFromUtm.toUtm(); - NOTICE_LOG(m_logger) << " utmP3D_a = " << utmP3D; - QVERIFY2(qFuzzyCompare(utmP3D.z(), 75 ) , "utmCoordinateAsPoint3D failed"); - - { - - const double ele3 = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm); - NOTICE_LOG(m_logger) << " ele3 = " << ele3; - - QVERIFY2(qFuzzyCompare(ele, ele3 ), "LtsCoordinate::elevationAt failed"); - } - - - - - - - LtsCoordTrans::UtmCoordinate utmMoved = ltsFromUtm.utmCoordinate(); - utmMoved.x += 11; - utmMoved.y += 22; - const int zValueNew = 123; - ltsFromUtm = LtsCoordTrans::LtsCoordinate(utmMoved.geographic(), zValueNew); - NOTICE_LOG(m_logger) << " ltsFromUtm4 = " << ltsFromUtm; - - - const double ele2 = ltsFromUtm.elevationAt(); - NOTICE_LOG(m_logger) << " ltsFromUtm5 = " << ltsFromUtm; - QVERIFY2(ele2 != 75 , "setZValueAccordingElevation failed"); -} - -void UnitTestCoordinate::test_coordinate_elevationAt_isCovered() -{ - const LtsCoordTrans::UtmCoordinate &utmIn1 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const LtsCoordTrans::UtmCoordinate &utmIn2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U MD 10000 10000")); - - LtsCoordTrans::LtsCoordinate ltsFromUtm1(utmIn1.geographic(), 11); - LtsCoordTrans::LtsCoordinate ltsFromUtm2(utmIn2.geographic(), 11); - - { - bool bIsCovered = false; - double ele = ltsFromUtm1.elevationAt(-123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == true , "elevationAt1a failed"); - - - bIsCovered = false; - ele = ltsFromUtm1.elevationAt(-123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == true , "elevationAt1b failed"); - - - - bIsCovered = false; - ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm1, -123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == true , "elevationAt2a failed"); - - - bIsCovered = false; - ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm1, -123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == true , "elevationAt2b failed"); - } - - { - bool bIsCovered = true; - double ele = ltsFromUtm2.elevationAt(-123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == false , "elevationAt2 failed"); - - - bIsCovered = true; - ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm2, -123, &bIsCovered); - Q_UNUSED(ele); - QVERIFY2(bIsCovered == false , "elevationAt1 failed"); - } -} - -void UnitTestCoordinate::test_coordinate_elevation_data() -{ - QTest::addColumn("mgrs"); - QTest::addColumn("elevationIn"); - QTest::addColumn("elevationRef"); - - - QTest::newRow("elevation 10 ") << QStringLiteral("32 U PD 70000 10000") << std::nan("") << 75.0; - QTest::newRow("elevation 11 ") << QStringLiteral("32 U PD 70000 10000") << 0.0 << 75.0; - QTest::newRow("elevation 20 ") << QStringLiteral("32 U PD 72000 13000") << std::nan("") << 80.0; - QTest::newRow("elevation 21 ") << QStringLiteral("32 U PD 72000 13000") << 0.340 << 80.0; -} -void UnitTestCoordinate::test_coordinate_elevation() -{ - QFETCH(QString, mgrs); - QFETCH(double, elevationIn); - QFETCH(double, elevationRef); - - const LtsCoordTrans::UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs); - NOTICE_LOG(m_logger) << " utmIn = " << utmIn; - - LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic(), elevationIn); - NOTICE_LOG(m_logger) << " ltsFromUtm = " << ltsFromUtm; - - - NOTICE_LOG(m_logger) << " geoPosition = " << ltsFromUtm.geoPosition(); - ltsFromUtm.replaceZValueByElevationIfValue(elevationIn); - - - NOTICE_LOG(m_logger) << " geoPosition = " << ltsFromUtm.geoPosition(); - QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), elevationRef), "elevation failed"); - - - const LtsUtils::QPoint3D &utmP3D = ltsFromUtm.toUtm(); - NOTICE_LOG(m_logger) << " utmP3D = " << utmP3D; - QVERIFY2(qFuzzyCompare(utmP3D.z(), elevationRef) , "utmCoordinateAsPoint3D failed"); -} - -void UnitTestCoordinate::test_coordinate_mgrs() -{ - const LtsCoordTrans::UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - NOTICE_LOG(m_logger) << " utmIn = " << utmIn.mgrs(); - - LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic()); - - { - const QString asMgrs = ltsFromUtm.mgrs(); - NOTICE_LOG(m_logger) << " asMgrs = " << asMgrs; - QVERIFY2(asMgrs.compare(QStringLiteral("32U PD 70000 10000")) == 0, "UtmCoordinate::mgrs failed"); - } - { - const QString asMgrs = ltsFromUtm.mgrsWithZ(); - NOTICE_LOG(m_logger) << " asMgrs = " << asMgrs; - QVERIFY2(asMgrs.compare(QStringLiteral("32U PD 70000 10000 z=75.00")) == 0, "UtmCoordinate::mgrs failed"); - } - - { - const LtsCoordTrans::LtsCoordinate coord(0.200706392814992, 0.914792952478694, 123); - - - const QString str1 = coord.toString(); - NOTICE_LOG(m_logger) << " str1 cheap = " << str1; - QVERIFY2(str1.compare(QStringLiteral("LtsCoordinate(m_geo=[[lon: 0.200706392814992, lat: 0.914792952478694]z: 123], m_utm2D.x 0, m_utm2D.y 0, m_utm2D.zone -1, elevation nan)")) == 0, "toString failed"); - - - - - - const bool fullAndExpensive = true; - const QString str2 = coord.toString(fullAndExpensive); - NOTICE_LOG(m_logger) << " str2 expensive = " << str2; - QVERIFY2(str2.compare(QStringLiteral("LtsCoordinate(m_geo=[[lon: 0.200706392814992, lat: 0.914792952478694]z: 123], m_utm2D.x 670000.499999998, m_utm2D.y 5810000.5, m_utm2D.zone 32, m_utm2D.mgrs '32U PD 70000 10000', elevation 75.000)")) == 0, "toString failed"); - } - -} - -void UnitTestCoordinate::test_coordinate_distance() -{ - const LtsCoordTrans::UtmCoordinate &utmIn1 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const LtsCoordTrans::UtmCoordinate &utmIn2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70010 10100")); - NOTICE_LOG(m_logger) << " XXutmIn1 = " << utmIn1; - NOTICE_LOG(m_logger) << " XXutmIn2 = " << utmIn2; - - QVERIFY2(qFuzzyCompare((utmIn2.x - utmIn1.x), 10) , "coord failed"); - QVERIFY2(qFuzzyCompare((utmIn2.y - utmIn1.y), 100) , "coord failed"); - - const LtsCoordTrans::LtsCoordinate lts1(utmIn1.geographic(), 11); - const LtsCoordTrans::LtsCoordinate lts2(utmIn2.geographic(), 111); - - { - double dist2D = lts1.distanceUtm2D(lts2); - NOTICE_LOG(m_logger) << " dist2D = " << dist2D; - QVERIFY2(std::fabs(dist2D-100.499) < 0.01, "distanceUtm2D failed"); - } - - { - double dist3D = lts1.distanceUtm3D(lts2); - NOTICE_LOG(m_logger) << " dist3D = " << dist3D; - QVERIFY2(std::fabs(dist3D-141.774) < 0.01, "distanceUtm3D failed"); - } -} - - -void UnitTestCoordinate::test_coordinate_distanceDiffZones() -{ - const LtsCoordTrans::UtmCoordinate &utmIn1 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("31 U FR 99999 00000")); - const LtsCoordTrans::UtmCoordinate &utmIn2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LA 00000 00000")); - NOTICE_LOG(m_logger) << " XXutmIn1 = " << utmIn1; - NOTICE_LOG(m_logger) << " XXutmIn2 = " << utmIn2; - - - - LtsCoordTrans::LtsCoordinate lts1(utmIn1.geographic(), 0); - LtsCoordTrans::LtsCoordinate lts2(utmIn2.geographic(), 0); - NOTICE_LOG(m_logger) << " lts1 = " << lts1; - NOTICE_LOG(m_logger) << " lts2 = " << lts2; - - double dist = lts1.distanceUtm3D(lts2); - - NOTICE_LOG(m_logger) << " dist = " << dist; - QVERIFY2(std::fabs(dist-33382.7) < 0.1, "distance3D failed"); -} - -void UnitTestCoordinate::test_squareInDifferentZone() -{ - const LtsCoordTrans::UtmCoordinate &utm_bl = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const LtsCoordTrans::UtmCoordinate &utm_br = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 11000")); - const LtsCoordTrans::UtmCoordinate &utm_tl = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 10000")); - const LtsCoordTrans::UtmCoordinate &utm_tr = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 11000")); - - const LtsCoordTrans::LtsCoordinate lts_bl(utm_bl.geographic(), 0); - const LtsCoordTrans::LtsCoordinate lts_br(utm_br.geographic(), 0); - const LtsCoordTrans::LtsCoordinate lts_tl(utm_tl.geographic(), 0); - const LtsCoordTrans::LtsCoordinate lts_tr(utm_tr.geographic(), 0); - - { - - QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_br), 1000.0), "distance3D 1 failed"); - QVERIFY2(qFuzzyCompare(lts_tl.distanceUtm2D(lts_tr), 1000.0), "distance3D 2 failed"); - QVERIFY2(qFuzzyCompare(lts_br.distanceUtm2D(lts_tr), 1000.0), "distance3D 3 failed"); - QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_tl), 1000.0), "distance3D 4 failed"); - - QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_tr), 1414.213562373177), "distance3D 5 failed"); - QVERIFY2(qFuzzyCompare(lts_br.distanceUtm2D(lts_tl), 1414.213562373177), "distance3D 6 failed"); - } - - - - { - const LtsCoordTrans::UtmCoordinate &utm31_bl = LtsCoordTrans::UtmCoordinate(31, utm_bl.x, utm_bl.y); - const LtsCoordTrans::UtmCoordinate &utm31_br = LtsCoordTrans::UtmCoordinate(31, utm_br.x, utm_br.y); - const LtsCoordTrans::UtmCoordinate &utm31_tl = LtsCoordTrans::UtmCoordinate(31, utm_tl.x, utm_tl.y); - const LtsCoordTrans::UtmCoordinate &utm31_tr = LtsCoordTrans::UtmCoordinate(31, utm_tr.x, utm_tr.y); - - - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_br), 1000.0), "LtsCoordTrans::distance 1 failed"); - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_tl, utm31_tr), 1000.0), "LtsCoordTrans::distance 2 failed"); - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_br, utm31_tr), 1000.0), "LtsCoordTrans::distance 3 failed"); - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_tl), 1000.0), "LtsCoordTrans::distance 4 failed"); - - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_tr), 1414.213562373095), "LtsCoordTrans::distance 5 failed"); - QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_br, utm31_tl), 1414.213562373095), "LtsCoordTrans::distance 6 failed"); - } - - - - - - - - -} - -void UnitTestCoordinate::test_coordinate_angle() -{ - - - const LtsCoordTrans::UtmCoordinate &utm_ul = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const LtsCoordTrans::LtsCoordinate lts_ul(utm_ul.geographic(), 0); - - { - - const LtsCoordTrans::UtmCoordinate &utm_2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 11000")); - const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0); - double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2); - QVERIFY2(qFuzzyCompare(angle, 0.0), "angleUtm2D 1 failed"); - } - - { - - const LtsCoordTrans::UtmCoordinate &utm_2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 10000")); - const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0); - double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2); - QVERIFY2(qFuzzyCompare(angle, 90.0), "angleUtm2D 1 failed"); - } - - { - - const LtsCoordTrans::UtmCoordinate &utm_2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 09000")); - const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0); - double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2); - QVERIFY2(qFuzzyCompare(angle, 180.0), "angleUtm2D 1 failed"); - } - - { - - const LtsCoordTrans::UtmCoordinate &utm_2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 69000 10000")); - const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0); - double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2); - QVERIFY2(qFuzzyCompare(angle, 270.0), "angleUtm2D 1 failed"); - } - - - { - const LtsCoordTrans::UtmCoordinate &utm_or = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 12000")); - const LtsCoordTrans::LtsCoordinate lts_or(utm_or.geographic(), 0); - double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_or); - - QVERIFY2(qFuzzyCompare(angle, 26.565051177056659526), "angleUtm2D 1 failed"); - QVERIFY2((angle-26.5651 < 0.001), "angleUtm2D 1 failed"); - } -} - -void UnitTestCoordinate::test_coordinate_cont() -{ - std::vector stdVec; - stdVec.push_back(Core::GeoPosition(0.1, 0.11, 1)); - stdVec.push_back(Core::GeoPosition(0.2, 0.22, 2)); - stdVec.push_back(Core::GeoPosition(0.3, 0.33, 3)); - - const QVector &newCont = LtsCoordTrans::convert>(stdVec); - - NOTICE_LOG(m_logger) << " newCont.size() = " << newCont.size(); - QVERIFY2(stdVec.size() == static_cast(newCont.size()), "convert failed"); -} - -using namespace LtsCoordTrans; -class ParticpantPositionStore -{ -public: - ParticpantPositionStore() = default; - ~ParticpantPositionStore() = default; - - void setGeoPosition(const Core::GeoPosition &newGeoPos) - { - m_ltsCoord = LtsCoordinate(newGeoPos); - m_ltsCoord_NoImplicitCopy.updateGeoPosition(newGeoPos); - } - - - - - - LtsCoordinate getLtsCoord_copy() - { - return m_ltsCoord; - } - - - - const LtsCoordinateNoImplicitCopy& getLtsCoord_noCopy() - { - return m_ltsCoord_NoImplicitCopy; - } - - - - - - - - - - LtsCoordinate m_ltsCoord; - LtsCoordinateNoImplicitCopy m_ltsCoord_NoImplicitCopy; -}; - - -void UnitTestCoordinate::test_coordinate_avoid_copy() -{ - LtsCoordTrans::LtsCoordinate::resetConversionCounter(); - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 0, "getConversionCounter failed"); - - ParticpantPositionStore store; - - - - - const UtmCoordinate &utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000")); - const LtsCoordinate ltsIn(utmIn.geographic(), 11); - - store.getLtsCoord_noCopy().updateGeoPosition(ltsIn.geoPosition()); - - - - - - - const LtsCoordinateNoImplicitCopy &justReference = store.getLtsCoord_noCopy(); - NOTICE_LOG(m_logger) << " justReference = " << justReference.mgrs(); - - const LtsCoordTrans::UtmCoordinate &convertedToUtm = justReference.utmCoordinate(); - NOTICE_LOG(m_logger) << " convertedToUtm = " << convertedToUtm; - QVERIFY2(qFuzzyCompare(convertedToUtm.x, 670000.5 ), "convert failed"); - QVERIFY2(qFuzzyCompare(convertedToUtm.y, 5810000.5), "convert failed"); - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed"); - - - - - - const LtsCoordTrans::UtmCoordinate &convertedToUtmSecondTry = justReference.utmCoordinate(); - Q_UNUSED(convertedToUtmSecondTry) - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed"); - - - std::vector myVec; - myVec.push_back(justReference); - - - const LtsCoordTrans::LtsCoordinateNoImplicitCopy &retrievedUtmRef = myVec.front(); - NOTICE_LOG(m_logger) << " retrievedUtm = " << retrievedUtmRef.mgrs(); - - - QVERIFY2(justReference.mgrs() == retrievedUtmRef.mgrs(), "getConversionCounter failed"); - - - - - - QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed"); - - - - - -} - - - - -template -void removeItem(T& map, int key) { map.erase(key); } - - -template <> -void removeItem(QMap& map, int key) { map.remove(key); } - - -template <> -void removeItem(QHash& map, int key) { map.remove(key); } - - -template -void test_references(T& map) -{ - for (int i=2; i<10; ++i) - map[i] = i; - - const int &ref2 = map[2]; - const int &ref3 = map[3]; - const int &ref4 = map[4]; - const int &ref5 = map[5]; - const int &ref6 = map[6]; - const int &ref7 = map[7]; - const int &ref8 = map[8]; - - QVERIFY2(ref2 == 2, "ref failed"); - QVERIFY2(ref3 == 3, "ref failed"); - QVERIFY2(ref4 == 4, "ref failed"); - QVERIFY2(ref5 == 5, "ref failed"); - QVERIFY2(ref6 == 6, "ref failed"); - QVERIFY2(ref7 == 7, "ref failed"); - QVERIFY2(ref8 == 8, "ref failed"); - - - removeItem(map, 1); - removeItem(map, 3); - removeItem(map, 5); - - { - QVERIFY2(ref2 == 2, "ref failed"); - - QVERIFY2(ref4 == 4, "ref failed"); - - QVERIFY2(ref6 == 6, "ref failed"); - QVERIFY2(ref7 == 7, "ref failed"); - QVERIFY2(ref8 == 8, "ref failed"); - } - - - map[0] = 0; - map[1] = 1; - const int &ref0 = map[0]; - const int &ref1 = map[1]; - - { - QVERIFY2(ref0 == 0, "ref failed"); - QVERIFY2(ref1 == 1, "ref failed"); - - QVERIFY2(ref2 == 2, "ref failed"); - - QVERIFY2(ref4 == 4, "ref failed"); - - QVERIFY2(ref6 == 6, "ref failed"); - QVERIFY2(ref7 == 7, "ref failed"); - QVERIFY2(ref8 == 8, "ref failed"); - } - -} - -void UnitTestCoordinate::test_container_references() -{ - - - - - - - - - - { - std::unordered_map std_unordered_map; - test_references(std_unordered_map); - } - { - std::map std_map; - test_references(std_map); - } - { - QMap Qmap; - test_references(Qmap); - } - { - QHash Qhash; - test_references(Qhash); - } -} - - - - - -void UnitTestCoordinate::test_coordVector() -{ - std::vector geoVec; - - { - - QPointF utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic(); - geoVec.push_back( Core::GeoPosition(utmPnt.x(), utmPnt.y(), 1.1) ); - - utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic(1, 0); - geoVec.push_back( Core::GeoPosition(utmPnt.x(), utmPnt.y(), 2.2) ); - - utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic(2, 0); - geoVec.push_back( Core::GeoPosition(utmPnt.x(), utmPnt.y(), 3.3) ); - } - - const LtsCoordVector coordVec(geoVec); - QVERIFY2(coordVec.size() == 3, "size failed"); - - int cnt = LtsCoordTrans::getConversionCounter_UTMUPS_Forward(); - - { - const std::vector &utmVec = coordVec.toStdUtmCoord(32); - - - - - - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed"); - QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 3, "getConversionCounterUTMUPS_Forward failed"); - } - { - const std::vector &utmVec = coordVec.toStdUtmCoord(32); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed"); - QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 3, "getConversionCounterUTMUPS_Forward failed"); - } - - { - const std::vector &utmVec = coordVec.toStdUtmCoord(31); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - { - const std::vector &utmVec = coordVec.toStdUtmCoord(31); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - - - - - - { - const QVector &utmVec = coordVec.toQVectorUtmCoord(32); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed"); - QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - { - const QVector &utmVec = coordVec.toQVectorUtmCoord(32); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed"); - QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - - - - - - { - const QVector &utmVec = coordVec.toQVectorQPointF(32); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - { - const QVector &utmVec = coordVec.toQVectorUtmCoord(32); - QVERIFY2(utmVec.size() == 3, "size failed"); - QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6, "getConversionCounterUTMUPS_Forward failed"); - } - -} - - - - - -int main(int argc, char *argv[]) -{ - NGLCoreApplication app(argc, argv); - app.createLogger("UnitTestCoordinate", "log4cxxPositionUnitTest.props"); - - UnitTestCoordinate tc; - - int ret = QTest::qExec(&tc, argc, argv); - - - if (s_eleMgr) - { - s_eleMgr->cleanupAll(); - } - - return ret; -} - - diff --git a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h b/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h deleted file mode 100755 index 033a25221..000000000 --- a/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h +++ /dev/null @@ -1,80 +0,0 @@ - - - -#pragma once - -#include - -#include - -#include -#include -#include - - -using LOGGER_T = NGL::INGLLogger&; - - - - -class UnitTestCoordinate : public QObject -{ - Q_OBJECT - -public: - UnitTestCoordinate(); - - -private Q_SLOTS: - - - - - - - void initTestCase(); - - void init(); - - - - void test_NaN(); - - void test_coordinate_simple1(); - void test_coordinate_simple2(); - void test_coordinate_simple3(); - void test_coordinate_simple4(); - - void test_coordinate_fromUtm(); - void test_coordinate_fromUtm2(); - - void test_coordinate_elevationAt(); - void test_coordinate_elevationAt_isCovered(); - - void test_coordinate_elevation_data(); - void test_coordinate_elevation(); - - void test_coordinate_mgrs(); - - void test_coordinate_distance(); - void test_coordinate_distanceDiffZones(); - - void test_squareInDifferentZone(); - - void test_coordinate_angle(); - - void test_coordinate_cont(); - - void test_coordinate_avoid_copy(); - - - - void test_container_references(); - void test_coordVector(); - - - - -private: - LOGGER_T m_logger; -}; diff --git a/Lts/Common/LtsCoordinate/lts_coord_vector.cpp b/Lts/Common/LtsCoordinate/lts_coord_vector.cpp deleted file mode 100755 index f386c915d..000000000 --- a/Lts/Common/LtsCoordinate/lts_coord_vector.cpp +++ /dev/null @@ -1,90 +0,0 @@ - - - - -#include "lts_coord_vector.h" - -namespace LtsCoordTrans -{ - -LtsCoordVector::LtsCoordVector(const std::vector &geo_coordinates) - : m_geo_coordinates(geo_coordinates) -{ - -} - -const std::vector &LtsCoordVector::toStdUtmCoord(int zone) const -{ - convertGeoToStdUtm(zone); - return m_utmMapStd[zone]; -} - -const QVector &LtsCoordVector::toQVectorUtmCoord(int zone) const -{ - convertGeoToQVectorUtm(zone); - return m_utmMapQVector[zone]; -} - -const QVector &LtsCoordVector::toQVectorQPointF(int zone) const -{ - convertGeoToQVectorQPointF(zone); - return m_utmMapQVectorQPointF[zone]; -} - -void LtsCoordVector::convertGeoToStdUtm(int zone) const -{ - const auto it = m_utmMapStd.find(zone); - if (it != m_utmMapStd.end()) - return; - convertGeoToUtmInternal(zone, m_utmMapStd[zone]); -} - -void LtsCoordVector::convertGeoToQVectorUtm(int zone) const -{ - const auto it = m_utmMapQVector.find(zone); - if (it != m_utmMapQVector.end()) - return; - - - - - - convertGeoToStdUtm(zone); - QVector &utmVectorRef = m_utmMapQVector[zone]; - utmVectorRef.clear(); - utmVectorRef.reserve(m_geo_coordinates.size()); - - for (const auto &utmCoord : m_utmMapStd[zone]) - utmVectorRef.push_back(utmCoord); -} - -void LtsCoordVector::convertGeoToQVectorQPointF(int zone) const -{ - const auto it = m_utmMapQVectorQPointF.find(zone); - if (it != m_utmMapQVectorQPointF.end()) - return; - - convertGeoToStdUtm(zone); - QVector &QVectorQPointFRef = m_utmMapQVectorQPointF[zone]; - QVectorQPointFRef.clear(); - QVectorQPointFRef.reserve(m_geo_coordinates.size()); - - for (const auto &utmCoord : m_utmMapStd[zone]) - QVectorQPointFRef.push_back(QPointF(utmCoord.x, utmCoord.y)); -} - - - - - - - - - - - - - -} - - diff --git a/Lts/Common/LtsCoordinate/lts_coord_vector.h b/Lts/Common/LtsCoordinate/lts_coord_vector.h deleted file mode 100755 index a16e12d5a..000000000 --- a/Lts/Common/LtsCoordinate/lts_coord_vector.h +++ /dev/null @@ -1,81 +0,0 @@ - - - -#pragma once - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - - -namespace LtsCoordTrans -{ - - - -class LTSCOORDINATE_EXPORT LtsCoordVector -{ -public: - - - - explicit LtsCoordVector(const std::vector &geo_coordinates); - - - - int size() const { return m_geo_coordinates.size() ; } - const std::vector &geoPosition() const { return m_geo_coordinates; } - - - - - const std::vector &toStdUtmCoord(int zone) const; - const QVector &toQVectorUtmCoord(int zone) const; - const QVector &toQVectorQPointF(int zone) const; - - - -protected: - void convertGeoToStdUtm(int zone) const; - void convertGeoToQVectorUtm(int zone) const; - void convertGeoToQVectorQPointF(int zone) const; - - template - void convertGeoToUtmInternal(int zone, T &utmVectorRef) const - { - - utmVectorRef.clear(); - utmVectorRef.reserve(m_geo_coordinates.size()); - - for (const auto &geoCoord : qAsConst(m_geo_coordinates)) - utmVectorRef.push_back(LtsCoordTrans::UtmCoordinate::fromGeographic(geoCoord.lon(), geoCoord.lat(), zone)); - } - -protected: - std::vector m_geo_coordinates; - - - - - mutable std::unordered_map> m_utmMapStd; - mutable std::unordered_map> m_utmMapQVector; - mutable std::unordered_map> m_utmMapQVectorQPointF; -}; - - - - - - - -} - diff --git a/Lts/Common/LtsCoordinate/lts_coordinate.cpp b/Lts/Common/LtsCoordinate/lts_coordinate.cpp deleted file mode 100755 index 5074715bf..000000000 --- a/Lts/Common/LtsCoordinate/lts_coordinate.cpp +++ /dev/null @@ -1,178 +0,0 @@ - - - - -#include "lts_coordinate.h" -#include - -namespace LtsCoordTrans -{ - -static long s_conversion_counter = 0; - - -std::function LtsCoordinate::m_getElevationCB; - - - - -void LtsCoordinate::setElevationCallback(const elevationCbFunc &elevationCB) -{ - m_getElevationCB = elevationCB; -} - -bool LtsCoordinate::hasElevationCallback() -{ - return m_getElevationCB != nullptr; -} - -double LtsCoordinate::elevationAt(double defaultElevation, bool *bIsCovered) const -{ - - - - if (!bIsCovered) - if (!std::isnan(m_elevation)) - return m_elevation; - if (m_getElevationCB) - m_elevation = m_getElevationCB(m_geo.lon(), m_geo.lat(), defaultElevation, bIsCovered); - return m_elevation; -} - -void LtsCoordinate::setZValueAccordingElevation(double defaultElevation) -{ - m_geo.z(elevationAt(defaultElevation)); -} - -double LtsCoordinate::elevationAt(const LtsCoordinate &coordinate, double defaultElevation, bool *bIsCovered) -{ - return coordinate.elevationAt(defaultElevation, bIsCovered); -} - -void LtsCoordinate::replaceZValueByElevationIfValue(double zValueToBeReplaced, double defaultElevation) -{ - - - - - - if ((std::isnan(zValueToBeReplaced) && std::isnan(m_geo.z())) || - qFuzzyCompare(m_geo.z(), zValueToBeReplaced)) - setZValueAccordingElevation(defaultElevation); -} - -double LtsCoordinate::angleDegreeUtm2D(const LtsCoordinate &coord1, const LtsCoordinate &coord2) -{ - const LtsCoordTrans::UtmCoordinate &utm1 = coord1.utmCoordinate(); - const LtsCoordTrans::UtmCoordinate &utm2 = coord2.utmCoordinateDiffZone(utm1.zone); - - const QPointF& offsetUtm = utm2.toPoint() - utm1.toPoint(); - QLineF poly(QPointF(), offsetUtm); - const double angle = poly.angle(); - - - - - - - - - - - - - - - - - - - - - return fmod(angle+90.0, 360); -} - -const UtmCoordinate &LtsCoordinate::utmCoordinateInternal(int zone) const -{ - if (zone == -1) - { - if (!hasUtm()) - convertGeoToUtm(); - return m_utm2D; - } - utmCoordinateDiffZone(zone); - return m_utm2D_differentZone; -} - -void LtsCoordinate::convertGeoToUtm() const -{ - - int zone = -1; - - if (std::isnan(m_geo.lon()) || std::isnan(m_geo.lat())) - { - fprintf(stderr, "Error while fromGeographic, value is NaN\n"); - m_utm2D = LtsCoordTrans::UtmCoordinate(); - return; - } - -#ifdef LTS_DEBUG - ++s_conversion_counter; -#endif - - m_utm2D = LtsCoordTrans::UtmCoordinate::fromGeographic(m_geo.lon(), m_geo.lat(), zone); - Q_ASSERT_X(m_utm2D.zone > 0, "LtsCoordinate", "zone has to be set"); -} - -QString LtsCoordinate::mgrs(int prec, bool withSpaces) const -{ - utmCoordinateInternal(); - QString tmp = m_utm2D.mgrs(prec, withSpaces); - return tmp; -} - -QString LtsCoordinate::mgrsWithZ(int prec, bool withSpaces) const -{ - double ele = elevationAt(); - QString tmp = mgrs(prec, withSpaces) + QStringLiteral(" z=") + QString::number(ele, 'f', 2); - return tmp; -} - - -QString LtsCoordinate::toString(bool fullAndExpensive) const -{ - if (fullAndExpensive) - { - utmCoordinateInternal(); - elevationAt(); - } - std::ostringstream stream; - stream << "LtsCoordinate(m_geo=" << m_geo - << ", m_utm2D.x " << m_utm2D.x << ", m_utm2D.y " << m_utm2D.y << ", m_utm2D.zone " << m_utm2D.zone; - if (!qFuzzyIsNull(m_utm2D.x) || !qFuzzyIsNull(m_utm2D.y)) - stream << ", m_utm2D.mgrs '" << m_utm2D.mgrs().toUtf8().constData() << "'"; - stream << ", elevation " << std::fixed << std::setprecision(3) << m_elevation - << ")"; - return QString::fromStdString(stream.str()); -} - -void LtsCoordinate::resetConversionCounter() -{ - s_conversion_counter = 0; -} -long LtsCoordinate::getConversionCounter() -{ - return s_conversion_counter; -} - - - -std::ostream &operator<<(std::ostream &stream, const LtsCoordinate &value) -{ - stream << value.toString().toStdString(); - return stream; -} - -} - - diff --git a/Lts/Common/LtsCoordinate/lts_coordinate.h b/Lts/Common/LtsCoordinate/lts_coordinate.h deleted file mode 100755 index 2f98e4d72..000000000 --- a/Lts/Common/LtsCoordinate/lts_coordinate.h +++ /dev/null @@ -1,367 +0,0 @@ - - - -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include - - -namespace LtsCoordTrans -{ - -using UtmCoordinate2D = LtsCoordTrans::UtmCoordinate; -using QPoint3D = LtsUtils::QPoint3D; - - - - - -class LTSCOORDINATE_EXPORT LtsCoordinate -{ -public: - - - - - - - - explicit LtsCoordinate(double lon = 0, double lat = 0, double z = std::nan("")) - : m_geo(Core::GeoPosition(lon, lat, z)) - {} - - explicit LtsCoordinate(const QPointF &lonLat, double z = std::nan("")) - : m_geo(Core::GeoPosition(lonLat.x(), lonLat.y(), z)) - {} - - explicit LtsCoordinate(const Core::GeoPosition &geoPos) - : m_geo(geoPos) - {} - - explicit LtsCoordinate(const Core::GeoPosition2D &geoPos2D) - : m_geo(geoPos2D.lon(), geoPos2D.lat(), std::nan("")) - {} - - - - LtsCoordinate(const LtsCoordTrans::UtmCoordinate &utm2D, double zValue) = delete; - - static LtsCoordinate fromGeoPosition(double lon, double lat, double z = std::nan("")) - { - return LtsCoordinate(lon, lat, z); - } - - - static LtsCoordinate fromUtmCoordinate(const LtsCoordTrans::UtmCoordinate &utm2D, double zValue) - { - Q_ASSERT_X(utm2D.zone > 0, "LtsCoordinate", "zone has to be set"); - - - - const QPointF &geoPnt = utm2D.geographic(); - - return LtsCoordinate(geoPnt.x(), geoPnt.y(), zValue); - } - - static LtsCoordinate fromUtm(const QPointF &utm2d, int zone, double zValue) - { - const LtsCoordTrans::UtmCoordinate utmCoord(zone, utm2d.x(), utm2d.y()); - const QPointF &geoPnt = utmCoord.geographic(); - return LtsCoordinate(geoPnt.x(), geoPnt.y(), zValue); - } - - - - - - - bool isValid() const { return hasGeo() || hasUtm(); } - bool hasGeo() const { return !(valueIsNullOrNan(m_geo.lon()) || - valueIsNullOrNan(m_geo.lat())); } - bool hasUtm() const { return m_utm2D.isValid(); } - - static bool valueIsNullOrNan(double value) { return qFuzzyIsNull(value) || std::isnan(value); } - - QString mgrs(int prec = 5, bool withSpaces = true) const; - QString mgrsWithZ(int prec = 5, bool withSpaces = true) const; - - QString toString(bool fullAndExpensive = false) const; - - - - static void resetConversionCounter(); - static long getConversionCounter(); - - - - - - const Core::GeoPosition &geoPosition() const - { - return m_geo; - } - const Core::GeoPosition2D &geoPosition2D() const - { - return m_geo; - } - - const LtsCoordTrans::UtmCoordinate &utmCoordinate() const - { - if (!hasUtm()) - convertGeoToUtm(); - return m_utm2D; - } - - - double &rz() { return m_geo.z(); } - double z() const { return m_geo.z(); } - void setZ(double value) { m_geo.z(value); } - - - LtsCoordTrans::UtmCoordinate utmCoordinateDiffZone(int zone) const - { - - - m_utm2D_differentZone = LtsCoordTrans::UtmCoordinate::fromGeographic(m_geo.lon(), m_geo.lat(), zone); - return m_utm2D_differentZone; - } - - - - - - template - T toUtm(int zone = -1) const - { - T obj; - helperToUtm(obj, zone); - return obj; - - - - } - - QPointF toUtmPointF() const { return toUtm(); } - QPoint3D toUtmPoint3D() const { return toUtm(); } - - QPointF toGeoPointF() const { return QPointF (m_geo.lon(), m_geo.lat()); } - QPoint3D toGeoPoint3D() const { return QPoint3D(m_geo.lon(), m_geo.lat(), m_geo.z() ); } - - - UtmCoordinate3D toUtmCoordinate3D() const - { - return UtmCoordinate3D(utmCoordinate(), m_geo.z()); - } - - - - - - using elevationCbFunc = std::function; - - - - - static void setElevationCallback(const elevationCbFunc &elevationCB); - static bool hasElevationCallback(); - - - - - double elevationAt(double defaultElevation = 0.0, bool *bIsCovered = nullptr) const; - void setZValueAccordingElevation(double defaultElevation = 0.0); - - static double elevationAt(const LtsCoordinate &coordinate, double defaultElevation = 0.0, bool *bIsCovered = nullptr); - - - - - - - - - - - - - - void replaceZValueByElevationIfValue(double zValueToBeReplaced = std::nan(""), double defaultElevation = 0.0); - - - - - - - - - double heightOverElevation() const { return m_geo.z() - elevationAt(); } - - - double distanceUtm2D(const LtsCoordinate &other) const - { - - - const LtsCoordTrans::UtmCoordinate &thisUtm2D = utmCoordinate(); - const LtsCoordTrans::UtmCoordinate &otherUtm2D = other.utmCoordinateDiffZone(m_utm2D.zone); - return LtsCoordTrans::distance(thisUtm2D, otherUtm2D); - } - - double distanceUtm3D(const LtsCoordinate &other) const - { - - - const QPoint3D &thisUtm3D = toUtm(); - const QPoint3D &otherUtm3D = other.toUtm(m_utm2D.zone); - const double dist3D = (thisUtm3D - otherUtm3D).length(); - - Q_ASSERT_X(!std::isnan(dist3D), "LtsCoordinate", "ERROR, distance3D is NaN"); - - return dist3D; - } - - - - - - - - - - static double angleDegreeUtm2D(const LtsCoordinate &coord1, const LtsCoordinate &coord2); - - - - - -protected: - - void clearGeo() const { clearGeoLonLat(); m_geo.z(std::nan("")); } - void clearGeoLonLat() const { m_geo.lon(0); m_geo.lat(0); } - void clearUtm() const { m_utm2D = m_utm2D_differentZone = UtmCoordinate2D(); } - void clearElevation() const { m_elevation = std::nan(""); } - void clear() const { clearGeo(); clearUtm(); clearElevation(); } - - const LtsCoordTrans::UtmCoordinate &utmCoordinateInternal(int zone = -1) const; - void convertGeoToUtm() const; - -protected: - - void helperToUtm(QPointF &pntRef, int zone = -1) const - { - const auto &utmTemp = utmCoordinateInternal(zone); - pntRef.rx() = utmTemp.x; - pntRef.ry() = utmTemp.y; - } - void helperToUtm(QPoint3D &pntRef, int zone = -1) const - { - const auto &utmTemp = utmCoordinateInternal(zone); - pntRef.rx() = utmTemp.x; - pntRef.ry() = utmTemp.y; - pntRef.rz() = m_geo.z(); - } - -protected: - mutable Core::GeoPosition m_geo; - mutable UtmCoordinate2D m_utm2D; - mutable UtmCoordinate2D m_utm2D_differentZone; - mutable double m_elevation{std::nan("")}; - - static elevationCbFunc m_getElevationCB; -}; - - - - -class LTSCOORDINATE_EXPORT LtsCoordinateNoImplicitCopy : public LtsCoordinate -{ -public: - using BaseClass = LtsCoordinate; - - - - LtsCoordinateNoImplicitCopy() = default; - explicit LtsCoordinateNoImplicitCopy (const LtsCoordinateNoImplicitCopy &) = default; - - - - - - - - - ~LtsCoordinateNoImplicitCopy() = default; - - - - - - - - explicit LtsCoordinateNoImplicitCopy(const QPointF &lonLat, double z = std::nan("")) - : BaseClass(Core::GeoPosition(lonLat.x(), lonLat.y(), z)) - {} - - explicit LtsCoordinateNoImplicitCopy(const Core::GeoPosition &geoPos) - : BaseClass(geoPos) - {} - - explicit LtsCoordinateNoImplicitCopy(const Core::GeoPosition2D &geoPos2D) - : BaseClass(geoPos2D) - {} - - - - - void updateGeoPosition(const Core::GeoPosition &geoPos) const - { - clear(); - m_geo = geoPos; - } -}; - - - - - - - - - - - - - - - - - - -template -T_OUT convert(const T_IN& contIn) -{ - T_OUT out; - out.reserve(contIn.size()); - for (const Core::GeoPosition &posGeo : qAsConst(contIn)) - out.push_back(LtsCoordinate(posGeo)); - return out; -} - - - -LTSCOORDINATE_EXPORT std::ostream &operator<< (std::ostream &stream, - const LtsCoordinate &value); - - -} - diff --git a/Lts/Common/LtsElevation/CMakeLists.txt b/Lts/Common/LtsElevation/CMakeLists.txt deleted file mode 100755 index 73eaec362..000000000 --- a/Lts/Common/LtsElevation/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -NGL_ADD_LIBRARY2(WS SV HLA LtsElevation SHARED - lts_elevation_manager.cpp - lts_elevation_manager.h - logger.cpp - logger.h -) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsElevation - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsElevation - PRIVATE - NGLLogger -## LtsSessionDataDds - AsmDataPluginInterface - ExmDataPluginInterface - situationMapCommon - situationMapElevationMap - PUBLIC - LtsUtilsCoreHeaderOnly - Qt5::Core -) - -ADD_SUBDIRECTORY(UnitTest_Elevation) diff --git a/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt b/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt deleted file mode 100755 index 60602e3d8..000000000 --- a/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# -# @RDE_HEADER@ -# $Id$ -# $HeadURL$ -# $Author$ -# - -SET(UnitTestSources - elevation_unit_test.cpp - elevation_unit_test.h -) - -IF(BUILD_TESTING AND TARGET LtsElevation) -ADD_EXECUTABLE(commenUnitTestElevation ${LinkedSources} ${UnitTestSources} ) - -TARGET_LINK_LIBRARIES(commenUnitTestElevation - NGLLogger - nglcoreapplication - LtsElevation - LtsCoordTrans - situationMapCommon - situationMapElevationMap - Qt5::Core - Qt5::Gui - Qt5::Test) - -ADD_TEST(NAME commenUnitTestElevation COMMAND $) - -ENDIF() diff --git a/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp b/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp deleted file mode 100755 index f4c67e4c3..000000000 --- a/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp +++ /dev/null @@ -1,230 +0,0 @@ - - - -#include "elevation_unit_test.h" - -#include -#include - -#include -#include -#include - - -Q_DECLARE_METATYPE(LtsUtils::QPoint3D); -Q_DECLARE_METATYPE(LtsElevation::LineOfSight); - -#include - -static const int s_id{4711}; -static LtsElevation::LtsElevationManager *s_eleMgr{nullptr}; - - - - - - - -class MyLtsElevationManagerBootstrap : public LtsElevation::LtsElevationManager -{ - using BaseClass = LtsElevation::LtsElevationManager; - -public: - static LtsElevationManager *createInstanceUnitTest(int id, const QStringList &filePaths_rough_to_fine) - { - return BaseClass::createInstanceUnitTest(id, filePaths_rough_to_fine); - } - -}; - - - - -UnitTestElevation::UnitTestElevation() - : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTestElevation")) -{} - - -void UnitTestElevation::initTestCase() -{ - - - QString filePath(QStringLiteral("/usr/share/arcgis/resources/elevation/Altmark_dt1.tif")); - - s_eleMgr = MyLtsElevationManagerBootstrap::createInstanceUnitTest(s_id, {filePath}); - - const bool bIsValid = s_eleMgr->isValid(); - std::cerr << "myBootstrapMgr->isValid() 1 = " << bIsValid << std::endl; - - - { - s_eleMgr->waitForInitialization(); - const bool bIsValid = s_eleMgr->isValid(); - std::cerr << "myBootstrapMgr->isValid() 2 = " << bIsValid << std::endl; - } -} - -void UnitTestElevation::init() -{ - QVERIFY2(s_eleMgr , "static LtsElevationManager does not exist"); -} - - - - -void UnitTestElevation::test_elevation_data() -{ - QTest::addColumn("lonLat"); - QTest::addColumn("elevationRef"); - - - QTest::newRow("elevation 1 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 70000 10000")).geographic() << 75.0; - QTest::newRow("elevation 2 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 72000 13000")).geographic() << 80.0; - - - - - QTest::newRow("elevation 3 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 76925 18625")).geographic() << 92.0; -} - -void UnitTestElevation::test_elevation() -{ - QFETCH(QPointF, lonLat); - QFETCH(double, elevationRef); - - double elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y()); - - QVERIFY2(qFuzzyCompare(elevationRef, elevation), " got wrong elevation result"); -} - -void UnitTestElevation::test_elevation_isCovered() -{ - const QPointF &lonLat1 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 70000 10000")).geographic(); - const QPointF &lonLat2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 60000 10000")).geographic(); - - const double defaultElevationValue = -123; - { - bool bIsCovered = false; - s_eleMgr->elevation(lonLat1.x(), lonLat1.y(), defaultElevationValue, &bIsCovered); - QVERIFY2(bIsCovered, " error on bIsCovered"); - } - { - bool bIsCovered = true; - s_eleMgr->elevation(lonLat2.x(), lonLat2.y(), defaultElevationValue, &bIsCovered); - QVERIFY2(!bIsCovered, " error on bIsCovered"); - } -} - -void UnitTestElevation::test_elevation_defaultValue() -{ - const QPointF &lonLat = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 10000")).geographic(); - - double elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y()); - QVERIFY2(qFuzzyCompare(elevation, std::numeric_limits::min()), " error on defaultValue"); - - { - const double defaultValue = 100; - elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y(), defaultValue); - QVERIFY2(qFuzzyCompare(elevation, defaultValue), " error on defaultValue"); - } - - { - const double defaultValue = std::numeric_limits::max(); - elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y(), defaultValue); - QVERIFY2(qFuzzyCompare(elevation, defaultValue), " error on defaultValue"); - } -} - - - - -void UnitTestElevation::test_lineOfSightCalculation_data() -{ - QTest::addColumn("startGeo2D"); - QTest::addColumn("startObjH"); - QTest::addColumn("endGeo2D"); - QTest::addColumn("endObjH"); - QTest::addColumn("expectedResult"); - - - - - QTest::newRow("losDataset 1 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 10000")).geographic() << 1.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 60000")).geographic() << 1.0 - << LtsElevation::LineOfSight::LOS_ERROR; - - - QTest::newRow("losDataset 2 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 1.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 1.0 - << LtsElevation::LineOfSight::LOS_INVISIBLE; - - - QTest::newRow("losDataset 3 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 20.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 20.0 - << LtsElevation::LineOfSight::LOS_VISIBLE; - - - QTest::newRow("losDataset 4 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << -1.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 1.0 - << LtsElevation::LineOfSight::LOS_ERROR; - QTest::newRow("losDataset 5 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 1.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << -1.0 - << LtsElevation::LineOfSight::LOS_ERROR; - QTest::newRow("losDataset 6 ") << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 0.0 - << LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 0.0 - << LtsElevation::LineOfSight::LOS_INVISIBLE; - -} -void UnitTestElevation::test_lineOfSightCalculation() -{ - QFETCH(QPointF, startGeo2D); - QFETCH(double, startObjH); - QFETCH(QPointF, endGeo2D); - QFETCH(double, endObjH); - QFETCH(LtsElevation::LineOfSight, expectedResult); - - LtsUtils::QPoint3D start3D = LtsUtils::QPoint3D::fromQPointF(startGeo2D); - start3D.rz() = startObjH; - start3D.rz() += s_eleMgr->elevation(start3D.x(), start3D.y()); - - LtsUtils::QPoint3D end3D = LtsUtils::QPoint3D::fromQPointF(endGeo2D); - end3D.rz() = endObjH; - end3D.rz() += s_eleMgr->elevation(end3D.x(), end3D.y()); - - LtsElevation::LineOfSight los = s_eleMgr->lineOfSightCalculation(start3D, end3D); - NOTICE_LOG(m_logger) << "los = " << los; - - { - LtsElevation::LineOfSight los2 = s_eleMgr->lineOfSightCalculation(startGeo2D, startObjH, - endGeo2D, endObjH); - QVERIFY2(los == los2, " got different lineOfSightCalculation result"); - } - - - - QVERIFY2(los == expectedResult, " got lineOfSightCalculation result"); -} - - - - - -int main(int argc, char *argv[]) -{ - NGLCoreApplication app(argc, argv); - app.createLogger("UnitTestElevation", "log4cxxElevationUnitTest.props"); - - UnitTestElevation tc; - - int ret = QTest::qExec(&tc, argc, argv); - - - if (s_eleMgr) - { - s_eleMgr->cleanupAll(); - } - - return ret; -} - - diff --git a/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h b/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h deleted file mode 100755 index 6268babf1..000000000 --- a/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h +++ /dev/null @@ -1,56 +0,0 @@ - - - -#pragma once - -#include - -#include - -#include -#include -#include - - -using LOGGER_T = NGL::INGLLogger&; - - - - -class UnitTestElevation : public QObject -{ - Q_OBJECT - -public: - UnitTestElevation(); - - -private Q_SLOTS: - - - - - - - void initTestCase(); - - void init(); - - - - - void test_elevation_data(); - void test_elevation(); - - void test_elevation_isCovered(); - void test_elevation_defaultValue(); - - void test_lineOfSightCalculation_data(); - void test_lineOfSightCalculation(); - - - - -private: - LOGGER_T m_logger; -}; diff --git a/Lts/Common/LtsElevation/logger.cpp b/Lts/Common/LtsElevation/logger.cpp deleted file mode 100755 index c5b10f2fc..000000000 --- a/Lts/Common/LtsElevation/logger.cpp +++ /dev/null @@ -1,32 +0,0 @@ - - - -#include "logger.h" - -#include - -namespace LtsElevation -{ - -LOGGER_T getLogger(const char *name) -{ - static const char ns[] = "Lts.Elevation."; - std::string loggerName; - const size_t len = strlen(name); - loggerName.reserve(sizeof(ns) + len); - loggerName.append(ns, sizeof(ns) - 1); - loggerName.append(name, len); - - try - { - return NGL::NGLLoggerBootstrap::getNGLRootLogger(). - getNGLLogger(loggerName); - } - catch (const std::exception &ex) - { - fprintf(stderr, "Error retrieving logger: %s", ex.what()); - abort(); - } -} - -} diff --git a/Lts/Common/LtsElevation/logger.h b/Lts/Common/LtsElevation/logger.h deleted file mode 100755 index fa2ec1baa..000000000 --- a/Lts/Common/LtsElevation/logger.h +++ /dev/null @@ -1,18 +0,0 @@ - - - - -#pragma once - -#include - -namespace LtsElevation -{ -typedef NGL::INGLLogger &LOGGER_T; - -LOGGER_T getLogger(const char *name); -inline LOGGER_T getLogger(const QString &loggerName) -{ - return getLogger(loggerName.toUtf8().constData()); -} -} diff --git a/Lts/Common/LtsElevation/lts_elevation_manager.cpp b/Lts/Common/LtsElevation/lts_elevation_manager.cpp deleted file mode 100755 index f69fe8d90..000000000 --- a/Lts/Common/LtsElevation/lts_elevation_manager.cpp +++ /dev/null @@ -1,510 +0,0 @@ - - - -#include "lts_elevation_manager.h" -#include -#include - -#include -#include - -#include -#include -#include -#include - - - - -static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_UNDEFINED == (int)LtsElevation::LineOfSight::LOS_UNDEFINED, "LineOfSight mismatch"); -static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_ERROR == (int)LtsElevation::LineOfSight::LOS_ERROR, "LineOfSight mismatch"); -static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_VISIBLE == (int)LtsElevation::LineOfSight::LOS_VISIBLE, "LineOfSight mismatch"); -static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_INVISIBLE == (int)LtsElevation::LineOfSight::LOS_INVISIBLE, "LineOfSight mismatch"); - -namespace LtsElevation -{ -class LtsElevationManager; - - - -using LtsElevatioMgrMapTrainingAreaId = QMap; - - -static LtsElevatioMgrMapTrainingAreaId <sElevatioMgrMapTrainingAreaId() -{ - static LtsElevatioMgrMapTrainingAreaId s_ltsElevatioMgrMapTrainingAreaId; - return s_ltsElevatioMgrMapTrainingAreaId; -} - -static void cleanupLtsElevatioMgrMapTrainingAreaId() -{ - qDeleteAll(ltsElevatioMgrMapTrainingAreaId()); - ltsElevatioMgrMapTrainingAreaId().clear(); -} - -LtsElevatioMgrMapTrainingAreaId &getLtsElevationMgrMapTrainingAreaId() -{ - if (ltsElevatioMgrMapTrainingAreaId().isEmpty()) - qAddPostRoutine(cleanupLtsElevatioMgrMapTrainingAreaId); - - return ltsElevatioMgrMapTrainingAreaId(); -} - -static long s_counter{0}; - - - - -class LtsElevationManager::Private -{ -public: - Private() = default; - ~Private() - { - delete m_elevationManager; - } - std::shared_ptr m_exmDtc; - std::shared_ptr m_asmDtc; - - QStringList m_filePaths_rough_to_fine; - SituationMap::ElevationMapManager *m_elevationManager{nullptr}; -}; - - - - - -LtsElevationManager::LtsElevationManager() - : QObject(nullptr) - , d(new Private) - , m_logger(getLogger("LtsElevationManager")) -{ - ++s_counter; - TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this; - TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this; -} - -LtsElevationManager::~LtsElevationManager() -{ - --s_counter; - TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this; - delete d; - TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this; -} - -LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::createInstance(int exerciseId, - const std::shared_ptr &exmDtc, - const std::shared_ptr &asmDtc) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << LOGFUNCTION << " START, exerciseId=" << exerciseId; - - if (exerciseId < 0) - { - ERROR_LOG(logger) << LOGFUNCTION << " exerciseId invalid, exerciseId=" << exerciseId; - return nullptr; - } - - int trainingAreaId = -1; - - - - - - static QHash s_exIdToTaIdStore; - - const auto exTaIt = s_exIdToTaIdStore.find(exerciseId); - if (exTaIt != s_exIdToTaIdStore.end()) - { - - - trainingAreaId = *exTaIt; - - } else { - - - - if (!exmDtc || !asmDtc) - { - ERROR_LOG(logger) << LOGFUNCTION << " no dtcs for exerciseId=" << exerciseId - << ", asmDtc=" << asmDtc - << ", exmDtc=" << exmDtc; - return nullptr; - } - - - trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, exmDtc); - if (trainingAreaId <= 0) - { - ERROR_LOG(logger) << LOGFUNCTION << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return nullptr; - } - - - s_exIdToTaIdStore[exerciseId] = trainingAreaId; - - } - - - - - LTS_ELEVATION_MANAGER_TYPE temp = createInstanceForTrainingAreaId(trainingAreaId, asmDtc); - if (!temp) - { - ERROR_LOG(logger) << LOGFUNCTION << " Error on createInstanceForTrainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return nullptr; - } - - - temp->initDDS(exmDtc, asmDtc); - - TRACE_LOG(logger) << LOGFUNCTION << " END, exerciseId=" << exerciseId; - return temp; -} - -LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::getInstance(int exerciseId) -{ - return LtsElevationManager::createInstance(exerciseId); -} - -LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::createInstanceForTrainingAreaId(int trainingAreaId, - const std::shared_ptr &asmDtc) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << LOGFUNCTION << " START, trainingAreaId=" << trainingAreaId; - - if (trainingAreaId < 0) - { - ERROR_LOG(logger) << LOGFUNCTION << " trainingAreaId invalid, trainingAreaId=" << trainingAreaId; - return nullptr; - } - - - LtsElevatioMgrMapTrainingAreaId &map = getLtsElevationMgrMapTrainingAreaId(); - QMap::iterator it = map.find(trainingAreaId); - - - if (it != map.end()) - { - DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances: " << map.size(); - return it.value(); - } - - - if (!asmDtc) - { - ERROR_LOG(logger) << LOGFUNCTION << " no dtcs for trainingAreaId=" << trainingAreaId - << ", asmDtc=" << asmDtc; - return nullptr; - } - - LTS_ELEVATION_MANAGER_TYPE temp = new LtsElevationManager(); - - - temp->initDDS(nullptr, asmDtc); - - const QStringList &filenames = temp->getFilenamesForTrainingArea(trainingAreaId); - if (filenames.isEmpty()) - { - ERROR_LOG(logger) << LOGFUNCTION << " no filenames for trainingAreaId=" << trainingAreaId; - delete temp; - return nullptr; - } - - - bool bInitFilenames = temp->init(filenames); - if (!bInitFilenames) - { - ERROR_LOG(logger) << LOGFUNCTION << " bInitFilenames failed for trainingAreaId=" << trainingAreaId; - delete temp; - return nullptr; - } - - map.insert(trainingAreaId, temp); - - DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances now: " << map.size(); - - TRACE_LOG(logger) << LOGFUNCTION << " END, trainingAreaId=" << trainingAreaId; - return map[trainingAreaId]; -} - - - - -LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::createInstanceUnitTest(int id, const QStringList &filePaths_rough_to_fine) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - - LtsElevatioMgrMapTrainingAreaId &map = getLtsElevationMgrMapTrainingAreaId(); - QMap::iterator it = map.find(id); - - - if (it != map.end()) - { - DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances: " << map.size(); - return it.value(); - } - - - LTS_ELEVATION_MANAGER_TYPE temp = new LtsElevationManager(); - - - bool bInitFilenames = temp->init(filePaths_rough_to_fine); - if (!bInitFilenames) - { - ERROR_LOG(logger) << LOGFUNCTION << " bInitFilenames failed for id=" << id; - delete temp; - return nullptr; - } - - map.insert(id, temp); - - DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances now: " << map.size(); - - TRACE_LOG(logger) << LOGFUNCTION << " END, trainingAreaId=" << id; - return map[id]; - - - -} - - -void LtsElevationManager::cleanupAll() -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << LOGFUNCTION << " START"; - cleanupLtsElevatioMgrMapTrainingAreaId(); - TRACE_LOG(logger) << LOGFUNCTION << " END"; -} - -void LtsElevationManager::initDDS(const std::shared_ptr &exmDtc, const std::shared_ptr &asmDtc) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - - d->m_exmDtc = exmDtc; - if (d->m_exmDtc) - d->m_exmDtc->waitForHistoricalDataExercise(1000); - - d->m_asmDtc = asmDtc; - if (d->m_asmDtc) - d->m_asmDtc->waitForHistoricalDataTrainingArea(1000); - - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -QStringList LtsElevationManager::getFilenamesForExercise(int exerciseId) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - - if (!d->m_exmDtc || !d->m_asmDtc) - { - ERROR_LOG(m_logger) << LOGFUNCTION << " no dtcs" - << ", exerciseId=" << exerciseId; - return {}; - } - - - int trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, d->m_exmDtc); - if (trainingAreaId <= 0) - { - ERROR_LOG(m_logger) << LOGFUNCTION << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return {}; - } - - QStringList filePaths_rough_to_fine = getFilenamesForTrainingArea(trainingAreaId); - - TRACE_LOG(m_logger) << LOGFUNCTION << " END, filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(";")); - return filePaths_rough_to_fine; -} - -QStringList LtsElevationManager::getFilenamesForTrainingArea(int trainingAreaId) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - - if (!d->m_asmDtc) - { - ERROR_LOG(m_logger) << LOGFUNCTION << " no dtcs" - << ", trainingAreaId=" << trainingAreaId; - return {}; - } - - - QStringList filePaths_rough_to_fine; - { - d->m_asmDtc->waitForHistoricalDataTrainingArea(1000); - const AssetManagement::TrainingArea &ta = d->m_asmDtc->trainingArea(trainingAreaId); - if (ta.id() != trainingAreaId) - { - ERROR_LOG(m_logger) << LOGFUNCTION << " Error on s_asmDtc->trainingArea" - << ", trainingAreaId=" << trainingAreaId; - return {}; - } - const std::vector &mdVec = ta.elevation(); - filePaths_rough_to_fine.reserve(mdVec.size()); - - for (const AssetManagement::MapDescription &md : qAsConst(mdVec)) - { - - QUrl url(QString::fromStdString(md.url())); - QString filePathAbs = url.toLocalFile(); - if (QDir::isRelativePath(filePathAbs)) - filePathAbs = SituationMap::getResourcePath() + QStringLiteral("/elevation/") + filePathAbs; - - filePaths_rough_to_fine.append(filePathAbs); - } - } - TRACE_LOG(m_logger) << LOGFUNCTION << " END, filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(";")); - return filePaths_rough_to_fine; -} - -bool LtsElevationManager::init(const QStringList &filePaths_rough_to_fine) -{ - delete d->m_elevationManager; - - d->m_elevationManager = new SituationMap::ElevationMapManager(); - int cnt = d->m_elevationManager->init(filePaths_rough_to_fine); - - if (cnt != filePaths_rough_to_fine.size()) - { - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << LOGFUNCTION << " ERROR, not all files could be loaded !!!"; - return false; - } - return true; -} - -int LtsElevationManager::exerciseIdToTrainingAreaId(int exerciseId, const std::shared_ptr &exmDtc) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - - if (!exmDtc) - { - ERROR_LOG(logger) << LOGFUNCTION << " no dtcs" - << ", exerciseId=" << exerciseId; - return {}; - } - - - int trainingAreaId = -1; - { - exmDtc->waitForHistoricalDataExercise(1000); - - const ExerciseManagement::Exercise &exm = exmDtc->exercise(exerciseId); - if (exm.id() != exerciseId) - { - ERROR_LOG(logger) << LOGFUNCTION << " Error on m_exmDtc->exercise" - << ", exerciseId=" << exerciseId; - return {}; - } - trainingAreaId = exm.trainingAreaId(); - - if (trainingAreaId <= 0) - { - ERROR_LOG(logger) << LOGFUNCTION << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return -1; - } - } - return trainingAreaId; -} - -bool LtsElevationManager::waitForInitialization() -{ - if (!d->m_elevationManager) - return false; - return d->m_elevationManager->waitForInitialization(); -} - -bool LtsElevationManager::isValid() const -{ - if (!d->m_elevationManager) - return false; - return d->m_elevationManager->isValid(); -} - -double LtsElevationManager::elevation(double lon_rad, double lat_rad, double defaultValue, bool *bIsCovered) const -{ - if (!d->m_elevationManager) - return defaultValue; - return d->m_elevationManager->elevation(lon_rad, lat_rad, defaultValue, bIsCovered); -} - -LineOfSight LtsElevationManager::lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ, - const LtsUtils::QPoint3D &endPointGeo_absZ) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", startPointGeo_absZ = " << startPointGeo_absZ << ", endPointGeo_absZ = " << endPointGeo_absZ; - - if (!isValid()) - { - ERROR_LOG(m_logger) << LOGFUNCTION << " Error, LtsElevationManager not valid"; - return LineOfSight::LOS_ERROR; - } - - - SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = d->m_elevationManager->lineOfSightCalculation(startPointGeo_absZ,endPointGeo_absZ); - - - LtsElevation::LineOfSight result = static_cast(eleMapMgrResult); - - TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", startPointGeo = " << startPointGeo_absZ << ", endPointGeo = " << endPointGeo_absZ - << " -> LineOfSight " << result; - return result; -} - -LineOfSight LtsElevationManager::lineOfSightCalculation(const QPointF &startPointGeo, double startHat, const QPointF &endPointGeo, double endHat) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START" - << ", startPointGeo = " << startPointGeo << ", startHat = " << startHat - << ", endPointGeo = " << endPointGeo << ", endHat = " << endHat; - - - SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = d->m_elevationManager->lineOfSightCalculation(startPointGeo, startHat, - endPointGeo, endHat); - - - LtsElevation::LineOfSight result = static_cast(eleMapMgrResult); - - TRACE_LOG(m_logger) << LOGFUNCTION << " END" - << ", startPointGeo = " << startPointGeo << ", startHat = " << startHat - << ", endPointGeo = " << endPointGeo << ", endHat = " << endHat - << " -> LineOfSight " << static_cast(eleMapMgrResult); - return result; -} - - -const QString toString(const LineOfSight &value) -{ - switch (value) - { - case LineOfSight::LOS_UNDEFINED: - return QStringLiteral("LOS_UNDEFINED"); - case LineOfSight::LOS_ERROR: - return QStringLiteral("LOS_ERROR"); - case LineOfSight::LOS_VISIBLE: - return QStringLiteral("LOS_VISIBLE"); - case LineOfSight::LOS_INVISIBLE: - return QStringLiteral("LOS_INVISIBLE"); - } - Q_ASSERT(false); - return QStringLiteral("LineOfSightERROR"); -} - -std::ostream &operator<<(std::ostream &stream, const LineOfSight value) -{ - stream << toString(value).toStdString(); - return stream; -} - -} diff --git a/Lts/Common/LtsElevation/lts_elevation_manager.h b/Lts/Common/LtsElevation/lts_elevation_manager.h deleted file mode 100755 index 85c73fbe8..000000000 --- a/Lts/Common/LtsElevation/lts_elevation_manager.h +++ /dev/null @@ -1,129 +0,0 @@ - - - -#pragma once - -#include "logger.h" -#include -#include - - - -#include -#include -#include -#include - -#include -#include - - - -namespace LtsSession -{ -class LtsSessionDataDds; -} - -namespace ExerciseManagement -{ -namespace Interface -{ -class Dtc; -} -} - -namespace AssetManagement -{ -namespace Interface -{ -class Dtc; -} -} - - -namespace LtsElevation -{ - -enum class LineOfSight { LOS_UNDEFINED, - LOS_ERROR, - LOS_VISIBLE, - LOS_INVISIBLE }; - -class LtsElevationManager; -using LTS_ELEVATION_MANAGER_TYPE = QPointer; - - - - -class LTSELEVATION_EXPORT LtsElevationManager : public QObject -{ -public: - - - - - - static LTS_ELEVATION_MANAGER_TYPE createInstance(int exerciseId, - const std::shared_ptr &exmDtc = {}, - const std::shared_ptr &asmDtc = {}); - - static LTS_ELEVATION_MANAGER_TYPE getInstance(int exerciseId); - - - static LTS_ELEVATION_MANAGER_TYPE createInstanceForTrainingAreaId(int trainingAreaId, - const std::shared_ptr &asmDtc = nullptr); - - - - - static void cleanupAll(); - virtual ~LtsElevationManager(); - - - bool waitForInitialization(); - bool isValid() const; - - double elevation(double lon_rad, double lat_rad, double defaultValue = std::numeric_limits::min(), bool *bIsCovered = nullptr) const; - - LineOfSight lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ, - const LtsUtils::QPoint3D &endPointGeo_absZ); - - LineOfSight lineOfSightCalculation(const QPointF &startPointGeo, double startHat, - const QPointF &endPointGeo, double endHat); - -protected: - explicit LtsElevationManager(); - - - - void initDDS(const std::shared_ptr &exmDtc, - const std::shared_ptr &asmDtc); - bool init(const QStringList &filePaths_rough_to_fine); - static int exerciseIdToTrainingAreaId(int exerciseId, const std::shared_ptr &exmDtc); - - - QStringList getFilenamesForExercise(int exerciseId); - QStringList getFilenamesForTrainingArea(int trainingAreaId); - - - - - static LTS_ELEVATION_MANAGER_TYPE createInstanceUnitTest(int id, const QStringList &filePaths_rough_to_fine); - -private: - - - class Private; - - Private *d; - LOGGER_T m_logger; -}; - -LTSELEVATION_EXPORT const QString toString(const LtsElevation::LineOfSight &value); - -LTSELEVATION_EXPORT std::ostream &operator<< (std::ostream &stream, - const LtsElevation::LineOfSight value); - - - -} diff --git a/Lts/Common/LtsLicence/CMakeLists.txt b/Lts/Common/LtsLicence/CMakeLists.txt deleted file mode 100755 index 7b0002bb2..000000000 --- a/Lts/Common/LtsLicence/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by hjl, SLSE, 2016-05-31 -# - -FIND_PACKAGE(OpenSSL REQUIRED) - -SET(ltslicence_SRCS - aes_crypt.cpp - aes_crypt.h - lts_licence.cpp - lts_licence.h -) - -NGL_ADD_LIBRARY2(AAR SV WS LtsLicence SHARED ${ltslicence_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsLicence - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsLicence - PRIVATE - ${OPENSSL_LIBRARIES} - Qt5::Xml - NGLLogger - PUBLIC - Qt5::Core -) - -IF (BUILD_TESTING AND TARGET LtsLicence) - ADD_EXECUTABLE(aes_crypt_test aes_crypt_test.cpp aes_crypt_test.h) - TARGET_LINK_LIBRARIES(aes_crypt_test - LtsLicence - NGLLogger - Qt5::Core - Qt5::Test - ) - ADD_TEST(NAME aes_crypt_test COMMAND $) -ENDIF() diff --git a/Lts/Common/LtsLicence/aes_crypt.cpp b/Lts/Common/LtsLicence/aes_crypt.cpp deleted file mode 100755 index 59e117898..000000000 --- a/Lts/Common/LtsLicence/aes_crypt.cpp +++ /dev/null @@ -1,232 +0,0 @@ - - - -#include "aes_crypt.h" -#include -#include -#include -#include - -namespace LtsLicence -{ -struct AesCryptPrivate -{ - AesCryptPrivate() - : passPhrase(QLatin1String("LTS " LTS_VERSION_MAJOR "." LTS_VERSION_MINOR)) - , logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsLicenceLib.AesCrypt")) - { - qsrand(time(nullptr)); -#if OPENSSL_VERSION_NUMBER < 0x1010000fL - ERR_load_crypto_strings(); -#endif - } - ~AesCryptPrivate() - { -#if OPENSSL_VERSION_NUMBER < 0x1010000fL - ERR_free_strings(); -#endif - } - - QString passPhrase; - NGL::INGLLogger &logger; -}; -Q_GLOBAL_STATIC(AesCryptPrivate, s_Private) - -void setPassPhrase(const QString &passPhrase) -{ - s_Private->passPhrase = passPhrase; -} - -QString passPhrase() -{ - return s_Private->passPhrase; -} - -QByteArray decryptData(const QByteArray &dataToDecrypt, - const QString &pw, - bool *bOk) -{ - if (bOk) - *bOk = false; - - int ret; - int outSize1 = 0; - int outSize2 = 0; - unsigned char key[32]; - - - const QByteArray baPassword = pw.toUtf8(); - const unsigned char *dataPw = reinterpret_cast(baPassword.data()); - int i = EVP_BytesToKey(EVP_aes_256_cbc(), - EVP_sha1(), - nullptr, - dataPw, - baPassword.size(), - 5, - key, - nullptr); - if (i != 32) - { - ERROR_LOG(s_Private->logger) << "Wrong key size - should be 256 bits" ; - return QByteArray(); - } - - const QByteArray baSalt = dataToDecrypt.left(EVP_MAX_IV_LENGTH); - const unsigned char *salt = reinterpret_cast(baSalt.data()); - - const QByteArray bufEncrypted = dataToDecrypt.mid(EVP_MAX_IV_LENGTH); - - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - ret = EVP_DecryptInit_ex(ctx, - EVP_aes_256_cbc(), - nullptr, - key, - salt); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_DecryptInit_ex failed" ; - return QByteArray(); - } - - - QByteArray bufDecrypted; - bufDecrypted.resize(bufEncrypted.size() + EVP_CIPHER_CTX_block_size(ctx)); - const unsigned char *dataIn = reinterpret_cast(bufEncrypted.data()); - unsigned char *dataOut = reinterpret_cast(bufDecrypted.data()); - - ret = EVP_DecryptUpdate(ctx, - dataOut, - &outSize1, - dataIn, - bufEncrypted.size()); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_DecryptUpdate failed" ; - return QByteArray(); - } - - ret = EVP_DecryptFinal_ex(ctx, - dataOut + outSize1, - &outSize2); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_DecryptFinal_ex failed" ; - return QByteArray(); - } - - EVP_CIPHER_CTX_free(ctx); - - bufDecrypted.resize(outSize1 + outSize2); - - if (bOk) - *bOk = true; - return bufDecrypted; -} - - -QByteArray encryptData(const QByteArray &dataToEncrypt, - const QString &pw, - bool *bOk) -{ - if (bOk) - *bOk = false; - - int ret; - int outSize1 = 0; - int outSize2 = 0; - unsigned char key[32]; - QByteArray bufEncrypted; - - - const QByteArray baPassword = pw.toUtf8(); - const unsigned char *dataPw = reinterpret_cast(baPassword.data()); - int i = EVP_BytesToKey(EVP_aes_256_cbc(), - EVP_sha1(), - nullptr, - dataPw, - baPassword.size(), - 5, - key, - nullptr); - if (i != 32) - { - ERROR_LOG(s_Private->logger) << "Wrong key size - should be 256 bits" ; - return QByteArray(); - } - - - QByteArray baSalt(EVP_MAX_IV_LENGTH, 0); - for (int i = 0; i < EVP_MAX_IV_LENGTH; ++i) - baSalt[i] = qrand(); - const unsigned char *salt = reinterpret_cast(baSalt.data()); - - - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - ret = EVP_EncryptInit_ex(ctx, - EVP_aes_256_cbc(), - nullptr, - key, - salt); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_EncryptInit_ex failed" ; - return QByteArray(); - } - - - bufEncrypted = QByteArray::fromRawData(reinterpret_cast(salt), EVP_MAX_IV_LENGTH); - bufEncrypted.resize(bufEncrypted.size() + - dataToEncrypt.size() + - EVP_CIPHER_CTX_block_size(ctx)); - const unsigned char *dataIn = reinterpret_cast(dataToEncrypt.data()); - unsigned char *dataOut = reinterpret_cast(bufEncrypted.data()); - dataOut += baSalt.size(); - ret = EVP_EncryptUpdate(ctx, - dataOut, - &outSize1, - dataIn, - dataToEncrypt.size()); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_EncryptUpdate failed" ; - return QByteArray(); - } - - - ret = EVP_EncryptFinal_ex(ctx, - dataOut + outSize1, - &outSize2); - if (ret == 0) - { - EVP_CIPHER_CTX_free(ctx); - char errbuf[256]; - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - ERROR_LOG(s_Private->logger) << "EVP_EncryptFinal_ex failed" ; - return QByteArray(); - } - - EVP_CIPHER_CTX_free(ctx); - - bufEncrypted.resize(outSize1 + outSize2 + baSalt.size()); - - if (bOk) - *bOk = true; - return bufEncrypted; -} - -} diff --git a/Lts/Common/LtsLicence/aes_crypt.h b/Lts/Common/LtsLicence/aes_crypt.h deleted file mode 100755 index 45fd7dc90..000000000 --- a/Lts/Common/LtsLicence/aes_crypt.h +++ /dev/null @@ -1,22 +0,0 @@ - - - -#pragma once - -#include -#include - -namespace LtsLicence -{ - -LTSLICENCE_EXPORT void setPassPhrase(const QString &passPhrase); -LTSLICENCE_EXPORT QString passPhrase(); - -LTSLICENCE_EXPORT QByteArray decryptData(const QByteArray &dataToDecrypt, - const QString &pw = passPhrase(), - bool *bOk = nullptr); - -LTSLICENCE_EXPORT QByteArray encryptData(const QByteArray &dataToEncrypt, - const QString &pw = passPhrase(), - bool *bOk = nullptr); -} diff --git a/Lts/Common/LtsLicence/aes_crypt_test.cpp b/Lts/Common/LtsLicence/aes_crypt_test.cpp deleted file mode 100755 index 1c68bc31d..000000000 --- a/Lts/Common/LtsLicence/aes_crypt_test.cpp +++ /dev/null @@ -1,54 +0,0 @@ - - - -#include -#include -#include -#include - -#include "aes_crypt.h" -#include "aes_crypt_test.h" - -QTEST_MAIN(aes_crypt_test) - -void aes_crypt_test::initTestCase() -{ - NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false); -} - -void aes_crypt_test::truePassTest() -{ - qDebug("Simple TestCase"); - bool bOk; - const QByteArray content("30.06.2013"); - const QString pw(QStringLiteral("aes_crypt_test")); - const QByteArray enc = LtsLicence::encryptData(content, pw, &bOk); - QVERIFY2(bOk, "Error encrypting data..."); - - QCOMPARE(((content.size() + 15 + EVP_MAX_IV_LENGTH) / 18) * 16, enc.size()); - const QByteArray dec = LtsLicence::decryptData(enc, pw, &bOk); - QVERIFY2(bOk, "Error encrypting data..."); - QCOMPARE(dec, content); - - - const QByteArray bigContent(12345, '1'); - const QByteArray bigEnc = LtsLicence::encryptData(bigContent, pw, &bOk); - QVERIFY2(bOk, "Error encrypting data..."); - - QCOMPARE(((bigContent.size() + 15 + EVP_MAX_IV_LENGTH) / 16) * 16, bigEnc.size()); -} - -void aes_crypt_test::falsePassTest() -{ - qDebug("False Password TestCase"); - bool bOk; - const QByteArray content("30.06.2013"); - const QString pw(QStringLiteral("aes_crypt_test")); - const QString pw2(QStringLiteral("aes_crypt_test2")); - const QByteArray enc = LtsLicence::encryptData(content, pw, &bOk); - QVERIFY2(bOk, "Error encrypting data..."); - QVERIFY2(enc.size() != 20, "Error encrypting data..."); - const QByteArray dec = LtsLicence::decryptData(enc, pw2, &bOk); - QVERIFY2(!bOk, "Error encrypting data..."); - QVERIFY2(dec.isEmpty(), "decrypted data is not empty..."); -} diff --git a/Lts/Common/LtsLicence/aes_crypt_test.h b/Lts/Common/LtsLicence/aes_crypt_test.h deleted file mode 100755 index 49234c473..000000000 --- a/Lts/Common/LtsLicence/aes_crypt_test.h +++ /dev/null @@ -1,15 +0,0 @@ - - - -#pragma once - -#include - -class aes_crypt_test : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void initTestCase(); - void truePassTest(); - void falsePassTest(); -}; diff --git a/Lts/Common/LtsLicence/lts_licence.cpp b/Lts/Common/LtsLicence/lts_licence.cpp deleted file mode 100755 index 575eb2031..000000000 --- a/Lts/Common/LtsLicence/lts_licence.cpp +++ /dev/null @@ -1,173 +0,0 @@ - - - - -#include "lts_licence.h" -#include -#include -#include - -namespace LtsLicence -{ -static bool extractLicence(const QDomElement &licenceElement, QVector &licenceVec); -static NGL::INGLLogger &getLogger() -{ - return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsLicence"); -} - -QVector readLicences(const QByteArray &unencryptedXmlData) -{ - QVector licenceVec; - - QDomDocument xmlDoc; - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - if (!xmlDoc.setContent(unencryptedXmlData, &errorMsg, &errorLine, &errorColumn)) - { - ERROR_LOG(getLogger()) << "Error reading xml data, " << "error in line " << errorLine - << ", column " << errorColumn - << ", message: " << errorMsg; - return licenceVec; - } - - INFO_LOG(getLogger()) << "Try to extract licences"; - - QDomElement licenceElement = xmlDoc.documentElement().firstChildElement(QLatin1String("licence")); - - while (!licenceElement.isNull()) - { - if (!extractLicence(licenceElement, licenceVec)) - { - licenceVec.clear(); - return licenceVec; - } - - licenceElement = licenceElement.nextSiblingElement(QLatin1String("licence")); - } - return licenceVec; -} - - -QByteArray writeUnencryptedLicences(const QVector &licences) -{ - QDomDocument xmlDoc; - - QDomProcessingInstruction instr = xmlDoc.createProcessingInstruction(QLatin1String("xml"), - QLatin1String("version='1.0' encoding='UTF-8' standalone='yes'")); - xmlDoc.appendChild(instr); - - QDomElement root = xmlDoc.createElement(QLatin1String("licences")); - xmlDoc.appendChild(root); - - for (int i = 0; i < licences.size(); ++i) - { - QDomElement lic = xmlDoc.createElement(QLatin1String("licence")); - root.appendChild(lic); - - - QDomElement element = xmlDoc.createElement(QLatin1String("name")); - QDomText text = xmlDoc.createTextNode(licences[i].name); - element.appendChild(text); - lic.appendChild(element); - - - element = xmlDoc.createElement(QLatin1String("ignoreMaxCount")); - text = xmlDoc.createTextNode(QString::number(licences[i].ignoreMaxCount)); - element.appendChild(text); - lic.appendChild(element); - - - element = xmlDoc.createElement(QLatin1String("ignoreExpiration")); - text = xmlDoc.createTextNode(QString::number(licences[i].ignoreExpiration)); - element.appendChild(text); - lic.appendChild(element); - - - element = xmlDoc.createElement(QLatin1String("maxCount")); - text = xmlDoc.createTextNode(QString::number(licences[i].maxCount)); - element.appendChild(text); - lic.appendChild(element); - - - const QLatin1String format("dd.MM.yyyy hh:mm:ss"); - const QString qs = licences[i].expiration.toString(format); - - - element = xmlDoc.createElement(QLatin1String("expiration")); - text = xmlDoc.createTextNode(qs); - element.appendChild(text); - lic.appendChild(element); - } - INFO_LOG(getLogger()) << "Generated XML Data...\n" << xmlDoc.toByteArray(); - return xmlDoc.toByteArray(); -} - - -bool extractLicence(const QDomElement &licenceElement, QVector &licenceVec) -{ - Licence licence; - - - QDomElement tag = licenceElement.firstChildElement(QLatin1String("name")); - licence.name = tag.text(); - - - tag = licenceElement.firstChildElement(QLatin1String("ignoreMaxCount")); - licence.ignoreMaxCount = tag.text().toInt(); - - - tag = licenceElement.firstChildElement(QLatin1String("ignoreExpiration")); - licence.ignoreExpiration = tag.text().toInt(); - - - tag = licenceElement.firstChildElement(QLatin1String("maxCount")); - licence.maxCount = tag.text().toInt(); - - - tag = licenceElement.firstChildElement(QLatin1String("expiration")); - - const QString qs(tag.text()); - - - const QLatin1String format("dd.MM.yyyy hh:mm:ss"); - const QDateTime qdt = QDateTime::fromString(qs, format); - - if (!qdt.isValid()) - { - ERROR_LOG(getLogger()) << "Invalid expiration format! " << qs - << " used format: " << format - ; - return false; - } - - licence.expiration = qdt; - INFO_LOG(getLogger()) << "Read licence for:'" << licence.name << "'" - << " IgnoreMaxCount: " << licence.ignoreMaxCount - << " IgnoreExpiration: " << licence.ignoreExpiration - << " MaxCount: " << licence.maxCount - << " Expiration: " << licence.expiration.date().toString() - << " " << licence.expiration.time().toString() - << " RefCount: " << licence.licenceRefCount - ; - - licenceVec.append(licence); - return true; -} - - - -QString Licence::toString() const -{ - return QObject::tr("Name: %1, Count: %2, ExpireDate: %3, Expires: %4, UseMaxCount: %5") - .arg(name) - .arg(maxCount) - .arg(expiration.toString()) - .arg(ignoreExpiration) - .arg(ignoreMaxCount); -} - - - -} diff --git a/Lts/Common/LtsLicence/lts_licence.h b/Lts/Common/LtsLicence/lts_licence.h deleted file mode 100755 index d996756f2..000000000 --- a/Lts/Common/LtsLicence/lts_licence.h +++ /dev/null @@ -1,33 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include - -namespace LtsLicence -{ - -struct LTSLICENCE_EXPORT Licence -{ - QString name; - bool ignoreMaxCount = false; - bool ignoreExpiration = false; - int maxCount = 0; - QDateTime expiration; - int licenceRefCount = 0; - - QString toString() const; -}; - - - -LTSLICENCE_EXPORT QVector readLicences(const QByteArray &unencryptedXmlData); - - - -LTSLICENCE_EXPORT QByteArray writeUnencryptedLicences(const QVector &licences); -} diff --git a/Lts/Common/LtsLogin/CMakeLists.txt b/Lts/Common/LtsLogin/CMakeLists.txt deleted file mode 100755 index d91928f35..000000000 --- a/Lts/Common/LtsLogin/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# -# Library which automatically provides Login/Logoff so that user data (rights for apps, etc.) is available before any app is started -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-10-27 -# - - -# LtsLogin - -set(LtsLogin_SRCS - lts_login.cpp - logout_info_dialog.cpp -) - -NGL_WRAP_UI(LtsLogin_SRCS - logout_info_dialog.ui -) - -NGL_ADD_LIBRARY2(WS LtsLogin SHARED ${LtsLogin_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsLogin - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsLogin - PRIVATE - LtsAppDescriptor - LtsPluginManager - LtsSession - LtsSessionData - LtsWidgets - LtsUtilsSettings - LtsUtilsCoreHeaderOnly - PUBLIC - OpmDataPluginInterface - SrmDataPluginInterface - AsmDataPluginInterface - Qt5::Core -) diff --git a/Lts/Common/LtsLogin/logout_info_dialog.cpp b/Lts/Common/LtsLogin/logout_info_dialog.cpp deleted file mode 100755 index 4861822d4..000000000 --- a/Lts/Common/LtsLogin/logout_info_dialog.cpp +++ /dev/null @@ -1,94 +0,0 @@ - - - -#include "logout_info_dialog.h" -#include "ui_logout_info_dialog.h" - -#include - - -namespace LtsLogin -{ - -LogoutInformationDialog::LogoutInformationDialog(int timeOut, AssetManagement::ShutdownAction shutdownAction, QWidget *parent) - : LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal) - , m_ui(new Ui::LogoutInformationDialog) - , m_timer(new QTimer(this)) - , m_timeOut(timeOut) - , m_secsLeft(timeOut) -{ - m_ui->setupUi(this); - QString actionText; - switch(shutdownAction.underlying()) - { - case AssetManagement::ShutdownAction::NoAction: - case AssetManagement::ShutdownAction::Logout: - actionText = tr("logout"); - break; - case AssetManagement::ShutdownAction::Shutdown: - actionText = tr("shutdown"); - break; - case AssetManagement::ShutdownAction::Reboot: - actionText = tr("reboot"); - break; - } - - setText(tr("A %1 was triggered by a remote workstation.").arg(actionText)); - - connect(m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this, &LogoutInformationDialog::acceptClicked); - connect(m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &LogoutInformationDialog::closeClicked); - connect(m_timer, &QTimer::timeout, this, &LogoutInformationDialog::onTimerTimeout); - - m_timer->setInterval(1000); -} - -LogoutInformationDialog::~LogoutInformationDialog() -{ - delete m_ui; -} - -void LogoutInformationDialog::setText(const QString &text) -{ - m_ui->lblLogoutText->setText(text); -} - -void LogoutInformationDialog::onTimerTimeout() -{ - if (m_secsLeft > 0) - { - m_secsLeft--; - setCloseButtonText(m_secsLeft); - } - else - { - acceptClicked(); - } -} - -void LogoutInformationDialog::acceptClicked() -{ - m_timer->stop(); - accept(); -} - -void LogoutInformationDialog::closeClicked() -{ - m_timer->stop(); - reject(); - -} - -void LogoutInformationDialog::setCloseButtonText(int secsLeft) -{ - m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Close)->setText(tr("Abort [%1]").arg(secsLeft)); -} - -void LogoutInformationDialog::showEvent(QShowEvent *event) -{ - m_secsLeft = m_timeOut; - setCloseButtonText(m_secsLeft); - m_timer->start(); - LtsWidgets::LtsFramelessDialog::showEvent(event); -} - -} diff --git a/Lts/Common/LtsLogin/logout_info_dialog.h b/Lts/Common/LtsLogin/logout_info_dialog.h deleted file mode 100755 index c1938baaf..000000000 --- a/Lts/Common/LtsLogin/logout_info_dialog.h +++ /dev/null @@ -1,43 +0,0 @@ - - - -#pragma once - -#include -#include - -class QTimer; - -namespace Ui { class LogoutInformationDialog; } - -namespace LtsLogin -{ - -class LogoutInformationDialog : public LtsWidgets::LtsFramelessDialog -{ - Q_OBJECT - -public: - LogoutInformationDialog(int timeOut, AssetManagement::ShutdownAction shutdownAction, QWidget *parent = nullptr); - ~LogoutInformationDialog(); - -private slots: - void onTimerTimeout(); - void acceptClicked(); - void closeClicked(); - -private: - void setText(const QString &text); - void setCloseButtonText(int secsLeft); - - Ui::LogoutInformationDialog *m_ui; - QTimer *m_timer; - int m_timeOut; - int m_secsLeft; - - -protected: - void showEvent(QShowEvent *event) override final; -}; - -} diff --git a/Lts/Common/LtsLogin/logout_info_dialog.ui b/Lts/Common/LtsLogin/logout_info_dialog.ui deleted file mode 100755 index dbd122e6e..000000000 --- a/Lts/Common/LtsLogin/logout_info_dialog.ui +++ /dev/null @@ -1,76 +0,0 @@ - - - LogoutInformationDialog - - - - 0 - 0 - 400 - 165 - - - - Form - - - - 2 - - - 2 - - - 2 - - - 2 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - <logout information text> - - - Qt::AutoText - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - - - - - - - - - - - LtsWidgets::LtsDialogButtonBox - QWidget -
LtsWidgets/lts_dialog_button_box.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsLogin/lts_login.cpp b/Lts/Common/LtsLogin/lts_login.cpp deleted file mode 100755 index ac04484f0..000000000 --- a/Lts/Common/LtsLogin/lts_login.cpp +++ /dev/null @@ -1,312 +0,0 @@ - - -#include "lts_login.h" -#include "logout_info_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -namespace LtsLogin -{ -const int logoutShutdownTimeout = 2 * 60; - -LtsLogin::LtsLogin() - : m_loginApp(nullptr) - , m_shutdownAction(AssetManagement::ShutdownAction::NoAction) - , m_isInLogoutDialog(false) -{} - - -LtsLogin::~LtsLogin() -{ - delete m_loginApp; - m_loginApp = nullptr; -} - - -void LtsLogin::login() -{ - LtsAppDescriptor::PluginInfo opmDataPlugin; - opmDataPlugin.iid = QStringLiteral("opm"); - opmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - - LtsAppDescriptor::PluginInfo pdmDataPlugin; - pdmDataPlugin.iid = QStringLiteral("pdm"); - pdmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - - LtsAppDescriptor::PluginInfo asmDataPlugin; - asmDataPlugin.iid = QStringLiteral("asm"); - asmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - - LtsAppDescriptor::PluginInfo srmDataPlugin; - srmDataPlugin.iid = QStringLiteral("srm"); - srmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA; - - LtsAppDescriptor::PluginInfo guiPlugin; - guiPlugin.iid = m_loginPluginIid; - guiPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::GUI; - guiPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::PAYLOAD; - - QVector plugins; - plugins.append(opmDataPlugin); - plugins.append(pdmDataPlugin); - plugins.append(asmDataPlugin); - plugins.append(srmDataPlugin); - plugins.append(guiPlugin); - - LtsAppDescriptor::AppDescription app; - app.active = true; - app.appName = QStringLiteral("Login"); - app.pluginInfos = plugins; - - LtsSession::DdsConnectionParams connParams; - connParams.domainId = LtsSessionData::LtsSessionData::getInstance()->getDomainIdPreset(); - connParams.tcpConnectionUrl = LtsSessionData::LtsSessionData::getInstance()->getTcpConnectionUrl(); - connParams.partitionType = IDL::PartitionType::Global; - connParams.partitionId = 0; - - m_loginApp = new LtsPluginManager::LtsPluginManager(); - bool result = m_loginApp->startApp(app, true, connParams); - if (!result) - { - qWarning() << "no login plugin - probably an offline project"; - emit signalLoginDone(); - return; - } - - - - connect(m_loginApp, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, - this, [this]() - { - disconnect(m_loginApp, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, - this, nullptr); - - LtsSession::LtsSession *session = getAdminSession(); - - if (!session) - { - qCritical() << "I cannot find the Admin Session :( - abort!"; - QMessageBox::critical(nullptr, tr("Error loading login app"), tr("Could not load login app - abort.")); - abort(); - } - - if (session->isInitializationDone()) - QMetaObject::invokeMethod(this, &LtsLogin::slotLoginDone); - else - connect(session, &LtsSession::LtsSession::signalDoneInitializingSession, - this, &LtsLogin::slotLoginDone, Qt::UniqueConnection); - }); -} - - -void LtsLogin::slotLoginDone() -{ - LtsSession::LtsSession *session = getAdminSession(); - - if (session != nullptr) - { - disconnect(session, &LtsSession::LtsSession::signalDoneInitializingSession, - this, &LtsLogin::slotLoginDone); - - if (session->getDtc(m_asmDtc)) - { - m_asmDtc->enableWorkstationLogout(); - connect(m_asmDtc.get(), &AssetManagement::Interface::Dtc::workstationLogoutChanged, - this, &LtsLogin::slotLogoutShutdown); - } - else - qWarning() << "LtsLogin: no asm dtc, no remote logout/shutdown!"; - } - else - { - qCritical() << "LtsLogin: no admin session for login!!"; - QMessageBox::critical(nullptr, tr("Login error"), tr("Could not finalize login - abort.")); - abort(); - } - - emit signalLoginDone(); -} - - -void LtsLogin::showLogoutInformationDialog(AssetManagement::ShutdownAction action) -{ - LogoutInformationDialog *lid = new LogoutInformationDialog(logoutShutdownTimeout, action, nullptr); - connect(lid, &LogoutInformationDialog::accepted, - this, [this, action]() - { - m_shutdownAction = action; - m_isInLogoutDialog = false; - emit signalLogout(); - }); - - connect(lid, &LogoutInformationDialog::rejected, - this, [this]() - { - m_isInLogoutDialog = false; - }); - - lid->show(); -} - - -void LtsLogin::slotLogoutShutdown(const AssetManagement::WorkstationLogout &data, DataModification modType) -{ - if (m_isInLogoutDialog) - return; - - if (m_asmDtc == nullptr || modType != DataModification::eAdded) - return; - - if (!m_asmDtc->isAssetEnabled()) - m_asmDtc->enableAsset(); - for (int id : data.ids()) - { - if (QString::fromStdString(m_asmDtc->asset(id).name()).compare(LtsSession::LtsSession::workstationName(), Qt::CaseInsensitive) == 0) - { - m_isInLogoutDialog = true; - showLogoutInformationDialog(data.shutdownAction()); - break; - } - } -} - - -LtsSession::LtsSession *LtsLogin::getAdminSession() -{ - return m_loginApp->m_sessionManager->getSession(IDL::PartitionType::Global, 0); -} - - -void LtsLogin::logoff() -{ - if (m_loginApp == nullptr) - { - qCritical() << "LtsLogin::logoff: missing login app!"; - return; - } - - - - LtsSession::LtsSession *datSession = getAdminSession(); - - if (!datSession) - { - qCritical() << "LtsLogin::logoff: no admin session for logout!"; - abort(); - } - - int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId(); - int workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId(); - - LtsSessionData::LtsSessionData::getInstance()->clear(); - - std::shared_ptr opmFtc; - if (operatorId == -1 || - !datSession->getFtc(opmFtc) - ) - { - - } - else - { - try - { - - LtsUtils::OperatorSettings *os = LtsUtils::OperatorSettings::getInstance(); - - const QByteArray settings = os->exportSettings(); - - - const auto vec(LtsUtils::convert>(settings)); - os->clear(); - opmFtc->operatorGetSetSettingsRequest(OperatorManagement::OperatorGetSetSettingsRequest(operatorId, vec)); - } - catch (const std::runtime_error &err) - { - qCritical() << "Saving operator settings failed!" << err.what(); - } - - - std::shared_ptr srmIfc; - if (datSession->getForeignDataInterface(srmIfc)) - { - try - { - srmIfc->removeReplayPartitionsWorkstation(workstationId); - } - catch (const std::runtime_error &err) - { - qCritical() << "Removing reply partitions from this workstation failed!" << err.what(); - } - - try - { - srmIfc->removeReplayPartitionsOperator(operatorId); - } - catch (const std::runtime_error &err) - { - qCritical() << "Removing reply partitions from this operator failed!" << err.what(); - } - } - - - try - { - opmFtc->operatorLogoutRequest(OperatorManagement::OperatorLogoutRequest(operatorId)); - } - catch (const std::runtime_error &err) - { - qCritical() << "Logout failed!" << err.what(); - } - - - } - - m_loginApp->deleteLater(); - m_loginApp = nullptr; - - QString cmd; - switch (m_shutdownAction.underlying()) - { - case AssetManagement::ShutdownAction_def::Shutdown: -#ifdef LTS_DEBUG - cmd = QStringLiteral("xeyes"); -#else - cmd = QStringLiteral("systemctl poweroff"); -#endif - break; - case AssetManagement::ShutdownAction_def::Reboot: -#ifdef LTS_DEBUG - cmd = QStringLiteral("kcalc"); -#else - cmd = QStringLiteral("systemctl reboot"); -#endif - break; - case AssetManagement::ShutdownAction_def::Logout: - case AssetManagement::ShutdownAction_def::NoAction: - emit signalLogoffDone(); - break; - } - - if (!cmd.isEmpty()) - { - QProcess::startDetached(cmd); - QCoreApplication::quit(); - } -} - -} diff --git a/Lts/Common/LtsLogin/lts_login.h b/Lts/Common/LtsLogin/lts_login.h deleted file mode 100755 index d22b7e1c9..000000000 --- a/Lts/Common/LtsLogin/lts_login.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - - - -#include -#include -#include -#include - -#include - - -namespace LtsPluginManager -{ -class LtsPluginManager; -} -namespace LtsUtils -{ -class RequestHelper; -} -namespace LtsSession -{ -class LtsSession; -} -namespace DeveloperTools -{ -class DevDialog; -} - -namespace LtsLogin -{ - -class LTSLOGIN_EXPORT LtsLogin : public QObject -{ - Q_OBJECT - -public: - LtsLogin(); - ~LtsLogin(); - - void login(); - void logoff(); - void setLoginPlugin(const QString &loginPluginIid) { m_loginPluginIid = loginPluginIid; } - -private: - friend class DeveloperTools::DevDialog; - - LtsSession::LtsSession *getAdminSession(); - void showLogoutInformationDialog(AssetManagement::ShutdownAction action); - - LtsPluginManager::LtsPluginManager *m_loginApp; - AssetManagement::ShutdownAction m_shutdownAction; - std::shared_ptr m_asmDtc; - QString m_loginPluginIid; - bool m_isInLogoutDialog; - -private slots: - void slotLoginDone(); - void slotLogoutShutdown(const AssetManagement::WorkstationLogout &data, DataModification modType); - -signals: - void signalLogout(); - void signalStopBlock(); - void signalLoginDone(); - void signalLogoffDone(); - -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/CMakeLists.txt b/Lts/Common/LtsMultiMonitor/CMakeLists.txt deleted file mode 100755 index bcb988452..000000000 --- a/Lts/Common/LtsMultiMonitor/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-11-01 -# - - -# LtsMultiMonitor -SET(LtsMultiMonitor_SRCS - lts_multi_monitor_export.h - lts_multi_monitor.cpp - lts_dummy_widget.cpp - lts_screen_container.cpp - lts_multi_monitor_selector_base.cpp - lts_screen_base.cpp - lts_magic_corner.h - lts_magic_corner.cpp - - # normal screen - lts_multi_monitor_selector_normal.cpp - lts_screen_normal.cpp - - # ultra wide screen - lts_multi_monitor_selector_wide.cpp - lts_screen_wide.cpp -) - -NGL_ADD_LIBRARY(WS LtsMultiMonitor SHARED ${LtsMultiMonitor_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsMultiMonitor - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsMultiMonitor - PUBLIC - Qt5::Core - Qt5::Widgets - PRIVATE - Qt5::Gui - LtsWidgets - LtsSessionData - LtsUtilsCore - LtsUtilsGuiDds -) - -NGL_INSTALL_TARGET(LtsMultiMonitor) diff --git a/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp b/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp deleted file mode 100755 index e98341a60..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "lts_dummy_widget.h" - -#include - -#include - -#include -#include - - -namespace LtsMultiMonitor -{ - -FullscreenVideoDummyWidget::FullscreenVideoDummyWidget() -{ - setWindowIcon(LtsUtils::iconFromSvg(QStringLiteral(":/style/mobile_video.svg"))); - setWindowTitle(tr("Fullscreen Video Helper")); - - - - -} - - -void FullscreenVideoDummyWidget::hideVideo() -{ - qWarning() << "FullscreenVideoDummyWidget::hideVideo()"; - emit signalHideVideo(); -} - -void FullscreenVideoDummyWidget::showVideo() -{ - emit signalShowVideo(); -} - - -bool FullscreenVideoDummyWidget::event(QEvent *event) -{ - if (event->type() == QEvent::Type::Show) - assert(false); - return QWidget::event(event); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h b/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h deleted file mode 100755 index a5030a84b..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -#include - - -class QEvent; - - -namespace LtsMultiMonitor -{ - -class LTSMULTIMONITOR_EXPORT SecondaryWidget : public QWidget -{ - Q_OBJECT -public: - SecondaryWidget() = default; - virtual ~SecondaryWidget() = default; -}; - - - -class LTSMULTIMONITOR_EXPORT FullscreenVideoDummyWidget : public SecondaryWidget -{ - Q_OBJECT - -public: - FullscreenVideoDummyWidget(); - - void hideVideo(); - void showVideo(); - -protected: - bool event(QEvent *); - -signals: - void signalWidgetPlaced(const QRect &globalGeometry); - void signalHideVideo(); - void signalShowVideo(); -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp b/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp deleted file mode 100755 index b31212ab8..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "lts_magic_corner.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include - - -#define SMOL 48 -#define SWOLE 100 - - -using namespace std::chrono_literals; - - -namespace LtsMultiMonitor -{ -LtsMagicCorner::LtsMagicCorner(const QRect &neogeo) - : m_neogeo(neogeo) - , m_shrinkTimer(new QTimer(this)) - , m_label(new QLabel(this)) -{ - - - m_shrinkTimer->setInterval(1500ms); - m_shrinkTimer->setSingleShot(true); - - connect(m_shrinkTimer, &QTimer::timeout, - this, &LtsMagicCorner::makeSmol); - - makeSmol(); - - - setWindowFlags(windowFlags() | Qt::FramelessWindowHint); - setMouseTracking(true); - setContentsMargins(0, 0, 0, 0); - setWindowFlag(Qt::WindowType::WindowStaysOnTopHint); - setWindowFlag(Qt::WindowType::X11BypassWindowManagerHint); - activateWindow(); - - - - auto *t = new QTimer(this); - connect(t, &QTimer::timeout, - this, [this]() - { - if (!isVisible()) - return; - - raise(); - - - - }); - t->setInterval(750ms); - t->start(); - - - m_label->setMinimumSize(SWOLE, SWOLE); - m_label->setMaximumSize(SWOLE, SWOLE); - m_label->setGeometry(0, 0, SWOLE, SWOLE); - m_label->setText(QStringLiteral("")); - m_label->setAlignment(Qt::AlignmentFlag::AlignCenter); - - - - - - -} - - -void LtsMagicCorner::setActiveWidgetType(LtsScreenBase::WidgetType widgetType) -{ - m_activeWidgetType = widgetType; - if (widgetType == LtsScreenBase::WidgetType::MainWidget) - m_label->setPixmap(m_mainPixmap); - else - m_label->setPixmap(m_secondaryPixmap); -} - -void LtsMagicCorner::setFreezed(bool freezed) -{ - setVisible(!freezed); -} - - -void LtsMagicCorner::setIcon(const QIcon &icon, LtsScreenBase::WidgetType widgetType) -{ - QPixmap p(LtsUtils::pixmapFromIcon(icon)); - - if (widgetType == LtsScreenBase::WidgetType::MainWidget) - m_mainPixmap = p; - else - m_secondaryPixmap = p; - - if (widgetType != m_activeWidgetType) - m_label->setPixmap(p); - - - - - - - - - - - - -} - - -void LtsMagicCorner::mousePressEvent(QMouseEvent *event) -{ - m_mousePressed = (event->button() == Qt::MouseButton::LeftButton); -} - - -void LtsMagicCorner::mouseReleaseEvent(QMouseEvent *event) -{ - if (!m_mousePressed) - return; - - m_mousePressed = false; - - if (event->button() != Qt::MouseButton::LeftButton) - return; - - m_shrinkTimer->start(); - emit toggle(); -} - - -void LtsMagicCorner::makeSmol() -{ - setAttribute(Qt::WA_TranslucentBackground); - setGeometry(m_neogeo.x(), m_neogeo.y(), SMOL, SMOL); - setMinimumSize(SMOL, SMOL); - setMaximumSize(SMOL, SMOL); - m_label->hide(); -} - - -void LtsMagicCorner::makeSwole() -{ - if (!isVisible()) - return; - - setAttribute(Qt::WA_TranslucentBackground, false); - setAttribute(Qt::WA_NoSystemBackground, false); - setGeometry(m_neogeo.x(), m_neogeo.y(), SWOLE, SWOLE); - setMinimumSize(SWOLE, SWOLE); - setMaximumSize(SWOLE, SWOLE); - m_label->show(); - m_shrinkTimer->start(); - update(); -} - - -void LtsMagicCorner::mouseMoveEvent(QMouseEvent *) -{ - - emit signalMakeAllSwole(); -} - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsMultiMonitor/lts_magic_corner.h b/Lts/Common/LtsMultiMonitor/lts_magic_corner.h deleted file mode 100755 index 8af1b17b5..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_magic_corner.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "lts_screen_base.h" - -#include -#include - - -class QMouseEvent; -class QPaintEvent; -class QLabel; -class QTimer; - - -namespace LtsMultiMonitor -{ -class LtsMagicCorner : public QDialog -{ - Q_OBJECT - -public: - explicit LtsMagicCorner(const QRect &neogeo); - - void setIcon(const QIcon &icon, LtsScreenBase::WidgetType widgetType); - void setActiveWidgetType(LtsScreenBase::WidgetType widgetType); - - - void setFreezed(bool freezed); - -protected: - void mouseMoveEvent(QMouseEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - -signals: - void toggle(); - void signalMakeAllSwole(); - -private slots: - void makeSmol(); - -public slots: - void makeSwole(); - -private: - QRect m_neogeo; - QTimer *m_shrinkTimer; - bool m_mousePressed { false }; - bool m_freezed = false; - QLabel *m_label; - QPixmap m_mainPixmap; - QPixmap m_secondaryPixmap; - LtsScreenBase::WidgetType m_activeWidgetType; -}; -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp b/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp deleted file mode 100755 index 934cdc05d..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp +++ /dev/null @@ -1,696 +0,0 @@ - - - -#include "lts_multi_monitor.h" -#include "lts_multi_monitor_selector_normal.h" -#include "lts_multi_monitor_selector_wide.h" -#include "lts_screen_normal.h" -#include "lts_screen_wide.h" -#include "lts_dummy_widget.h" -#include "lts_screen_container.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef Q_OS_WIN -#include -#endif - - -namespace LtsMultiMonitor -{ -static LtsMultiMonitor *s_ltsMultiMonitor = nullptr; - - -LtsMultiMonitor *LtsMultiMonitor::getInstance() -{ - if (!s_ltsMultiMonitor) - { - s_ltsMultiMonitor = new LtsMultiMonitor; - qAddPostRoutine(&cleanupLtsMultiMonitor); - } - return s_ltsMultiMonitor; -} - -void LtsMultiMonitor::cleanupLtsMultiMonitor() -{ - delete s_ltsMultiMonitor; - s_ltsMultiMonitor = nullptr; -} - - -LtsMultiMonitor::LtsMultiMonitor() - : QObject(nullptr) - , m_mainScreen(nullptr) - , m_active(false) - , m_initialized(false) - , m_skipScreenId(-1) - , m_subSkipScreenId(-1) - - , m_overlapBaseLayouts(false) - , m_debugMargins(false) -{} - - -LtsMultiMonitor::~LtsMultiMonitor() -{ - - - - for (auto i = m_screens.begin(); i != m_screens.end(); ++i) - { - LtsScreenBase *screen = *i; - disconnect(screen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied); - screen->deleteLater(); - } - - - - -} - - -void LtsMultiMonitor::screenDied() -{ - - Q_ASSERT(false); -} - - -void LtsMultiMonitor::init() -{ - - if (LtsSessionData::LtsSessionData::getInstance()->isTablet()) - { - m_overlapBaseLayouts = true; - m_initialized = true; - return; - } - - if (m_initialized || !m_active) - return; - - QList screenList = QGuiApplication::screens(); - const QScreen &mainScreen = *QGuiApplication::primaryScreen(); - - - - - for (auto i = screenList.constBegin(); i != screenList.constEnd(); ++i) - { - const QScreen &screen = **i; - - - bool found = false; - - for (LtsScreenBase *ltsScreen : qAsConst(m_screens)) - { - if (ltsScreen->serialNumber() == screen.name()) - { - found = true; - break; - } - } - - if (found) - continue; - - - int screenId = m_nextScreenId++; - - - QRect screenGeo(screen.geometry()); - - if (screenId == m_skipScreenId) - { - if (m_subSkipScreenId == 0) - { - - QRect newScreenGeo(screenGeo); - - newScreenGeo.moveLeft(screenGeo.x() + (screenGeo.width() / 2)); - newScreenGeo.setWidth(screenGeo.width() / 2); - - screenGeo = newScreenGeo; - } - else if (m_subSkipScreenId == 1) - { - - QRect newScreenGeo(screenGeo); - - newScreenGeo.setWidth(screenGeo.width() / 2); - - screenGeo = newScreenGeo; - } - else - { - - continue; - } - } - - - - bool isUltraWideView = (screenGeo.width() > 3000); - - LtsScreenBase *newScreen; - if (isUltraWideView) - { - newScreen = new LtsScreenWide(screenGeo, screenId, m_debugMargins, screen.name()); - } - else - { - auto *newScreenNormal = new LtsScreenNormal(std::move(screenGeo), screenId, m_debugMargins, screen.name()); - newScreen = newScreenNormal; - - - connect(newScreenNormal, &LtsScreenNormal::signalMakeAllSwole, - this, &LtsMultiMonitor::signalMakeAllSwole); - connect(this, &LtsMultiMonitor::signalMakeAllSwole, - newScreenNormal, &LtsScreenNormal::signalMakeSwole); - } - - connect(newScreen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied); - - if ((&mainScreen) == (&screen)) - m_mainScreen = newScreen; - - m_screens.append(newScreen); - } - if (m_screens.isEmpty()) - { - QMessageBox::critical(nullptr, QString(), tr("No screens found - cannot start application.")); - abort(); - } - - if (m_mainScreen == nullptr) - m_mainScreen = m_screens.first(); - - m_initialized = true; - emit signalScreenCountChanged(screenCount()); -} - - -int LtsMultiMonitor::screenCount() const -{ - return m_screens.count(); -} - - -bool LtsMultiMonitor::cancelAndProcessIfInactive(QWidget *widget) const -{ - if (m_active) - return false; - - widget->activateWindow(); - widget->show(); - - place(widget); - - return true; -} - - -void LtsMultiMonitor::place(QWidget *widget) -{ - - - auto *blw = qobject_cast(widget); - if (blw != nullptr) - emit blw->signalSuccessfullyPlaced(); -} - - -bool LtsMultiMonitor::putScreen(QWidget *widget, int screenId, int screenPart) -{ - Q_ASSERT(widget != nullptr); - if (widget == nullptr) - return false; - - if (cancelAndProcessIfInactive(widget)) - return true; - - init(); - - if (overlapBaseLayouts(widget)) - return true; - - - bool isSecondary = isWidgetSecondary(widget); - qWarning() << "putScreen: isSecondary" << isSecondary; - - for (LtsScreenBase *screen : qAsConst(m_screens)) - { - if (screen->screenId() != screenId) - continue; - - screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget); - - try - { - screen->putWidget(widget, screenPart); - } - catch (...) - { - screen->revertSwitch(); - - return selectScreen(widget); - } - - screen->check(); - - place(widget); - screen->finalizeSwitch(); - return true; - } - - - return selectScreen(widget); -} - -bool LtsMultiMonitor::putScreenAutomatically(QWidget *widget) -{ - if (widget == nullptr) - return false; - - if (cancelAndProcessIfInactive(widget)) - return true; - - init(); - - if (overlapBaseLayouts(widget)) - return true; - - for (LtsScreenBase *screen : qAsConst(m_screens)) - { - if (screen->screenId() == m_skipScreenId || !screen->isEmpty()) - continue; - - screen->switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget); - - try - { - screen->putWidget(widget, 1); - } - catch (...) - { - try - { - screen->putWidget(widget, 2); - } - catch (...) - { - screen->revertSwitch(); - continue; - } - } - - screen->check(); - place(widget); - screen->finalizeSwitch(); - - return true; - } - - return false; -} - - -bool LtsMultiMonitor::isOverlapBaseLayouts() const -{ - return (m_overlapBaseLayouts ); -} - - -void LtsMultiMonitor::freezeMagicCorner(int screenId) -{ - - for (auto *screen : qAsConst(m_screens)) - { - - if (screen->screenId() == screenId) - { - LtsScreenNormal* normalScreen = qobject_cast(screen); - if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget()) - { - normalScreen->hideMagicCorner(true); - if(normalScreen->child()->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget) - normalScreen->child()->toggle(); - } - } - } -} - -void LtsMultiMonitor::unfreezeMagicCorner(int screenId) -{ - - for (auto *screen : qAsConst(m_screens)) - { - - if (screen->screenId() == screenId) - { - LtsScreenNormal* normalScreen = qobject_cast(screen); - if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget()) - normalScreen->hideMagicCorner(false); - } - } -} - - -bool LtsMultiMonitor::overlapBaseLayouts(QWidget *widget) -{ - if (!isOverlapBaseLayouts()) - return false; - - - - - Q_ASSERT(qobject_cast(widget) == nullptr); - if (qobject_cast(widget) != nullptr) - { - - return false; - } - - - - - - - - - - - - - - - -#ifdef Q_OS_WIN - widget->winId(); - QWindowsWindowFunctions::setHasBorderInFullScreen(widget->windowHandle(), true); -#endif - - widget->activateWindow(); - widget->showFullScreen(); - - place(widget); - - return true; -} - - -bool LtsMultiMonitor::selectAndSwap(QWidget *widget) -{ - if (!m_active) - return false; - - Q_ASSERT(!isOverlapBaseLayouts()); - if (isOverlapBaseLayouts()) - return false; - - - - LtsScreenContainer *oldContainer = nullptr; - for (LtsScreenBase *_screen : qAsConst(m_screens)) - { - try - { - oldContainer = _screen->containerOfWidget(widget); - } - catch (...){} - } - - if (oldContainer == nullptr) - { - - LtsWidgets::LtsMessageDialog::showMessage(widget, tr("Could not determine screen id for this swap!
Swapping not possible, please try again later.")); - return false; - } - - - - - return selectScreen(widget, oldContainer); -} - - -LtsScreenBase *LtsMultiMonitor::widgetsScreen(QWidget *widget) -{ - for (LtsScreenBase *screen : qAsConst(m_screens)) - if (screen->hasWidget(widget)) - return screen; - return nullptr; -} - - -bool LtsMultiMonitor::isWidgetSecondary(QWidget *widget) -{ -#ifndef GUEZ_MODE - return false; -#endif - - auto *blw = qobject_cast(widget); - auto *secondaryWidget = qobject_cast(widget); - - bool isSecondaryWidget = ((blw != nullptr && blw->isSecondaryWidget()) || secondaryWidget != nullptr); - - - - return isSecondaryWidget; -} - - -class StupidDeleter -{ -public: - explicit StupidDeleter(LtsScreenBase *screen) - : m_screen(screen) - {} - - ~StupidDeleter() - { - - if (m_screen != nullptr) - { - - m_screen->revertSwitch(); - } - - } - - void reset() - { - - m_screen = nullptr; - } - - -private: - LtsScreenBase *m_screen; -}; - - -bool LtsMultiMonitor::selectScreen(QWidget *widget, LtsScreenContainer *swap, bool putOnMainIfFree, bool ltsScreenNormalOnly, const QVector &irreplacableWidgets) -{ - Q_ASSERT(widget != nullptr); - if (widget == nullptr) - return false; - - if (cancelAndProcessIfInactive(widget)) - return true; - - init(); - - if (overlapBaseLayouts(widget)) - return true; - - bool isSecondary = isWidgetSecondary(widget); - - - if (!isSecondary) - { - bool allEmpty = putOnMainIfFree; - for (LtsScreenBase *screen : qAsConst(m_screens)) - { - screen->switchToTemp(LtsScreenBase::WidgetType::MainWidget); - bool isEmpty = screen->isEmpty(); - screen->revertSwitch(); - - if (isEmpty) - continue; - - allEmpty = false; - break; - } - - - - if (allEmpty) - { - - - - m_mainScreen->switchToTemp(LtsScreenBase::WidgetType::MainWidget); - - try - { - m_mainScreen->putWidget(widget); - } - catch (...) - { - qWarning() << "failed - reverting"; - m_mainScreen->revertSwitch(); - - Q_ASSERT_X(false, __FUNCTION__, "screen promised to be empty but 'putWidgetSomewhere' method failed regardless"); - - - auto *blw = qobject_cast(widget); - if (blw != nullptr) - emit blw->signalClose(); - else - widget->deleteLater(); - - return false; - } - - m_mainScreen->check(); - place(widget); - m_mainScreen->finalizeSwitch(); - return true; - } - } - - - auto *selectionProcessWatcher = new QObject; - - - bool minOnce = false; - - for (auto i = m_screens.begin(); i != m_screens.end(); ++i) - { - LtsScreenBase *screen = *i; - - screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget); - - StupidDeleter _(screen); - - if (screen->isIrreplaceable()) - { - - continue; - } - - bool skip = false; - for (QWidget *widget : qAsConst(irreplacableWidgets)) - { - - if (screen->hasWidget(widget)) - { - - skip = true; - continue; - } - - - } - - if (skip) - continue; - - MultiMonitorSelectorBase *mms; - if (qobject_cast(screen) != nullptr) - { - auto *screenNormal = static_cast(screen); - mms = new MultiMonitorSelectorNormal(screenNormal, isSecondary, irreplacableWidgets); - } - else if (qobject_cast(screen) != nullptr && !ltsScreenNormalOnly) - { - auto *screenWide = static_cast(screen); - mms = new MultiMonitorSelectorWide(screenWide, nullptr, irreplacableWidgets); - } - else - continue; - - - QPointer widgetPtr(widget); - - - connect(widget, &QWidget::destroyed, - mms, &MultiMonitorSelectorBase::close); - connect(mms, &MultiMonitorSelectorBase::rejected, - this, [widgetPtr, swap]() - { - - - if (!widgetPtr || swap != nullptr) - return; - - - - - - auto *blw = qobject_cast(widgetPtr.data()); - if (blw != nullptr) - emit blw->signalClose(); - else - widgetPtr->deleteLater(); - }); - connect(mms, &MultiMonitorSelectorBase::signalSelected, - this, [this, mms, widgetPtr]() - { - - - for (LtsScreenBase *screen : qAsConst(m_screens)) - screen->check(); - - - - if (widgetPtr) - place(widgetPtr.data()); - - - - mms->deleteLater(); - emit signalSelected(); - - - }); - connect(mms, &MultiMonitorSelectorBase::destroyed, - selectionProcessWatcher, &QObject::deleteLater); - connect(selectionProcessWatcher, &QObject::destroyed, - mms, &MultiMonitorSelectorBase::close); - - mms->setSwap(swap); - mms->setWidget(widget); - mms->show(); - _.reset(); - minOnce = true; - } - - if (!minOnce) - { - selectionProcessWatcher->deleteLater(); - auto *blw = qobject_cast(widget); - if (blw != nullptr) - emit blw->signalClose(); - else - widget->deleteLater(); - - LtsWidgets::LtsMessageDialog::showMessage(m_mainScreen->screenDialog(), tr("There are no free screens available to select from.")); - return false; - } - - return true; -} - - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h b/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h deleted file mode 100755 index 88c0cbb6b..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "lts_multi_monitor_export.h" - -#include -#include - -#include - - -namespace LtsMultiMonitor -{ -class LtsScreenBase; -class MultiMonitorSelectorBase; -class LtsScreenContainer; - - -class LTSMULTIMONITOR_EXPORT LtsMultiMonitor : public QObject -{ - Q_OBJECT - -public: - static LtsMultiMonitor *getInstance(); - - - - bool selectScreen(QWidget *widget, LtsScreenContainer *swap = nullptr, bool putOnMainIfFree = true, bool ltsScreenNormalOnly = false, const QVector &irreplacableWidgets = {}); - bool selectAndSwap(QWidget *widget); - - - void setEnableMultiMonitorMode() { m_active = true; } - void setDebugMultiMonitorMode() { m_debugMargins = true; } - bool isActive() const { return m_active; } - - LtsScreenBase *widgetsScreen(QWidget *widget); - - - - - void freezeMagicCorner(int screenId); - void unfreezeMagicCorner(int screenId); - - bool putScreen(QWidget *widget, int screenId, int screenPart); - bool putScreenAutomatically(QWidget *widget); - static void place(QWidget *widget); - void setSkipScreen(int screenId, int subScreenId = -1) { m_skipScreenId = screenId; m_subSkipScreenId = subScreenId; } - LtsScreenBase *mainScreen() const { return m_mainScreen; } - int screenCount() const; - -private slots: - void screenDied(); - -private: - LtsMultiMonitor(); - ~LtsMultiMonitor(); - static void cleanupLtsMultiMonitor(); - void init(); - bool cancelAndProcessIfInactive(QWidget *widget) const; - static bool isWidgetSecondary(QWidget *widget) ; - - - - bool overlapBaseLayouts(QWidget *widget); - bool isOverlapBaseLayouts() const; - - QVector m_screens; - int m_nextScreenId { 0 }; - LtsScreenBase *m_mainScreen; - bool m_active; - bool m_initialized; - int m_skipScreenId; - int m_subSkipScreenId; - - bool m_overlapBaseLayouts; - bool m_debugMargins; - -signals: - void signalQuitLoop(); - void signalSelected(); - void signalScreenCountChanged(int); - void signalMakeAllSwole(); - -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h deleted file mode 100755 index 973af8a09..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include - -#ifndef LTSMULTIMONITOR_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSMULTIMONITOR_EXPORT -# else -# if defined(MAKE_LTSMULTIMONITOR_LIB) -# define LTSMULTIMONITOR_EXPORT Q_DECL_EXPORT -# else -# define LTSMULTIMONITOR_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp deleted file mode 100755 index 382ade6f8..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp +++ /dev/null @@ -1,419 +0,0 @@ - - -#include "lts_multi_monitor_selector_base.h" -#include "lts_multi_monitor.h" -#include "lts_screen_base.h" -#include "lts_screen_container.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsMultiMonitor -{ -int MultiMonitorSelectorBase::s_margin = 10; -int MultiMonitorSelectorBase::s_offset = 6; -int MultiMonitorSelectorBase::s_buttonSize = 20; - - -MultiMonitorSelectorBase::MultiMonitorSelectorBase(LtsScreenBase *screen, const QVector &irreplacableWidgets, QWidget *) - : QDialog(nullptr) - , m_irreplacableWidgets(irreplacableWidgets) - , m_screen(screen) - , m_screenIsIrreplaceable(screen->isIrreplaceable()) -{ - setGeometry(screen->screenRect()); - setMouseTracking(true); - setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint ); - setAttribute(Qt::WA_TranslucentBackground); - setAttribute(Qt::WA_DeleteOnClose); -} - - -bool MultiMonitorSelectorBase::isNullSwap(LtsScreenContainer *container) -{ - if (container == m_swap) - { - - return true; - } - - - return false; -} - - -void MultiMonitorSelectorBase::closeEvent(QCloseEvent *event) -{ - - - qWarning() << "closeEvent"; - - if (!m_selected && !m_reverted) - { - qWarning() << "closeEvent: not selected and not reverted! reverting!"; - m_screen->revertSwitch(); - } - - - - - event->accept(); -} - - -bool MultiMonitorSelectorBase::killOrSwapOldBLW(LtsScreenContainer *container ) noexcept(false) -{ - - - - - - - - - if (isNullSwap(container)) - return true; - - - - - if (m_swap != nullptr) - { - QWidget *oldSwapWidget; - try - { - - oldSwapWidget = m_swap->stealWidget(); - - - } - catch (...) - { - - throw; - } - - - - - - if (!container->isEmpty()) - { - - QWidget *oldContainerWidget; - try - { - - oldContainerWidget = container->stealWidget(); - - - } - catch (...) - { - - throw; - } - - - m_swap->setWidget(oldContainerWidget); - } - - - - container->setWidget(oldSwapWidget); - - - if(m_swap->activeWidgetType() == LtsScreenBase::WidgetType::SecondaryWidget && !m_swap->hasSecondaryWidget() && m_swap->hasMainWidget()) - m_swap->toggle(); - else if(m_swap->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget && !m_swap->hasMainWidget() && m_swap->hasSecondaryWidget()) - m_swap->toggle(); - - - return true; - } - - - - QWidget *oldWidget; - try - { - - oldWidget = container->widget(); - - } - catch (...) - { - - return false; - } - - - - auto *oldBLW = qobject_cast(oldWidget); - - if (oldBLW == nullptr) - { - container->stealWidget()->deleteLater(); - - return false; - } - - - - if (!oldBLW->slotClose()) - { - - throw "MultiMonitorSelectorBase::killOrSwapOldBLW: failed to close old blw"; - } - - container->stealWidget(); - return false; - -} - - -void MultiMonitorSelectorBase::setWidget(QWidget *widget) -{ - m_widget = widget; -} - - -void MultiMonitorSelectorBase::mouseMoveEvent(QMouseEvent *event) -{ - m_cursorPos = event->pos(); - update(); -} - - -void MultiMonitorSelectorBase::mousePressEvent(QMouseEvent *event) -{ - const QRect abortButtonRect(QRect(centeredRect().topRight() + QPoint(s_buttonSize, s_buttonSize), QSize(s_buttonSize, s_buttonSize))); - auto pos = event->pos(); - - Q_ASSERT(m_widget != nullptr); - - - if (abortButtonRect.contains(pos) || m_widget == nullptr) - { - qWarning() << "X pressed!"; - m_reverted = true; - m_screen->revertSwitch(); - reject(); - return; - } - - if (processSelection(pos)) - { - qWarning() << "process!"; - m_screen->finalizeSwitch(); - selected(); - return; - } -} - - -void MultiMonitorSelectorBase::selected() -{ - m_widget = nullptr; - m_selected = true; - emit signalSelected(); -} - - -QRect MultiMonitorSelectorBase::centeredRect() const -{ - - if (m_cachedCenteredRect.isValid()) - return m_cachedCenteredRect; - - - const QSize thisSize(size()); - const QSize newSize(thisSize.width() * 0.20, thisSize.height() * 0.20); - m_cachedCenteredRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, newSize, rect()); - - return m_cachedCenteredRect; -} - - -void MultiMonitorSelectorBase::setColors(QPainter &p, const QRect checkSelection, bool occupied, bool countEdge) -{ - if (occupied) - { - p.setBrush(QColor("silver")); - p.setPen(QPen(QColor("white"), 2)); - return; - } - - - if (checkSelection.contains(m_cursorPos, countEdge)) - { - - p.setBrush(QColor("grey")); - p.setPen(QPen(QColor("white"), 2)); - return; - } - - - - p.setBrush(QColor("black")); - p.setPen(QPen(p.brush(), 1)); -} - - -void MultiMonitorSelectorBase::drawBase(QPainter &p, bool drawSelector, bool occupied, bool isSecondary, bool isInactiveWidgetOccupied) -{ - bool showReplacementIndicator = true; -#ifndef GUEZ_MODE - isSecondary = false; - showReplacementIndicator = false; -#endif - - - p.save(); - p.setRenderHint(QPainter::Antialiasing, true); - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - - - - p.setBrush(QColor(53, 53, 53, 150)); - p.drawRect(geometry()); - - - - const QRect centered(centeredRect()); - p.setBrush(QColor("silver")); - p.setPen(QPen(QColor("black"), 0.5)); - - - - if (isSecondary && isInactiveWidgetOccupied) - { - const QRect r(centered.adjusted(12, 12, 12, 12)); - setColors(p, r, true); - p.drawRoundedRect(r, 7, 7); - } - - - - if (drawSelector) - { - if (!m_screenIsIrreplaceable) - { - - setColors(p, centered, occupied); - } - - p.drawRoundedRect(centered, 7, 7); - } - - - const int cX = centered.x(); - const int cW = centered.width(); - const int cY = centered.y(); - const int cH = centered.height(); - - - - if (isSecondary) - { - QPen pen(p.pen()); - QColor pc(pen.color()); - const QColor c(255 - pc.red(), 255 - pc.green(), 255 - pc.blue()); - pen.setColor(c); - p.setPen(pen); - - QFont f(p.font()); - f.setBold(true); - f.setPixelSize(cH / 2); - p.setFont(f); - - const QString text(tr("S", "Secondary (German: 'H' for 'Hilfsansicht')")); - - p.drawText(centered, Qt::AlignCenter, text); - } - - - - const QRect abortButtonRect(QRect(centered.topRight() + QPoint(s_buttonSize, s_buttonSize), QSize(s_buttonSize, s_buttonSize))); - const QPoint topRight(abortButtonRect.topLeft() ); - setColors(p, abortButtonRect, false, false); - - QPen pen(p.pen()); - pen.setWidth(5.0); - p.setPen(pen); - - p.drawLines(QVector() - << QLine(topRight , topRight + QPoint(s_buttonSize, s_buttonSize)) - << QLine(topRight + QPoint(0, s_buttonSize), topRight + QPoint(s_buttonSize, 0)) - ); - - - - if (showReplacementIndicator && occupied) - { - p.setBrush(QColor("silver")); - p.setPen(QPen(QColor("white"), 2)); - - const QRect secondRect(centered.adjusted(cW + 70, 0, cW + 70, 0)); - p.drawRoundedRect(secondRect, 7, 7); - - - p.drawLine(cX + cW + 15, cY + (cH / 2), - cX + cW + 55, cY + (cH / 2)); - - - p.drawPolygon(QPolygon( - { - QPoint(cX + cW + 56, cY + (cH / 2)), - QPoint(cX + cW + 46, cY + (cH / 2) - 10), - QPoint(cX + cW + 46, cY + (cH / 2) + 10), - })); - - const int srX = secondRect.x(); - const int srY = secondRect.y(); - const int srH = secondRect.height(); - const int diameter = srH - 50; - const int widthByTwo = (secondRect.width() / 2); - const int diameterByTwo = (diameter / 2); - const int lineX1 = srX + widthByTwo - diameterByTwo; - const int lineY1 = srY + 25; - - p.drawEllipse(lineX1, lineY1, diameter, diameter); - - - if (isSecondary) - { - QFont f(p.font()); - f.setBold(true); - f.setPixelSize(srH / 2); - p.setFont(f); - - const QString text(tr("S", "Secondary (German: 'H' for 'Hilfsansicht')")); - - p.drawText(secondRect, Qt::AlignCenter, text); - } - - - const int lineX2 = lineX1 + diameter; - const int lineY2 = lineY1 + diameter; - p.drawLine(lineX1, lineY1, lineX2, lineY2); - p.drawLine(lineX2, lineY1, lineX1, lineY2); - } - - - p.restore(); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h deleted file mode 100755 index c04cf457e..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include - - -class QScreen; -class QPainter; -class QPaintEvent; -class QMouseEvent; -class QCloseEvent; - - -namespace LtsMultiMonitor -{ -class LtsScreenBase; -class LtsScreenContainer; - -class MultiMonitorSelectorBase : public QDialog -{ - Q_OBJECT - -public: - explicit MultiMonitorSelectorBase(LtsScreenBase *screen, const QVector &irreplacableWidgets, QWidget *); - virtual ~MultiMonitorSelectorBase() {} - - void setSwap(LtsScreenContainer *swap) { m_swap = swap; } - void setWidget(QWidget *widget); - LtsScreenBase *screen() const { return m_screen; } - -signals: - void signalSelected(); - -protected: - static int s_offset; - static int s_margin; - static int s_buttonSize; - - LtsScreenContainer *m_swap { nullptr }; - QWidget *m_widget { nullptr }; - mutable QRect m_cachedCenteredRect; - QVector m_irreplacableWidgets; - - void drawBase(QPainter &p, bool drawSelector = true, bool occupied = false, bool isSecondary = false, bool isInactiveWidgetOccupied = false); - bool killOrSwapOldBLW(LtsScreenContainer *container) noexcept(false); - bool isNullSwap(LtsScreenContainer *container); - QRect centeredRect() const; - void setColors(QPainter &p, const QRect checkSelection, bool occupied = false, bool countEdge = false); - void selected(); - virtual bool processSelection(const QPoint &selection) = 0; - -private: - QPoint m_cursorPos; - bool m_selected { false }; - LtsScreenBase *m_screen; - bool m_screenIsIrreplaceable; - bool m_reverted { false }; - - void closeEvent(QCloseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp deleted file mode 100755 index dc5908d4b..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp +++ /dev/null @@ -1,100 +0,0 @@ - - -#include "lts_multi_monitor.h" -#include "lts_multi_monitor_selector_normal.h" -#include "lts_screen_container.h" -#include "lts_screen_normal.h" -#include "lts_dummy_widget.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsMultiMonitor -{ - -MultiMonitorSelectorNormal::MultiMonitorSelectorNormal(LtsScreenNormal *screen, bool isSecondary, const QVector &irreplacableWidgets) - : MultiMonitorSelectorBase(screen, irreplacableWidgets, nullptr) - , m_screen(screen) - , m_screenIsEmpty(screen->isEmpty()) - , m_isSecondary(isSecondary) - , m_isInactiveWidgetOccupied(screen->child()->isInactiveWidgetOccupied()) -{ - -} - - -MultiMonitorSelectorNormal::~MultiMonitorSelectorNormal() -{ - -} - - -bool MultiMonitorSelectorNormal::processSelection(const QPoint &selection) -{ - - - - if (!centeredRect().contains(selection)) - { - - return false; - } - - if (m_screen->isIrreplaceable()) - { - - - return false; - } - - try - { - if (m_irreplacableWidgets.contains(m_screen->child()->widget())) - return false; - } - catch(...){} - - - - - - try - { - if (killOrSwapOldBLW(m_screen->child())) - { - - return true; - } - } - catch (...) - { - return false; - } - - - - - m_screen->putWidgetUnmanaged(m_widget); - - - return true; -} - - -void MultiMonitorSelectorNormal::paintEvent(QPaintEvent *) -{ - QPainter p(this); - drawBase(p, true, !m_screenIsEmpty, m_isSecondary, m_isInactiveWidgetOccupied); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h deleted file mode 100755 index b793341ac..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "lts_multi_monitor_selector_base.h" - -#include -#include - - -class QScreen; -class QPaintEvent; -class QMouseEvent; - - -namespace LtsMultiMonitor -{ -class LtsScreenBase; -class LtsScreenNormal; - -class MultiMonitorSelectorNormal : public MultiMonitorSelectorBase -{ - Q_OBJECT - -public: - explicit MultiMonitorSelectorNormal(LtsScreenNormal *screen, bool isSecondary, const QVector &irreplacableWidgets = {}); - ~MultiMonitorSelectorNormal(); - -private: - LtsScreenNormal *m_screen; - bool m_screenIsEmpty; - bool m_isSecondary; - bool m_isInactiveWidgetOccupied; - - bool processSelection(const QPoint &selection) override; - void paintEvent(QPaintEvent *event) override; -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp deleted file mode 100755 index 3f514d624..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp +++ /dev/null @@ -1,324 +0,0 @@ - - -#include "lts_multi_monitor.h" -#include "lts_multi_monitor_selector_wide.h" -#include "lts_screen_container.h" -#include "lts_screen_wide.h" - -#include -#include -#include -#include - -#include -#include -#include - - - - -namespace LtsMultiMonitor -{ - -MultiMonitorSelectorWide::MultiMonitorSelectorWide(LtsScreenWide *screen, QWidget *, const QVector &irreplacableWidgets) - : MultiMonitorSelectorBase(screen, irreplacableWidgets, nullptr ) - , m_screen(screen) -{} - - -bool MultiMonitorSelectorWide::isDrawOnlyOneSelector() -{ - - - - bool res = ((m_screen->childMain()->isEmpty() && m_screen->childLeft()->isEmpty() && m_screen->childRight()->isEmpty()) - || (!m_screen->childMain()->isEmpty() && m_swap != nullptr && m_screen->childMain() == m_swap)); - - - - - return res; -} - - -bool MultiMonitorSelectorWide::processSelection(const QPoint &selection) -{ - - - - - - LtsScreenContainer *main = m_screen->childMain(); - LtsScreenContainer *left = m_screen->childLeft(); - LtsScreenContainer *right = m_screen->childRight(); - - bool singleSelector = isDrawOnlyOneSelector(); - - if (singleSelector) - { - if (main->isIrreplaceable()) - { - - return false; - } - - try - { - if (m_irreplacableWidgets.contains(main->widget())) - return false; - } - catch(...){} - - - - if (isNullSwap(m_screen->childMain())) - { - - return true; - } - - if (centeredRect().contains(selection)) - { - try - { - if (killOrSwapOldBLW(m_screen->childMain())) - { - - return true; - } - } - catch (...) - { - - return false; - } - - - m_screen->putWidgetUnmanagedMain(m_widget); - - return true; - } - - - return false; - } - - - bool leftContains = leftSide().contains(selection); - bool rightContains = rightSide().contains(selection); - - - - - - if (leftContains) - { - if (left->isIrreplaceable()) - { - - return false; - } - - try - { - if (m_irreplacableWidgets.contains(left->widget())) - return false; - } - catch(...){} - - - - try - { - - QWidget *oldWidget = main->stealWidget(); - - - m_screen->putWidgetUnmanagedRight(oldWidget); - } - catch (...) {} - - - - - try - { - if (killOrSwapOldBLW(left)) - { - - return true; - } - } - catch (...) - { - - return false; - } - - - - m_screen->putWidgetUnmanagedLeft(m_widget); - return true; - } - else if (rightContains) - { - if (right->isIrreplaceable()) - { - - return false; - } - - try - { - if (m_irreplacableWidgets.contains(right->widget())) - return false; - } - catch(...){} - - - - try - { - - QWidget *oldWidget = main->stealWidget(); - - - m_screen->putWidgetUnmanagedLeft(oldWidget); - } - catch (...) {} - - - - - - try - { - if (killOrSwapOldBLW(right)) - { - - return true; - } - } - catch (...) - { - - return false; - } - - - m_screen->putWidgetUnmanagedRight(m_widget); - return true; - } - - return false; -} - - -QRect MultiMonitorSelectorWide::leftSide() const -{ - QRect centered = centeredRect(); - - return QRect(centered.x() + s_margin, - centered.y() + s_margin, - (centered.width() / 2) - (s_margin*2), - centered.height() - (s_margin*2)); -} - - -QRect MultiMonitorSelectorWide::rightSide() const -{ - QRect centered = centeredRect(); - - return QRect(centered.x() + (centered.width() / 2) + s_margin, - centered.y() + s_margin, - (centered.width() / 2) - (s_margin*2), - centered.height() - (s_margin*2)); -} - - -void MultiMonitorSelectorWide::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.save(); - - p.setRenderHint(QPainter::Antialiasing, true); - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - - const QRect centered(centeredRect()); - - bool drawOnlyOneSelector = isDrawOnlyOneSelector(); - drawBase(p, drawOnlyOneSelector, false ); - - - - if (!drawOnlyOneSelector) - { - p.setBrush(QColor("silver")); - p.setPen(QPen(QColor("black"), 0.5)); - p.drawRoundedRect(centered, 7, 7); - - - bool leftIrreplacable = false; - try - { - leftIrreplacable = m_irreplacableWidgets.contains(m_screen->childLeft()->widget()); - } - catch(...){} - - if (!m_screen->childLeft()->isIrreplaceable() && !leftIrreplacable) - { - - const QRect leftSide(centered.x() + s_margin, - centered.y() + s_margin, - (centered.width() / 2) - (s_margin*2), - centered.height() - (s_margin*2)); - - setColors(p, leftSide, !m_screen->childLeft()->isEmpty()); - - p.drawRoundedRect(leftSide, 6, 6); - } - - - bool rightIrreplacable = false; - try - { - rightIrreplacable = m_irreplacableWidgets.contains(m_screen->childRight()->widget()); - } - catch(...){} - - if (!m_screen->childRight()->isIrreplaceable() && !rightIrreplacable) - { - - const QRect rightSide(centered.x() + (centered.width() / 2) + s_margin, - centered.y() + s_margin, - (centered.width() / 2) - (s_margin*2), - centered.height() - (s_margin*2)); - - setColors(p, rightSide, !m_screen->childRight()->isEmpty()); - - p.drawRoundedRect(rightSide, 6, 6); - } - } - - - - static int buttonSize = 20; - const QRect abortButtonRect(QRect(centered.topRight() + QPoint(buttonSize, buttonSize), QSize(buttonSize, buttonSize))); - const QPoint topRight(abortButtonRect.topLeft() ); - setColors(p, abortButtonRect, false, false); - - - QPen pen(p.pen()); - pen.setWidth(5.0); - p.setPen(pen); - - p.drawLines(QVector() - << QLine(topRight , topRight + QPoint(buttonSize, buttonSize)) - << QLine(topRight + QPoint(0, buttonSize), topRight + QPoint(buttonSize, 0)) - ); - - - p.restore(); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h b/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h deleted file mode 100755 index 483401af7..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "LtsMultiMonitor/lts_multi_monitor_selector_base.h" - -#include -#include - - -class QScreen; -class QPaintEvent; -class QMouseEvent; - - -namespace LtsMultiMonitor -{ -class LtsScreenWide; -class LtsScreenBase; - -class MultiMonitorSelectorWide : public MultiMonitorSelectorBase -{ - Q_OBJECT - -public: - explicit MultiMonitorSelectorWide(LtsScreenWide *screen, QWidget *superParent, const QVector &irreplacableWidgets); - -private: - LtsScreenWide *m_screen; - - bool isDrawOnlyOneSelector(); - QRect leftSide() const; - QRect rightSide() const; - bool processSelection(const QPoint &selection) override; - void paintEvent(QPaintEvent *event) override; -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp b/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp deleted file mode 100755 index 1772c1899..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "lts_screen_base.h" -#include "lts_dummy_widget.h" - -#include -#include - -#include -#include -#include -#include -#ifdef Q_OS_WIN -#include -#endif - - -class NoCloseWidget : public QWidget -{ - void closeEvent(QCloseEvent *event) override - { - event->ignore(); - } -}; - - -namespace LtsMultiMonitor -{ -LtsScreenBase::LtsScreenBase(QRect screenGeo, int screenId, bool debugMargins, const QString &serial) - : m_screenDialog(new NoCloseWidget()) - , m_screenId(screenId) - , m_serial(serial) - , m_screenRect(screenGeo) -{ - connect(this, &LtsScreenBase::destroyed, - m_screenDialog, &QWidget::deleteLater); - - if (debugMargins) - { - qWarning() << "LtsScreenBase: screen rect adjusted for dev machines!"; - m_screenRect.adjust(+50, +50, -100, -100); - } - - - m_screenDialog->setWindowFlags(Qt::FramelessWindowHint); - m_screenDialog->setGeometry(m_screenRect); - m_screenDialog->setMinimumSize(m_screenRect.width(), m_screenRect.height()); - m_screenDialog->setMaximumSize(m_screenRect.width(), m_screenRect.height()); - m_screenDialog->setContentsMargins(0, 0, 0, 0); - m_screenDialog->setWindowFlag(Qt::WindowType::WindowStaysOnBottomHint); - -#ifdef Q_OS_WIN - m_screenDialog->winId(); - QWindowsWindowFunctions::setHasBorderInFullScreen(m_screenDialog->windowHandle(), true); -#endif - - - - - - - - - - - - m_screenDialog->activateWindow(); - m_screenDialog->show(); -} - - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_base.h b/Lts/Common/LtsMultiMonitor/lts_screen_base.h deleted file mode 100755 index 06a9bcde2..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_base.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "lts_multi_monitor_export.h" - -#include -#include - -class QSplitter; -class QHBoxLayout; - -namespace LtsUtils { class Promise; } - -namespace LtsMultiMonitor -{ -class LtsScreenContainer; - - - -class LTSMULTIMONITOR_EXPORT LtsScreenBase : public QObject -{ - Q_OBJECT - -public: - enum WidgetType - { - MainWidget, - SecondaryWidget, - None, - }; - - LtsScreenBase(QRect screenGeo, int screenId, bool debugMargins, const QString &serial); - - const QRect &screenRect() const { return m_screenRect; } - int screenId() const { return m_screenId; } - const QString serialNumber() const { return m_serial; } - QWidget *screenDialog() const { return m_screenDialog; } - - virtual int screenType() const = 0; - virtual int containerIdOfWidget(QWidget *widget) const noexcept(false) = 0; - virtual LtsScreenContainer *putWidget(QWidget *widget, int screenPart = -1 ) noexcept(false) = 0; - virtual LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) = 0; - virtual bool isEmpty() const = 0; - virtual bool isIrreplaceable() const = 0; - virtual void check() {} - virtual bool hasWidget(QWidget *widget) const = 0; - virtual void switchToTemp(WidgetType ) {} - virtual void finalizeSwitch() {} - virtual void revertSwitch() {} - -protected: - QWidget *m_screenDialog; - -private: - int m_screenId; - QString m_serial; - QRect m_screenRect; -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp b/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp deleted file mode 100755 index 35ce51804..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp +++ /dev/null @@ -1,555 +0,0 @@ -#include "lts_screen_container.h" -#include "lts_dummy_widget.h" - -#include - -#include -#include -#include -#include - - - -#include - - - -namespace LtsMultiMonitor -{ -QString LtsScreenContainer::geoStr() const -{ - return QStringLiteral("%1:%2:") - .arg(QString::number(m_screenGeo.x()).rightJustified(4, QLatin1Char(' '))) - .arg(QString::number(m_screenGeo.y()).rightJustified(4, QLatin1Char(' '))) - ; -} - -LtsScreenContainer::LtsScreenContainer(QRect screenGeo) - : m_layout(new QHBoxLayout) - , m_screenGeo(screenGeo) -{ - - m_layout->setMargin(0); - m_layout->setSpacing(0); - m_layout->setStretch(0, 999); - - setContentsMargins(0, 0, 0, 0); - vanish(); - setLayout(m_layout); - - - - QTimer *t = new QTimer(this); - t->setInterval(3000); -#ifdef LTS_DEBUG - t->start(); -#endif - connect(t, &QTimer::timeout, - this, [this, screenGeo]() - { - QString old(QStringLiteral("<#!UNKNOWN!#>")); - - if (m_oldActiveWidget == nullptr) - old = QStringLiteral("null"); - else if (m_oldActiveWidget == &m_mainWidget) - old = QStringLiteral("MAIN"); - else if (m_oldActiveWidget == &m_secondaryWidget) - old = QStringLiteral("SECONDARY"); - - - }); -} - - -bool LtsScreenContainer::isInactiveWidgetOccupied() const -{ - if (m_activeWidget == &m_mainWidget && m_secondaryWidget != nullptr) - return true; - if (m_activeWidget == &m_secondaryWidget && m_mainWidget != nullptr) - return true; - return false; -} - - -QIcon LtsScreenContainer::mainWindowIcon() const -{ - if (m_mainWidget != nullptr) - return m_mainWidget->windowIcon(); - return {}; -} - - -QIcon LtsScreenContainer::secondaryWindowIcon() const -{ - if (m_secondaryWidget != nullptr) - return m_secondaryWidget->windowIcon(); - return {}; -} - - -bool LtsScreenContainer::mainAndSecondaryOccupied() const -{ - return (m_mainWidget != nullptr && m_secondaryWidget != nullptr); -} - - -LtsScreenBase::WidgetType LtsScreenContainer::activeWidgetType() const -{ - if (m_activeWidget == &m_mainWidget) - return LtsScreenBase::WidgetType::MainWidget; - if (m_activeWidget == &m_secondaryWidget) - return LtsScreenBase::WidgetType::SecondaryWidget; - - assert(false); - return LtsScreenBase::WidgetType::None; -} - - -void LtsScreenContainer::toggle() -{ - - switchToTemp((*m_activeWidget == m_mainWidget) ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget); - - QWidget *w = activeWidget(); - - assert(w != nullptr); - if (w == nullptr) - { - revertSwitch(); - return; - } - - if (!tryPutDummyUnmanaged(w)) - w->show(); - - finalizeSwitch(); -} - - -bool LtsScreenContainer::isIrreplaceable() const -{ - if (m_activeWidget == nullptr) - return false; - - auto *blw = qobject_cast(activeWidget()); - if (blw == nullptr) - return false; - - return blw->isIrreplaceable(); -} - - -QWidget *LtsScreenContainer::activeWidget() const -{ - assert(m_activeWidget != nullptr); - - - - - - - return *m_activeWidget; -} - - -QWidget **LtsScreenContainer::activeWidgetPtr() -{ - assert(m_activeWidget != nullptr); - - - - - - - return m_activeWidget; -} - - -void LtsScreenContainer::vanish() -{ - return; - - - - - - - - - - - m_layout->setParent(nullptr); -} - - -void LtsScreenContainer::reappear() -{ - return; - - - - - setAttribute(Qt::WA_TranslucentBackground, false); - - - - - - if (m_layout->parent() == nullptr) - setLayout(m_layout); -} - - - - - - - - - - - - - -QWidget *LtsScreenContainer::stealWidget() noexcept(false) -{ - - - QWidget **widgetPtr = activeWidgetPtr(); - QWidget *widget = *widgetPtr; - - if (widget == nullptr) - { - - throw "stealWidget: no widget to steal"; - } - - - - - widget->setParent(nullptr); - vanish(); - - - disconnect(widget, &QWidget::destroyed, - this, &LtsScreenContainer::slotChildDestroyed); - - - - - - - - - QWidget *oldWidget = widget; - *widgetPtr = nullptr; - - return oldWidget; -} - - -bool LtsScreenContainer::hasWidget(QWidget *widget) const -{ - if (m_mainWidget != nullptr && m_mainWidget == widget) - return true; - if (m_secondaryWidget != nullptr && m_secondaryWidget == widget) - return true; - return false; -} - - -bool LtsScreenContainer::isEmpty() const -{ - QWidget **currentWidgetPtr = m_activeWidget; - if (currentWidgetPtr == nullptr) - return true; - return (*currentWidgetPtr == nullptr); -} - -bool LtsScreenContainer::hasMainWidget() -{ - return m_mainWidget != nullptr; -} - -bool LtsScreenContainer::hasSecondaryWidget() -{ - return m_secondaryWidget != nullptr; -} - -bool LtsScreenContainer::tryPutDummyUnmanaged(QWidget *widget) -{ - auto *dummy = qobject_cast(widget); - - if (dummy == nullptr) - return false; - - - - - - - QRect r(mapToGlobal(rect().topLeft()), rect().size()); - - emit dummy->signalWidgetPlaced(r); - - - - - - - - - - emit signalSetCornerIcons(); - - return true; -} - - -void LtsScreenContainer::switchToTemp(LtsScreenBase::WidgetType widgetType) -{ - - - Q_ASSERT(widgetType != LtsScreenBase::WidgetType::None); - - if ((m_activeWidget == &m_mainWidget && widgetType == LtsScreenBase::WidgetType::MainWidget) || - (m_activeWidget == &m_secondaryWidget && widgetType == LtsScreenBase::WidgetType::SecondaryWidget)) - { - - - m_nullSwitchActive = true; - return; - } - - if (widgetType == LtsScreenBase::WidgetType::MainWidget) - m_oldActiveWidget = &m_secondaryWidget; - else if (widgetType == LtsScreenBase::WidgetType::SecondaryWidget) - m_oldActiveWidget = &m_mainWidget; - else - Q_ASSERT(false); - - - - - m_activeWidget = (widgetType == LtsScreenBase::WidgetType::SecondaryWidget ? &m_secondaryWidget : &m_mainWidget); - - -} - - -void LtsScreenContainer::finalizeSwitch() -{ - - - QWidget **oldActiveWidget = m_oldActiveWidget; - m_oldActiveWidget = nullptr; - - - if (m_nullSwitchActive) - { - - m_nullSwitchActive = false; - return; - } - - if (oldActiveWidget == nullptr) - { - - return; - } - - if (*oldActiveWidget == nullptr) - { - - return; - } - - - - - - - - - - - - QWidget *widget = *oldActiveWidget; - auto *dummy = qobject_cast(widget); - if (dummy != nullptr) - { - - dummy->hideVideo(); - } - else - { - - widget->hide(); - } - - QWidget *widget2 = *m_activeWidget; - auto *dummy2 = qobject_cast(widget2); - if (dummy2 != nullptr) - { - dummy2->showVideo(); - } - - emit signalSetCornerIcons(); -} - - -void LtsScreenContainer::revertSwitch() -{ - - - QWidget **oldActiveWidgetBackup = m_oldActiveWidget; - m_oldActiveWidget = nullptr; - - - if (m_nullSwitchActive) - { - - m_nullSwitchActive = false; - return; - } - - - - if (oldActiveWidgetBackup == nullptr) - return; - - m_activeWidget = oldActiveWidgetBackup; - -} - - -void LtsScreenContainer::setWidget(QWidget *widget) -{ - - - - - QWidget **widgetPtr = activeWidgetPtr(); - - qWarning() << geoStr() << "setWidget: m_activeWidget" << m_activeWidget; - - QWidget *currentWidget = *widgetPtr; - - Q_ASSERT(currentWidget == nullptr); - - - - - - *widgetPtr = widget; - reappear(); - - connect(widget, &QWidget::destroyed, - this, &LtsScreenContainer::slotChildDestroyed, - Qt::UniqueConnection); - - if (tryPutDummyUnmanaged(widget)) - return; - - - - - - - m_layout->addWidget(widget); - - - - - - - - - widget->show(); - - emit signalSetCornerIcons(); -} - - -void LtsScreenContainer::slotChildDestroyed() -{ - if (sender() == static_cast(m_mainWidget)) - { - m_mainWidget = nullptr; - qWarning() << geoStr() << "m_mainWidget set to nullptr!"; - - if (*m_activeWidget == m_mainWidget) - { - qWarning() << "slotChildDestroyed: switching to sec"; - switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget); - - QWidget *widget = activeWidget(); - - if (widget != nullptr) - { - auto *dummy = qobject_cast(widget); - if (dummy != nullptr) - { - QRect r(mapToGlobal(rect().topLeft()), rect().size()); - qWarning() << geoStr() << "LtsScreenContainer::slotChildDestroyed => (main) dummy->signalWidgetPlaced"; - emit dummy->signalWidgetPlaced(r); - } - else - { - widget->show(); - } - } - - finalizeSwitch(); - } - } - else if (sender() == static_cast(m_secondaryWidget)) - { - m_secondaryWidget = nullptr; - qWarning() << geoStr() << "m_secondaryWidget set to nullptr!"; - - if (*m_activeWidget == m_secondaryWidget) - { - qWarning() << "slotChildDestroyed: switching to main"; - switchToTemp(LtsScreenBase::WidgetType::MainWidget); - - QWidget *widget = activeWidget(); - - if (widget != nullptr) - { - auto *dummy = qobject_cast(widget); - if (dummy != nullptr) - { - QRect r(mapToGlobal(rect().topLeft()), rect().size()); - qWarning() << geoStr() << "LtsScreenContainer::slotChildDestroyed => (sec) dummy->signalWidgetPlaced"; - emit dummy->signalWidgetPlaced(r); - } - else - { - widget->show(); - } - } - - finalizeSwitch(); - } - } - else - { - qWarning() << geoStr() << "slotChildDestroyed NOT received from any known widget !!"; - Q_ASSERT(false); - } - - vanish(); - emit check(); -} - - -QWidget *LtsScreenContainer::widget() noexcept(false) -{ - QWidget **widgetPtr = m_activeWidget; - if (widgetPtr != nullptr && *widgetPtr != nullptr) - return *widgetPtr; - throw "no widget in container"; -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_container.h b/Lts/Common/LtsMultiMonitor/lts_screen_container.h deleted file mode 100755 index acd91a7a6..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_container.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "lts_screen_base.h" - -#include - -#include -#include - - -class QHBoxLayout; - - -namespace LtsMultiMonitor -{ -class LTSMULTIMONITOR_EXPORT LtsScreenContainer : public QWidget -{ - Q_OBJECT - -public: - LtsScreenContainer(QRect screenGeo); - - bool isIrreplaceable() const; - bool isEmpty() const; - bool hasMainWidget(); - bool hasSecondaryWidget(); - void setWidget(QWidget *widget); - bool hasWidget(QWidget *widget) const; - QWidget *widget() noexcept(false); - QWidget *stealWidget() noexcept(false); - void switchToTemp(LtsScreenBase::WidgetType widgetType); - void finalizeSwitch(); - void revertSwitch(); - bool mainAndSecondaryOccupied() const; - QIcon mainWindowIcon() const; - QIcon secondaryWindowIcon() const; - LtsScreenBase::WidgetType activeWidgetType() const; - bool isInactiveWidgetOccupied() const; - -signals: - void check(); - void signalSetCornerIcons(); - -private: - QHBoxLayout *m_layout { nullptr }; - QWidget *m_mainWidget { nullptr }; - QWidget *m_secondaryWidget { nullptr }; - QWidget **m_activeWidget { nullptr }; - QWidget **m_oldActiveWidget { nullptr }; - bool m_nullSwitchActive { false }; - QRect m_screenGeo; - - QString geoStr() const; - bool tryPutDummyUnmanaged(QWidget *widget); - void vanish(); - void reappear(); - QWidget *activeWidget() const; - QWidget **activeWidgetPtr(); - -public slots: - void toggle(); - -private slots: - - void slotChildDestroyed(); -}; - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp b/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp deleted file mode 100755 index 39b25f7cf..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "lts_screen_normal.h" -#include "lts_dummy_widget.h" -#include "lts_screen_container.h" -#include "lts_magic_corner.h" - -#include - -#include -#include -#include - - -#ifdef Q_OS_WIN -#include -#endif - - -namespace LtsMultiMonitor -{ - -LtsScreenNormal::LtsScreenNormal(QRect screenGeo, int screenId, bool debugMargins, const QString &serial) - : LtsScreenBase(screenGeo, screenId, debugMargins, serial) - , m_childContainer(new LtsScreenContainer(screenGeo)) - , m_corner(new LtsMagicCorner(screenGeo)) -{ - - connect(m_childContainer, &LtsScreenContainer::check, - this, &LtsScreenNormal::check); - - auto *theLayout = new QHBoxLayout; - - theLayout->setContentsMargins(0, 0, 0, 0); - theLayout->setMargin(0); - theLayout->setSpacing(0); - theLayout->setStretch(0, 999); - theLayout->addWidget(m_childContainer); - - m_screenDialog->setLayout(theLayout); - - m_corner->hide(); - connect(m_corner, &LtsMagicCorner::toggle, - this, [this]() - { - m_childContainer->toggle(); - m_corner->setActiveWidgetType(m_childContainer->activeWidgetType()); - setCornerIcons(); - }); - - connect(m_corner, &LtsMagicCorner::signalMakeAllSwole, - this, &LtsScreenNormal::signalMakeAllSwole); - connect(this, &LtsScreenNormal::signalMakeSwole, - m_corner, &LtsMagicCorner::makeSwole); - - connect(m_childContainer, &LtsScreenContainer::signalSetCornerIcons, - this, &LtsScreenNormal::setCornerIcons); -} - - -LtsScreenNormal::~LtsScreenNormal() -{ - m_corner->deleteLater(); -} - - -void LtsScreenNormal::setCornerIcons() -{ - m_corner->setActiveWidgetType(m_childContainer->activeWidgetType()); - m_corner->setIcon(m_childContainer->mainWindowIcon(), LtsScreenBase::WidgetType::MainWidget); - m_corner->setIcon(m_childContainer->secondaryWindowIcon(), LtsScreenBase::WidgetType::SecondaryWidget); - -} - - -void LtsScreenNormal::check() -{ - m_childContainer->mainAndSecondaryOccupied() ? m_corner->show() : m_corner->hide(); -} - - -LtsScreenContainer *LtsScreenNormal::containerOfWidget(QWidget *widget) -{ - if (m_childContainer->hasWidget(widget)) - return m_childContainer; - throw "widget is not present on this container"; -} - -void LtsScreenNormal::hideMagicCorner(bool hide) -{ - m_corner->setFreezed(hide); -} - - -bool LtsScreenNormal::isIrreplaceable() const -{ - return m_childContainer->isIrreplaceable(); -} - - -int LtsScreenNormal::containerIdOfWidget(QWidget *widget) const noexcept(false) -{ - if (m_childContainer->hasWidget(widget)) - return 0; - throw "widget not on this screen"; -} - - -LtsScreenContainer *LtsScreenNormal::putWidget(QWidget *widget, int ) noexcept(false) -{ - if (!isEmpty()) - throw "cannot put widget as container is non-empty"; - putWidgetUnmanaged(widget); - return m_childContainer; -} - - -bool LtsScreenNormal::hasWidget(QWidget *widget) const -{ - return (m_childContainer->hasWidget(widget)); -} - - -void LtsScreenNormal::putWidgetUnmanaged(QWidget *widget) -{ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - m_childContainer->setWidget(widget); - - - - - setCornerIcons(); - - - - - - -} - - -void LtsScreenNormal::slotOverlayDestroyed() -{ - m_screenDialog->show(); -} - - -bool LtsScreenNormal::isEmpty() const -{ - return m_childContainer->isEmpty(); -} - - -void LtsScreenNormal::switchToTemp(LtsScreenBase::WidgetType widgetType) -{ - m_childContainer->switchToTemp(widgetType); -} - - -void LtsScreenNormal::finalizeSwitch() -{ - m_childContainer->finalizeSwitch(); -} - - -void LtsScreenNormal::revertSwitch() -{ - m_childContainer->revertSwitch(); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_normal.h b/Lts/Common/LtsMultiMonitor/lts_screen_normal.h deleted file mode 100755 index cae9373aa..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_normal.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "lts_screen_base.h" - -#include - -#include -#include - - -class QSplitter; -class QHBoxLayout; - - -namespace LtsMultiMonitor -{ -class LtsMagicCorner; - - -class LTSMULTIMONITOR_EXPORT LtsScreenNormal : public LtsScreenBase -{ - Q_OBJECT - -public: - LtsScreenNormal(QRect screenGeo, int screenId, bool debugMargins, const QString &serial); - ~LtsScreenNormal(); - int screenType() const override { return 0; } - int containerIdOfWidget(QWidget *widget) const noexcept(false) override; - LtsScreenContainer *child() const { return m_childContainer; } - LtsScreenContainer *putWidget(QWidget *widget, int) noexcept(false) override; - LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) override; - void hideMagicCorner(bool hide); - bool isEmpty() const override; - bool isIrreplaceable() const override; - void putWidgetUnmanaged(QWidget *widget); - bool hasWidget(QWidget *widget) const override; - void switchToTemp(WidgetType widgetType) override; - void finalizeSwitch() override; - void revertSwitch() override; - void check() override; - -private: - LtsScreenContainer *m_childContainer { nullptr }; - LtsMagicCorner *m_corner; - - - void swapLeftRight(); - bool tryPutDummyUnmanaged(QWidget *widget); - -signals: - void signalMakeAllSwole(); - void signalMakeSwole(); - -private slots: - void slotOverlayDestroyed(); - void setCornerIcons(); -}; -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp b/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp deleted file mode 100755 index 9116442c1..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "lts_screen_wide.h" -#include "lts_dummy_widget.h" -#include "lts_screen_container.h" - -#include - -#include -#include -#include -#include -#ifdef Q_OS_WIN -#include -#endif - -namespace LtsMultiMonitor -{ - -LtsScreenWide::LtsScreenWide(const QRect &screenGeo, int screenId, bool debugMargins, const QString &serial) - : LtsScreenBase(screenGeo, screenId, debugMargins, serial) - , m_childContainerMain(new LtsScreenContainer(screenGeo)) - , m_childContainerLeft(new LtsScreenContainer(screenGeo)) - , m_childContainerRight(new LtsScreenContainer(screenGeo)) -{ - - connect(m_childContainerMain, &LtsScreenContainer::check, - this, &LtsScreenWide::check); - connect(m_childContainerLeft, &LtsScreenContainer::check, - this, &LtsScreenWide::check); - connect(m_childContainerRight, &LtsScreenContainer::check, - this, &LtsScreenWide::check); - - auto *theLayout = new QHBoxLayout; - - theLayout->setContentsMargins(0, 0, 0, 0); - theLayout->setMargin(0); - theLayout->setSpacing(0); - theLayout->setStretch(0, 9999); - - m_splitter = new QSplitter; - m_splitter->setContentsMargins(0, 0, 0, 0); - - m_splitter->addWidget(m_childContainerLeft); - m_splitter->addWidget(m_childContainerRight); - - theLayout->addWidget(m_splitter); - theLayout->addWidget(m_childContainerMain); - - m_screenDialog->setLayout(theLayout); - - m_splitter->hide(); -} - - -void LtsScreenWide::putWidgetUnmanagedMain(QWidget *widget) -{ - - m_childContainerMain->setWidget(widget); -} - - -void LtsScreenWide::putWidgetUnmanagedRight(QWidget *widget) -{ - - m_childContainerRight->setWidget(widget); -} - - -void LtsScreenWide::putWidgetUnmanagedLeft(QWidget *widget) -{ - - m_childContainerLeft->setWidget(widget); -} - - -bool LtsScreenWide::hasWidget(QWidget *widget) const -{ - return (m_childContainerMain->hasWidget(widget) || - m_childContainerLeft->hasWidget(widget) || - m_childContainerRight->hasWidget(widget)); -} - - -bool LtsScreenWide::isIrreplaceable() const -{ - return (childLeft()->isIrreplaceable() && childRight()->isIrreplaceable() && childMain()->isIrreplaceable()); -} - - -bool LtsScreenWide::isEmpty() const -{ - return (m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty() && m_childContainerMain->isEmpty()); -} - - -int LtsScreenWide::containerIdOfWidget(QWidget *widget) const noexcept(false) -{ - if (m_childContainerMain->hasWidget(widget)) - return 0; - if (m_childContainerLeft->hasWidget(widget)) - return 1; - if (m_childContainerRight->hasWidget(widget)) - return 2; - throw "widget not on this screen"; -} - - -LtsScreenContainer *LtsScreenWide::containerOfWidget(QWidget *widget) noexcept(false) -{ - if (m_childContainerMain->hasWidget(widget)) - return m_childContainerMain; - if (m_childContainerLeft->hasWidget(widget)) - return m_childContainerLeft; - if (m_childContainerRight->hasWidget(widget)) - return m_childContainerRight; - throw "widget is not present on this container"; -} - - -LtsScreenContainer *LtsScreenWide::putWidget(QWidget *widget, int screenPart) noexcept(false) -{ - if (screenPart == -1) - { - if (!isEmpty()) - throw "cannot put widget as at least one container is occupied and screen part was not specified"; - - putWidgetUnmanagedMain(widget); - return m_childContainerMain; - } - - switch (screenPart) - { - case 0: - { - if (!m_childContainerMain->isEmpty()) - throw "cannot put widget as main container is already occupied"; - - putWidgetUnmanagedMain(widget); - return m_childContainerMain; - } - - case 1: - { - if (!m_childContainerLeft->isEmpty()) - throw "cannot put widget as left container is already occupied"; - - - - if (!m_childContainerMain->isEmpty()) - putWidgetUnmanagedRight(m_childContainerMain->stealWidget()); - - putWidgetUnmanagedLeft(widget); - return m_childContainerLeft; - } - - case 2: - { - if (!m_childContainerRight->isEmpty()) - throw "cannot put widget as right container is already occupied"; - - - - if (!m_childContainerMain->isEmpty()) - putWidgetUnmanagedLeft(m_childContainerMain->stealWidget()); - - putWidgetUnmanagedRight(widget); - return m_childContainerRight; - } - - default: - { - throw "unknown screen part id"; - } - } -} - - -void LtsScreenWide::check() -{ - - - - - - Q_ASSERT( (!m_childContainerMain->isEmpty() && m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty()) || m_childContainerMain->isEmpty() ); - - - - if (m_childContainerMain->isEmpty()) - { - if (m_childContainerLeft->isEmpty() && !m_childContainerRight->isEmpty()) - { - try - { - QWidget *widget = m_childContainerRight->stealWidget(); - m_childContainerMain->setWidget(widget); - } - catch(...) - { - - - } - } - else if (!m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty()) - { - try - { - QWidget *widget = m_childContainerLeft->stealWidget(); - m_childContainerMain->setWidget(widget); - } - catch(...) - { - - - } - } - } - - - - if (!m_childContainerMain->isEmpty()) - { - - m_childContainerMain->show(); - m_splitter->hide(); - return; - } - - - - m_childContainerMain->hide(); - m_splitter->setVisible(!m_childContainerLeft->isEmpty() || !m_childContainerRight->isEmpty()); - m_childContainerLeft->setVisible(!m_childContainerLeft->isEmpty()); - m_childContainerRight->setVisible(!m_childContainerRight->isEmpty()); -} - -} diff --git a/Lts/Common/LtsMultiMonitor/lts_screen_wide.h b/Lts/Common/LtsMultiMonitor/lts_screen_wide.h deleted file mode 100755 index dfe91d89a..000000000 --- a/Lts/Common/LtsMultiMonitor/lts_screen_wide.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "lts_screen_base.h" - -#include - -#include -#include - - -class QSplitter; -class QHBoxLayout; - - -namespace LtsMultiMonitor -{ - -class LTSMULTIMONITOR_EXPORT LtsScreenWide : public LtsScreenBase -{ - Q_OBJECT - -public: - LtsScreenWide(const QRect &screenGeo, int screenId, bool debugMargins, const QString &serial); - - int screenType() const override { return 1; } - LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) override; - bool isEmpty() const override; - LtsScreenContainer *childLeft() const { return m_childContainerLeft; } - LtsScreenContainer *childRight() const { return m_childContainerRight; } - LtsScreenContainer *childMain() const { return m_childContainerMain; } - void putWidgetUnmanagedMain(QWidget *widget); - void putWidgetUnmanagedLeft(QWidget *widget); - void putWidgetUnmanagedRight(QWidget *widget); - bool isIrreplaceable() const override; - bool hasWidget(QWidget *widget) const override; - LtsScreenContainer *putWidget(QWidget *widget, int screenPart) noexcept(false) override; - int containerIdOfWidget(QWidget *widget) const noexcept(false) override; - -public slots: - void check() override; - -private: - LtsScreenContainer *m_childContainerMain; - LtsScreenContainer *m_childContainerLeft; - LtsScreenContainer *m_childContainerRight; - QSplitter *m_splitter; - - void swapLeftRight(); -}; - -} diff --git a/Lts/Common/LtsNotification/CMakeLists.txt b/Lts/Common/LtsNotification/CMakeLists.txt deleted file mode 100755 index e995fa32c..000000000 --- a/Lts/Common/LtsNotification/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-01-13 -# - - -SET(LtsNoti_SRCS - lts_notification.h - lts_notification.cpp - lts_notification_ctl.h - lts_notification_ctl.cpp - - lts_notification_export.h -) - -NGL_ADD_LIBRARY(WS LtsNotification SHARED ${LtsNoti_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsNotification - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsNotification - Qt5::Widgets -) - -NGL_INSTALL_TARGET(LtsNotification) diff --git a/Lts/Common/LtsNotification/lts_notification.cpp b/Lts/Common/LtsNotification/lts_notification.cpp deleted file mode 100755 index aae0c0465..000000000 --- a/Lts/Common/LtsNotification/lts_notification.cpp +++ /dev/null @@ -1,92 +0,0 @@ - - -#include "lts_notification.h" -#include "lts_notification_ctl.h" - -#include - - -namespace LtsNotification -{ - -LtsNotification::LtsNotification(LtsNotificationController *ctl) - : QObject() - , m_controller(ctl) -{ - connect(ctl, &LtsNotificationController::destroyed, - this, &LtsNotification::slotCtlDestroyed); -} - - -LtsNotification::~LtsNotification() -{ - delete m_controller; -} - - -void LtsNotification::setIcon(const QIcon& icon) -{ - m_controller->setIcon(icon); -} - - -void LtsNotification::setAction(const std::function &action) -{ - m_controller->setAction(action); -} - - -void LtsNotification::setToolTip(const QString &toolTip) -{ - m_controller->setToolTip(toolTip); -} - - -const QString &LtsNotification::tooltip() const -{ - return m_controller->tooltip(); -} - - -void LtsNotification::setCount(const int count) -{ - m_controller->setCount(count); -} - -void LtsNotification::setEnabled(bool enable) -{ - m_controller->setEnabled(enable); -} - -void LtsNotification::setBackground(const int r, const int g, const int b) -{ - m_controller->setBackground(r, g, b); -} - - - - - - - - - - - - - - - - - - - - -void LtsNotification::slotCtlDestroyed(QObject *obj) -{ - (void)obj; - - m_controller = nullptr; -} - -} diff --git a/Lts/Common/LtsNotification/lts_notification.h b/Lts/Common/LtsNotification/lts_notification.h deleted file mode 100755 index 05be741ca..000000000 --- a/Lts/Common/LtsNotification/lts_notification.h +++ /dev/null @@ -1,57 +0,0 @@ - - -#pragma once - -#include "lts_notification_export.h" - -#include -#include -#include -#include - - -namespace LtsSession -{ -class LtsSession; -} - - -namespace LtsNotification -{ -class LtsNotificationController; - - - -class LTSNOTIFICATION_EXPORT LtsNotification : public QObject -{ - Q_OBJECT - -public: - - explicit LtsNotification(LtsNotificationController *ctl); - ~LtsNotification(); - - void setIcon(const QIcon &icon); - void setAction(const std::function &action); - void setToolTip(const QString &toolTip); - void setCount(const int count); - void setEnabled(bool enable); - void setBackground(const int r, const int g, const int b); - - const QString &tooltip() const; - - - - - -private: - LtsNotificationController *m_controller; - - - -private slots: - void slotCtlDestroyed(QObject *obj); - -}; - -} diff --git a/Lts/Common/LtsNotification/lts_notification_ctl.cpp b/Lts/Common/LtsNotification/lts_notification_ctl.cpp deleted file mode 100755 index 3a35ed5a9..000000000 --- a/Lts/Common/LtsNotification/lts_notification_ctl.cpp +++ /dev/null @@ -1,172 +0,0 @@ - - -#include "lts_notification_ctl.h" - -#include -#include -#include -#include -#include -#include - - -namespace LtsNotification -{ - -LtsNotificationController::LtsNotificationController(const QString &identifier) - : QObject() - , m_count(0) - , m_identifier(QStringLiteral("LtsNotification%1").arg(identifier)) -{} - - -LtsNotificationController::~LtsNotificationController() -{ - qDeleteAll(m_notiList); -} - - -void LtsNotificationController::setIcon(const QIcon& icon) -{ - m_icon = icon; - - setAllIcons(); -} - - -void LtsNotificationController::setCount(const int count) -{ - m_count = count; - - setAllIcons(); -} - - - -void LtsNotificationController::setBackground(const int r, const int g, const int b) -{ - - for (QToolButton *tb : qAsConst(m_notiList)) - { - - QString stylesheet; - - if(r == -1 && g == -1 && b == -1) - stylesheet = QStringLiteral(""); - else - stylesheet = QStringLiteral("QToolButton {background-color: rgb(") + - QString::number(r) + QStringLiteral(",") + - QString::number(g) + QStringLiteral(",") + - QString::number(b) + QStringLiteral(");}"); - - tb->setStyleSheet(stylesheet); - } -} - -void LtsNotificationController::setEnabled(bool enable) -{ - m_enable = enable; - - setAllIcons(); -} - - -void LtsNotificationController::setAction(const std::function &action) -{ - m_action = action; -} - - -void LtsNotificationController::setToolTip(const QString &toolTip) -{ - m_toolTip = toolTip; - - for (QToolButton *tb : qAsConst(m_notiList)) - { - tb->setToolTip(toolTip); - tb->setIconSize(QSize(30,30)); - } -} - - -void LtsNotificationController::setAllIcons() -{ - if (m_icon.isNull()) - return; - - - QIcon icon = modifyIcon(m_icon); - - for (QToolButton *tb : qAsConst(m_notiList)) - { - tb->setIcon(icon); - tb->setIconSize(QSize(30,30)); - tb->setEnabled(m_enable); - } -} - - -#define SIZE 40 - -QIcon LtsNotificationController::modifyIcon(const QIcon &icon) -{ - - - - if (m_count < 1) - return icon; - - QPixmap iconImage(icon.pixmap(SIZE * 8 / 10, SIZE * 8 / 10)); - QPainter painter(&iconImage); - - painter.save(); - painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform); - - QFont f; - f.setFamily(QStringLiteral("Monospace")); - f.setPointSize(16); - f.setBold(true); - painter.setFont(f); - - QColor color(Qt::red); - painter.setBrush(color); - painter.setPen(QPen(color, 3.1)); - painter.drawText(QRect(0, 0, 30, 30), QString::number(m_count)); - painter.restore(); - - return iconImage; -} - - -QToolButton *LtsNotificationController::createWidget() -{ - QToolButton *b = new QToolButton(); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setObjectName(m_identifier); - - - - b->setMinimumSize(SIZE, SIZE); - b->setMaximumSize(SIZE, SIZE); - - connect(b, &QToolButton::destroyed, - this, &LtsNotificationController::slotNotiWidgetDestroyed); - connect(b, &QToolButton::clicked, - this, [this](){ if (m_action) m_action(); }); - - if (!m_icon.isNull()) - b->setIcon(modifyIcon(m_icon)); - - b->setToolTip(m_toolTip); - - m_notiList.append(b); - return b; -} - - -void LtsNotificationController::slotNotiWidgetDestroyed(QObject *obj) -{ - m_notiList.removeAll(static_cast(obj)); -} - -} diff --git a/Lts/Common/LtsNotification/lts_notification_ctl.h b/Lts/Common/LtsNotification/lts_notification_ctl.h deleted file mode 100755 index a179afccf..000000000 --- a/Lts/Common/LtsNotification/lts_notification_ctl.h +++ /dev/null @@ -1,67 +0,0 @@ - - -#pragma once - -#include "lts_notification_export.h" - -#include -#include -#include -#include -#include - - -class QToolButton; - -namespace LtsSession -{ -class LtsSession; -} - - -namespace LtsNotification -{ -class LtsNotificationWidget; - - - -class LTSNOTIFICATION_EXPORT LtsNotificationController : public QObject -{ - Q_OBJECT - -public: - explicit LtsNotificationController(const QString &identifier); - ~LtsNotificationController(); - - QToolButton *createWidget(); - - void setIcon(const QIcon& icon); - void setAction(const std::function &action); - void setToolTip(const QString &toolTip); - void setCount(const int count); - void setBackground(const int r, const int g, const int b); - const QString &identifier() const { return m_identifier; } - const QString &tooltip() const { return m_toolTip; } - - void setEnabled(bool enable); - -private: - QList m_notiList; - - - QIcon m_icon; - std::function m_action; - QString m_toolTip; - int m_count; - bool m_enable{true}; - QString m_identifier; - - void setAllIcons(); - QIcon modifyIcon(const QIcon &icon); - -private slots: - void slotNotiWidgetDestroyed(QObject *obj); - -}; - -} diff --git a/Lts/Common/LtsNotification/lts_notification_export.h b/Lts/Common/LtsNotification/lts_notification_export.h deleted file mode 100755 index 92853b0c3..000000000 --- a/Lts/Common/LtsNotification/lts_notification_export.h +++ /dev/null @@ -1,19 +0,0 @@ - - - -#pragma once - -#include - -#ifndef LTSNOTIFICATION_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSNOTIFICATION_EXPORT -# else -# if defined(MAKE_LTSNOTIFICATION_LIB) -# define LTSNOTIFICATION_EXPORT Q_DECL_EXPORT -# else -# define LTSNOTIFICATION_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - diff --git a/Lts/Common/LtsPluginManager/CMakeLists.txt b/Lts/Common/LtsPluginManager/CMakeLists.txt deleted file mode 100755 index 4cbbc180f..000000000 --- a/Lts/Common/LtsPluginManager/CMakeLists.txt +++ /dev/null @@ -1,65 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-05-20 -# - - -# LtsPluginLoader -NGL_ADD_LIBRARY(ALL LtsPluginLoader SHARED lts_plugin_loader.cpp lts_plugin_loader.h) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsPluginLoader - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsPluginLoader - PRIVATE - NGLLogger - PUBLIC - Qt5::Core -) - -# LtsPluginManager -SET(ltspluginmanager_SRCS - lts_plugin_manager.h - lts_plugin_manager.cpp - Ui/lts_plugin_manager_dialog.h - Ui/lts_plugin_manager_dialog.cpp - Ui/lts_plugin_manager_widget.h - Ui/lts_plugin_manager_widget.cpp - - lts_plugin_manager_export.h -) - -NGL_WRAP_UI(ltspluginmanager_SRCS - Ui/lts_plugin_manager_dialog.ui - Ui/lts_plugin_manager_widget.ui -) - -NGL_ADD_LIBRARY(WS LtsPluginManager SHARED ${ltspluginmanager_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsPluginManager - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsPluginManager - PRIVATE - NGLLogger - LtsPluginLoader - GuiPluginInterface - DataPluginInterface - LtsWidgets - LtsSession - LtsSessionData - Qt5::Widgets - PUBLIC - ProjectConfig - LtsAppDescriptor - Qt5::Core -) - -NGL_INSTALL_TARGET(LtsPluginManager) - -NGL_INSTALL_TARGET(LtsPluginLoader) diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp deleted file mode 100755 index def518bb4..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp +++ /dev/null @@ -1,102 +0,0 @@ - - - -#include "lts_plugin_manager_dialog.h" -#include "lts_plugin_manager_widget.h" -#include "ui_lts_plugin_manager_dialog.h" -#include "../lts_plugin_manager.h" - -#include -#include -#include - - -namespace LtsPluginManager -{ - -LtsPluginManagerDialog::LtsPluginManagerDialog(QWidget *parent) - : QDialog(parent) - , m_ui(new Ui::LtsPluginManagerDialog) -{ - m_ui->setupUi(this); - - - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->pluginsWidget), 20); - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->lwErrorLog), 0); -} - - -LtsPluginManagerDialog::~LtsPluginManagerDialog() -{ - delete m_ui; -} - - -void LtsPluginManagerDialog::setServiceMissing(const QString &newPluginFilename, const QString &missingPluginName) -{ - m_ui->lwErrorLog->addItem(tr("ERROR: Plugin '%1' needs the plugin '%2', which is not available!").arg(newPluginFilename, missingPluginName)); - getPluginWidget(newPluginFilename)->setStatus(LtsPluginWidget::MISSING_SERVICE); - - if (!isVisible()) - show(); - - QCoreApplication::processEvents(); -} - - -void LtsPluginManagerDialog::setOptionalServiceMissing(const QString &newPluginFilename, const QString &missingPluginName) -{ - m_ui->lwErrorLog->addItem(tr("INFO: Plugin %1 asked for plugin %2, which is not available!").arg(newPluginFilename, missingPluginName)); - - if (!isVisible()) - show(); - - QCoreApplication::processEvents(); -} - - -void LtsPluginManagerDialog::setInvalidPlugin(const QString &newPluginFilename) -{ - m_ui->lwErrorLog->addItem(tr("INFO: Invalid plugin '%1'").arg(newPluginFilename)); -} - - -LtsPluginWidget *LtsPluginManagerDialog::getPluginWidget(const QString &filename) -{ - LtsPluginWidget *pluginWidget = findChild(filename); - - if (!pluginWidget) - { - pluginWidget = new LtsPluginWidget(filename); - pluginWidget->setObjectName(filename); - qobject_cast(m_ui->pluginsWidget->layout())->insertWidget(0, pluginWidget); - } - - return pluginWidget; -} - - -void LtsPluginManagerDialog::setPluginLogStatus(const QString &filename, const LtsPluginWidget::Status &status) -{ - getPluginWidget(filename)->setStatus(status); - - QCoreApplication::processEvents(); -} - - -void LtsPluginManagerDialog::setPluginLogType(const QString &filename, const QString &type) -{ - getPluginWidget(filename)->setType(type); - - QCoreApplication::processEvents(); -} - - -void LtsPluginManagerDialog::setPluginLogIdent(const QString &filename, const QString &ident) -{ - getPluginWidget(filename)->setIdent(ident); - - QCoreApplication::processEvents(); -} - -} diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h deleted file mode 100755 index 41c187195..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h +++ /dev/null @@ -1,38 +0,0 @@ - - -#pragma once - -#include -#include "lts_plugin_manager_widget.h" - -#include - - -namespace Ui -{ -class LtsPluginManagerDialog; -} - -namespace LtsPluginManager -{ -class LTSPLUGINMANAGER_EXPORT LtsPluginManagerDialog : public QDialog -{ - Q_OBJECT -public: - explicit LtsPluginManagerDialog(QWidget *parent = nullptr); - ~LtsPluginManagerDialog(); - - void setServiceMissing(const QString &newPluginFilename, const QString &missingPluginName); - void setOptionalServiceMissing(const QString &newPluginFilename, const QString &missingPluginName); - void setInvalidPlugin(const QString &newPluginFilename); - - void setPluginLogStatus(const QString &filename, const LtsPluginWidget::Status &status); - void setPluginLogType(const QString &filename, const QString &type); - void setPluginLogIdent(const QString &filename, const QString &ident); - -private: - Ui::LtsPluginManagerDialog *m_ui; - - LtsPluginWidget *getPluginWidget(const QString &filename); -}; -} diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui deleted file mode 100755 index 1afb9c845..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - LtsPluginManagerDialog - - - - 0 - 0 - 492 - 342 - - - - - 0 - 0 - - - - true - - - - - - Qt::Vertical - - - - - 0 - 0 - - - - - 0 - 250 - - - - - - - Qt::Horizontal - - - - 493 - 20 - - - - - - - - - - 0 - 0 - - - - - - - - - - diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp deleted file mode 100755 index f575f4d89..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp +++ /dev/null @@ -1,130 +0,0 @@ - - - -#include "lts_plugin_manager_widget.h" -#include "ui_lts_plugin_manager_widget.h" - -using namespace LtsPluginManager; - -LtsPluginWidget::LtsPluginWidget(const QString &filename, QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::LtsPluginWidget) -{ - m_ui->setupUi(this); - - m_ui->leFilename->setText(filename); - setStatus(UNKNOWN); - setType(); - setIdent(); - - - m_ui->lbGuiPlugins->hide(); - - m_ui->lbDtc->hide(); - m_ui->lwDtcs->hide(); - - m_ui->lbFtc->hide(); - m_ui->lwFtcs->hide(); -} - -LtsPluginWidget::~LtsPluginWidget() -{ - delete m_ui; -} - -void LtsPluginWidget::setStatus(Status status) -{ - QString statusText; - - switch (status) - { - case INVALID: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("Invalid"); - break; - } - case LOADED: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(124, 189, 124);")); - statusText = tr("Loaded"); - break; - } - case CONFLICT: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("CONFLICT!"); - break; - } - case UNLOADED: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(100, 100, 100);")); - statusText = tr("Unloaded"); - break; - } - case INIT_FAILED: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("INIT FAILED!"); - break; - } - case MISSING_SERVICE: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("MISSING SERVICE!"); - break; - } - case OBLIBATORY_INTERFACES_MISSING: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("OBLIGATORY INTERFACES MISSING!"); - break; - } - case INVALID_PLUGIN_VERSION: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(189, 106, 98);")); - statusText = tr("INVALID PLUGIN VERSION!"); - break; - } - default: - { - m_ui->frStatus->setStyleSheet(QLatin1String("background-color: rgb(100, 100, 100);")); - statusText = tr("Unknown (%1)").arg(status); - break; - } - } - - m_ui->leStatus->setText(statusText); -} - - -void LtsPluginWidget::setType(const QString &type) -{ - if (type.isEmpty()) - { - m_ui->leType->hide(); - m_ui->lbType->hide(); - } - else - { - m_ui->leType->show(); - m_ui->lbType->show(); - m_ui->leType->setText(type); - } -} - - -void LtsPluginWidget::setIdent(const QString &ident) -{ - if (ident.isEmpty()) - { - m_ui->leIdent->hide(); - m_ui->lbIdent->hide(); - } - else - { - m_ui->leIdent->show(); - m_ui->lbIdent->show(); - m_ui->leIdent->setText(ident); - } -} diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h deleted file mode 100755 index cf0826776..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h +++ /dev/null @@ -1,45 +0,0 @@ - - - -#pragma once - - -#include -#include - - -namespace Ui -{ -class LtsPluginWidget; -} - -namespace LtsPluginManager -{ -class LtsPluginWidget : public QWidget -{ - Q_OBJECT -public: - enum Status - { - UNKNOWN = 0, - INVALID, - LOADED, - CONFLICT, - UNLOADED, - INIT_FAILED, - MISSING_SERVICE, - OBLIBATORY_INTERFACES_MISSING, - INVALID_PLUGIN_VERSION, - }; - - explicit LtsPluginWidget(const QString &filename, QWidget *parent = nullptr); - ~LtsPluginWidget(); - - void setStatus(Status status); - void setType(const QString &type = QString()); - void setIdent(const QString &ident = QString()); - -private: - Ui::LtsPluginWidget *m_ui; -}; -} diff --git a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui b/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui deleted file mode 100755 index 2c93dac33..000000000 --- a/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui +++ /dev/null @@ -1,196 +0,0 @@ - - - LtsPluginWidget - - - - 0 - 0 - 481 - 580 - - - - - 0 - 0 - - - - true - - - - - - - - - 15 - 60 - - - - - 15 - 60 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - - - - - Name: - - - - - - - Type: - - - - - - - Status: - - - - - - - Ident: - - - - - - - Gui Plugins: - - - - - - - - 0 - 0 - - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - Foreign Data Topic Clients: - - - - - - - Foreign Function Topic Clients: - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - - - diff --git a/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp b/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp deleted file mode 100755 index 4a8cc5c1d..000000000 --- a/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp +++ /dev/null @@ -1,449 +0,0 @@ - - - -#include "lts_plugin_loader.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsPluginManager -{ - -struct LtsPluginLoaderHandler -{ - LtsPluginLoader ltsPluginLoader; - static NGL::INGLLogger &logger() { return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsPluginLoader"); } - static void cleanupPluginLoader(); -}; -Q_GLOBAL_STATIC(LtsPluginLoaderHandler, s_ltsPluginLoader) - -void LtsPluginLoaderHandler::cleanupPluginLoader() -{ - if (s_ltsPluginLoader.exists()) - s_ltsPluginLoader->ltsPluginLoader.unloadAllPlugins(); -} - - -LtsPluginLoader *LtsPluginLoader::getInstance() -{ - if (!s_ltsPluginLoader.exists()) - qAddPostRoutine(&LtsPluginLoaderHandler::cleanupPluginLoader); - return &s_ltsPluginLoader->ltsPluginLoader; -} - - -LtsPluginLoader::~LtsPluginLoader() -{ - unloadAllPlugins(); -} - -void LtsPluginLoader::unloadAllPlugins() -{ - for (auto &pi : qAsConst(m_loadedPlugins)) - { - QPluginLoader *pluginLoader = pi.pluginLoader; - - delete pluginLoader->instance(); - - if (!pluginLoader->unload()) - qWarning() << "~LtsPluginLoader: could not unload" << pi.pluginName << pluginLoader->errorString(); - - delete pluginLoader; - } - m_loadedPlugins.clear(); -} - - -void LtsPluginLoader::checkPlugins(const QVector &appVector, - QStringList &errors ) -{ - NGL::INGLLogger &logger = LtsPluginLoaderHandler::logger(); - - if (appVector.isEmpty()) - { - errors += QStringLiteral("No Apps available"); - return; - } - - - QVector dataPlugins; - QVector guiPlugins; - auto addDataPluginFn = [&](const PluginInstance &pi) - { - dataPlugins.append(pi); - return true; - }; - auto addGuiPluginFn = [&](const PluginInstance &pi) - { - guiPlugins.append(pi); - return true; - }; - - if (!loadPlugins(PluginType::DATA, addDataPluginFn)) - { - errors += QStringLiteral("failed to load data plugins"); - return; - } - - if (!loadPlugins(PluginType::GUI, addGuiPluginFn)) - { - errors += QStringLiteral("failed to load gui plugins"); - return; - } - - - - - auto checkDuplicateIidsFn = [&](const QVector &plugins, const QString &pluginType) - { - typedef QMap IidMap; - IidMap iids; - - for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3) - { - const PluginInstance &pin = *i3; - auto iidFound = iids.constFind(pin.pluginName); - - if (iidFound != iids.constEnd()) - errors += QStringLiteral("Iid duplicate for %1 plugin '%2'").arg(pluginType, pin.pluginName); - else - iids.insert(pin.pluginName, pin.fullFileName); - } - }; - - checkDuplicateIidsFn(dataPlugins, QStringLiteral("data")); - checkDuplicateIidsFn(guiPlugins, QStringLiteral("gui")); - - - - QVector plugins(dataPlugins + guiPlugins); - - - - for (auto i1 = appVector.constBegin(); i1 != appVector.constEnd(); ++i1) - { - const LtsAppDescriptor::AppDescription &app = *i1; - - - - const QVector &piVector = app.pluginInfos; - - for (auto i2 = piVector.constBegin(); i2 != piVector.constEnd(); i2++) - { - const LtsAppDescriptor::PluginInfo &pi = *i2; - bool found = false; - - for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3) - { - const PluginInstance &pin = *i3; - - if (pi.iid == pin.pluginName) - { - found = true; - break; - } - } - - if (!found) - { - ERROR_LOG(logger) << "Plugin with iid '" << pi.iid << "' was requested but was not loaded!"; - errors += QStringLiteral("App '%1' requires plugin '%2' that could not be found.").arg(app.appName, pi.iid); - } - } - } - - return; -} - - -bool LtsPluginLoader::loadPlugins(PluginType requestedPluginType, - const std::function &loadPlugin, - const QStringList &requiredPlugins, - const QString &specialTopicTypeFilter, - const QStringList &optionalPlugins) -{ - NGL::INGLLogger &logger = LtsPluginLoaderHandler::logger(); - Q_ASSERT(requestedPluginType != PluginType::UNKNOWN); - - QStringList requiredPluginsList = requiredPlugins; - QStringList optionalPluginsList = optionalPlugins; - bool checkPluginsLists = (!requiredPluginsList.isEmpty()) || (!optionalPluginsList.isEmpty()); - - if (checkPluginsLists && !m_loadedPlugins.isEmpty()) - { - { - QMutableListIterator i1(requiredPluginsList); - - while (i1.hasNext()) - { - const QString pluginName = i1.next().toLower(); - - QMap::iterator i2 = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName)); - - if (i2 != m_loadedPlugins.end()) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance"; - - if (loadPlugin) - loadPlugin(i2.value()); - - i1.remove(); - } - } - } - - - { - QMutableListIterator i1(optionalPluginsList); - - while (i1.hasNext()) - { - const QString pluginName = i1.next().toLower(); - - QMap::iterator i2 = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName)); - - if (i2 != m_loadedPlugins.end()) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance"; - - if (loadPlugin) - loadPlugin(i2.value()); - - i1.remove(); - } - } - } - } - - - QFileInfo libPathInfo(QCoreApplication::applicationDirPath() + QStringLiteral("/../lib/")); - QString libPath(libPathInfo.absolutePath()); - if (!libPathInfo.exists()) - { - - - libPathInfo.setFile(QCoreApplication::applicationFilePath()); - libPath = libPathInfo.absolutePath(); - } - -#ifdef Q_OS_WIN - const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.dll")); -#else - const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.so")); -#endif - - static const QStringList pluginTypes({QStringLiteral("data"), - QStringLiteral("gui"), - QStringLiteral("topic_type"), - QStringLiteral("project"), - QStringLiteral("style"), - QStringLiteral("special")}); - - QDirIterator pluginDir(libPath, nameFilter, QDir::Files); - while (pluginDir.hasNext()) - { - pluginDir.next(); - const QString filename = pluginDir.fileName(); - if (filename.contains(QStringLiteral("PluginInterface"), Qt::CaseSensitive)) - continue; - - const QString absoluteFilePath = pluginDir.filePath(); - - DEBUG_LOG(logger) << "File Path: " << absoluteFilePath; - - - - - - - - - QScopedPointer pluginLoader(new QPluginLoader); - pluginLoader->setFileName(absoluteFilePath); - - const QJsonObject jObj = pluginLoader->metaData(); - QJsonObject::const_iterator iid = jObj.find(QStringLiteral("IID")); - - if (iid == jObj.end()) - { - DEBUG_LOG(logger) << "Plugin '" << filename << "' has no IID !"; - continue; - } - - const QString jsonIid = iid.value().toString().toLower(); - const QVector iidParameters = jsonIid.splitRef(QLatin1Char(':')); - - DEBUG_LOG(logger) << "Plugin " << absoluteFilePath << " provides iid '" << jsonIid; - - if (iidParameters.count() != 3) - { - WARNING_LOG(logger) << "Invalid iid "< i (requiredPluginsList); - - while (i.hasNext()) - { - const QString requiredPlugin = i.next().toLower(); - - if (pluginName == requiredPlugin) - { - found = true; - i.remove(); - break; - } - } - } - - - if (!found) - { - for (auto i = optionalPluginsList.constBegin(); i != optionalPluginsList.constEnd(); i++) - { - const QString optionalPlugin = (*i).toLower(); - - if (pluginName == optionalPlugin) - { - found = true; - break; - } - } - } - - - if (!found) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " was not requested"; - continue; - } - } - - - - - if (loadPlugin && !m_loadedPlugins.isEmpty()) - { - QMap::iterator i = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName)); - - if (i != m_loadedPlugins.end()) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance ("<metaObject()->className()<<")"; - - loadPlugin(i.value()); - continue; - } - } - - - QObject *plugin = pluginLoader->instance(); - - if (!plugin) - { - ERROR_LOG(logger) << "Failed to load " << filename << " - " << pluginLoader->errorString(); - continue; - } - - PluginInstance pi; - pi.pluginName = pluginName; - pi.fileName = filename; - pi.fullFileName = absoluteFilePath; - pi.plugin = plugin; - pi.pluginLoader = pluginLoader.data(); - - if (loadPlugin && !loadPlugin(pi)) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " unloaded"; - delete pluginLoader->instance(); - pluginLoader->unload(); - continue; - } - pi.pluginLoader = pluginLoader.take(); - - - m_loadedPlugins.insert(PluginKey(requestedPluginType, pluginName), pi); - - - - - DEBUG_LOG(logger) << "Loaded " << jsonIid << " - File: " << filename; - } - - - if (checkPluginsLists && !requiredPluginsList.isEmpty()) - { - WARNING_LOG(logger) << "These plugins could not be found for Plugin Type " << (int)requestedPluginType << ": " << requiredPluginsList.join(QStringLiteral(", ")); - return false; - } - - return true; -} - -} diff --git a/Lts/Common/LtsPluginManager/lts_plugin_loader.h b/Lts/Common/LtsPluginManager/lts_plugin_loader.h deleted file mode 100755 index 9881bcefd..000000000 --- a/Lts/Common/LtsPluginManager/lts_plugin_loader.h +++ /dev/null @@ -1,85 +0,0 @@ - - - -#pragma once - -#define PLUGIN_LOADER_VERSION 3 - -#include -#include -#include -#include - -#ifndef LTSPLUGINLOADER_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSPLUGINLOADER_EXPORT -# else -# if defined(MAKE_LTSPLUGINLOADER_LIB) -# define LTSPLUGINLOADER_EXPORT Q_DECL_EXPORT -# else -# define LTSPLUGINLOADER_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - -class QObject; -class QPluginLoader; - -namespace LtsAppDescriptor -{ -struct AppDescription; -} - -namespace LtsPluginManager -{ - -enum class PluginType -{ - UNKNOWN = 0, - DATA, - GUI, - TOPIC_TYPE, - PROJECT, - STYLE, - SPECIAL, -}; - - -struct PluginInstance -{ - QString pluginName; - QString fullFileName; - QString fileName; - QObject *plugin = nullptr; - QPluginLoader *pluginLoader = nullptr; -}; - - -class LTSPLUGINLOADER_EXPORT LtsPluginLoader -{ -public: - static LtsPluginLoader *getInstance(); - - void unloadAllPlugins(); - - void checkPlugins(const QVector &appVector, - QStringList &errors ); - - bool loadPlugins(PluginType requestedPluginType, - const std::function &loadPlugin, - const QStringList &requiredPlugins = QStringList(), - const QString &specialTopicTypeFilter = QString(), - const QStringList &optionalPlugins = QStringList()); - - typedef QPair PluginKey; - -private: - friend struct LtsPluginLoaderHandler; - - LtsPluginLoader() = default; - ~LtsPluginLoader(); - - QMap m_loadedPlugins; -}; - -} diff --git a/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp b/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp deleted file mode 100755 index 0b2e3703e..000000000 --- a/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp +++ /dev/null @@ -1,286 +0,0 @@ - - - -#include "lts_plugin_manager.h" -#include "Ui/lts_plugin_manager_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -namespace LtsPluginManager -{ - -LtsPluginManager::LtsPluginManager() - : m_sessionManager(new LtsSession::LtsSessionManager) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsPluginManager")) - , m_markedForDeletion(false) -{ - connect(m_sessionManager, &LtsSession::LtsSessionManager::signalQuit, - this, &LtsPluginManager::signalQuit); - connect(m_sessionManager, &LtsSession::LtsSessionManager::signalLogout, - this, &LtsPluginManager::signalLogout); - connect(m_sessionManager, &LtsSession::LtsSessionManager::signalInitializationFinished, - this, &LtsPluginManager::signalInitializationFinished); - connect(m_sessionManager, &LtsSession::LtsSessionManager::signalInitializationAborted, - this, &LtsPluginManager::signalInitializationAborted); - connect(m_sessionManager, &LtsSession::LtsSessionManager::signalInitializationFinished, - this, &LtsPluginManager::slotInitializationFinished); - connect(m_sessionManager, &LtsSession::LtsSessionManager::taskSwitch, - this, &LtsPluginManager::taskSwitch); - connect(m_sessionManager, &LtsSession::LtsSessionManager::hideAllOtherBLWs, - this, &LtsPluginManager::hideAllOtherBLWs); - - QTimer *t = new QTimer(this); - t->setInterval(1000); - connect(t, &QTimer::timeout, - this, &LtsPluginManager::signalStillAlive); - t->start(); -} - - -LtsPluginManager::~LtsPluginManager() -{ - - - delete m_sessionManager; -} - - -void LtsPluginManager::softQuit() -{ - emit signalSoftQuit(); -} - - -void LtsPluginManager::_softQuit() -{ - m_sessionManager->softQuit(); -} - - -QVector LtsPluginManager::sessions() const -{ - return m_sessionManager->sessions(); -} - -bool LtsPluginManager::hasSituationMapInstance(const LtsAppDescriptor::AppDescription &appDescription, bool alsoTestForScriptApp) -{ - if (alsoTestForScriptApp && appDescription.id == LtsAppDescriptor::App::Scripts) - return true; - - for (const LtsAppDescriptor::PluginInfo &pluginInfo : qAsConst(appDescription.pluginInfos)) - { - if (pluginInfo.flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS) && - (pluginInfo.iid == QStringLiteral("sitmap"))) - return true; - } - return false; -} - -bool LtsPluginManager::hasSituationMapInstance() const -{ - return hasSituationMapInstance(m_app); -} - -LtsSession::LtsSession *LtsPluginManager::getSession(IDL::PartitionType partitionType, int partitionId) -{ - return m_sessionManager->getSession(partitionType, partitionId); -} - - -bool LtsPluginManager::startApp(const LtsAppDescriptor::AppDescription &app, bool headless, - const LtsSession::DdsConnectionParams &connParams, const QString &workspaceId, - bool hidden) -{ - - - - LtsWidgets::LtsWaitDialogContainer wait; - - m_app = app; - LtsPluginLoader *pluginLoader = LtsPluginLoader::getInstance(); - - - - QVector dataPlugins; - auto cb = [&](const PluginInstance &pi) - { - dataPlugins.append(pi); - return true; - }; - - if (!pluginLoader->loadPlugins(PluginType::DATA, cb)) - { - WARNING_LOG(m_logger) << "PluginLoader failed to load data plugins - abort!"; - return false; - } - - - - QVector guiPlugins; - auto cbLoadPlugins = [&](const PluginInstance &pi) - { - guiPlugins.append(pi); - return true; - }; - - if (!pluginLoader->loadPlugins(PluginType::GUI, cbLoadPlugins)) - { - WARNING_LOG(m_logger) << "PluginLoader failed to load gui plugins - abort!"; - return false; - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - QVector dataApiList; - for (auto i = dataPlugins.constBegin(); i != dataPlugins.constEnd(); ++i) - { - const PluginInstance &pi = *i; - - AppPluginInstance api; - api.pluginInstance = pi; - api.m_appName = m_app.appName; - api.m_appId = m_app.id; - - - dataApiList.append(api); - } - - - - - QVector guiApiList; - for (auto i = guiPlugins.constBegin(); i != guiPlugins.constEnd(); ++i) - { - const PluginInstance &pi = *i; - - AppPluginInstance api; - api.pluginInstance = pi; - api.m_appName = m_app.appName; - api.m_appId = m_app.id; - - - - for (auto i2 = app.pluginInfos.constBegin(); i2 != app.pluginInfos.constEnd(); ++i2) - { - const LtsAppDescriptor::PluginInfo &rp = *i2; - - - if (rp.type == LtsAppDescriptor::PluginInfo::PluginType::GUI - && rp.iid == pi.pluginName) - { - - api.m_flags = rp.flags; - api.m_options = rp.options; - - break; - } - } - - - - - - - - guiApiList.append(api); - } - - - - - - - - - - LtsSession::DdsConnectionParams connParamsNew(connParams); - if (!connParamsNew.isValid()) - { - INFO_LOG(m_logger) << "Invalid connection params given - using default parameter"; - - connParamsNew.domainId = LtsSessionData::LtsSessionData::getInstance()->getDomainIdPreset(); - connParamsNew.partitionId = 0; - connParamsNew.exerciseId = 0; - connParamsNew.partitionType = IDL::PartitionType::Global; - - - connParamsNew.tcpConnectionUrl = LtsSessionData::LtsSessionData::getInstance()->getTcpConnectionUrl(); - } - if (LtsSessionData::LtsSessionData::getInstance()->getQosFile().size() > 0 && LtsSessionData::LtsSessionData::getInstance()->getQosFile().at(0).size() > 0) - connParamsNew.qos_file = LtsSessionData::LtsSessionData::getInstance()->getQosFile(); - else - connParamsNew.qos_file = QStringList(); - if (!m_sessionManager->init(connParamsNew, - dataApiList, - guiApiList, - m_app.id, - workspaceId, - headless, - hidden)) - { - ERROR_LOG(m_logger) << "Could not create Global Session - abort."; - return false; - } - - return true; -} - -} diff --git a/Lts/Common/LtsPluginManager/lts_plugin_manager.h b/Lts/Common/LtsPluginManager/lts_plugin_manager.h deleted file mode 100755 index bcb09d776..000000000 --- a/Lts/Common/LtsPluginManager/lts_plugin_manager.h +++ /dev/null @@ -1,110 +0,0 @@ - - - -#pragma once - -#include "lts_plugin_manager_export.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - - -namespace NGL -{ -class INGLLogger; -} -namespace DataPluginBase -{ -class Plugin; -} -namespace GuiPluginBase -{ -class Plugin; -} -namespace ProjectPlugin -{ -class Plugin; -} -namespace LtsSession -{ -class LtsSession; -class LtsSessionManager; -struct DdsConnectionParams; -} -namespace DeveloperTools -{ -class DevDialog; -} - - -namespace LtsPluginManager -{ -struct AppPluginInstance -{ - PluginInstance pluginInstance; - - QFlags m_flags; - QStringList m_options; - QString m_appName; - LtsAppDescriptor::App m_appId = LtsAppDescriptor::App::Login; -}; - -class LTSPLUGINMANAGER_EXPORT LtsPluginManager : public QObject -{ - Q_OBJECT - -public: - LtsPluginManager(); - ~LtsPluginManager(); - - bool startApp(const LtsAppDescriptor::AppDescription &app, bool headless, - const LtsSession::DdsConnectionParams &connParams, - const QString &workspaceId = QString(), bool hidden = false); - LtsSession::LtsSession *getSession(IDL::PartitionType partitionType, int partitionId); - bool isInitialized() const { return m_initialized; } - const LtsAppDescriptor::AppDescription &appDescription() const { return m_app; } - QVector sessions() const; - Q_DECL_DEPRECATED void setMarkedForDeletion() { m_markedForDeletion = true; } - Q_DECL_DEPRECATED bool markedForDeletion() const { return m_markedForDeletion; } - const QString &description() const { return m_description; } - void setDescription(const QString &description) { m_description = description; } - - static bool hasSituationMapInstance(const LtsAppDescriptor::AppDescription &appDescription, bool alsoTestFOrScriptApp = false); - bool hasSituationMapInstance() const; - void softQuit(); - void _softQuit(); - - - - LtsSession::LtsSessionManager *m_sessionManager; -private: - friend class DeveloperTools::DevDialog; - - LtsAppDescriptor::AppDescription m_app; - NGL::INGLLogger &m_logger; - bool m_initialized = false; - bool m_markedForDeletion; - QString m_description; - -private slots: - void slotInitializationFinished() { m_initialized = true; } - -signals: - void signalQuit(); - void signalSoftQuit(); - void signalLogout(); - void signalInitializationFinished(); - void signalInitializationAborted(); - void signalStillAlive(); - void hideAllOtherBLWs(QObject *caller); - void taskSwitch(QObject *caller); -}; - -} diff --git a/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h b/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h deleted file mode 100755 index 5d563576f..000000000 --- a/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include - -#ifndef LTSPLUGINMANAGER_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSPLUGINMANAGER_EXPORT -# else -# if defined(MAKE_LTSPLUGINMANAGER_LIB) -# define LTSPLUGINMANAGER_EXPORT Q_DECL_EXPORT -# else -# define LTSPLUGINMANAGER_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif diff --git a/Lts/Common/LtsPrinting/CMakeLists.txt b/Lts/Common/LtsPrinting/CMakeLists.txt deleted file mode 100755 index 0892cb78f..000000000 --- a/Lts/Common/LtsPrinting/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Christian Ehrlicher, SLSE, 2017-12-05 -# - -FIND_PACKAGE(KDReports) - -SET(SRCS - printing_base.cpp - printing_base.h - printing.cpp - printing.h -) - -if (KDReports_FOUND) - NGL_ADD_LIBRARY(WS LtsPrinting SHARED ${SRCS}) - NGL_TARGET_INCLUDE_DIRECTORIES(LtsPrinting - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ) - NGL_TARGET_LINK_LIBRARIES(LtsPrinting - PRIVATE - KDReports::KDReports - LtsSessionData - LtsSession - nglsettings - ngltools - PUBLIC - Qt5::PrintSupport - Qt5::Core - ) - NGL_INSTALL_TARGET(LtsPrinting) -else() - message(STATUS "KDReports not found; skipping LtsPrinting target") -endif() diff --git a/Lts/Common/LtsPrinting/printing.cpp b/Lts/Common/LtsPrinting/printing.cpp deleted file mode 100755 index fbb841b57..000000000 --- a/Lts/Common/LtsPrinting/printing.cpp +++ /dev/null @@ -1,430 +0,0 @@ - - - -#include "printing.h" -#include "printing_base.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsPrinting -{ - - - - -bool itemsHaveNoChildren(const QTreeView *view) -{ - const QAbstractItemModel *itemModel = view->model(); - int rowCount = itemModel->rowCount(); - for (int row = 0; row < rowCount; ++row) - { - if (itemModel->hasChildren(itemModel->index(row, 0))) - return false; - } - return true; -} - - - - -void setNameOfExerciseAndOperator(LtsSession::LtsSession *session, LtsPrinting::Base &nglPrinter) -{ - if ( session->exerciseId() != 0 ) - nglPrinter.setCurrentExerciseName(QObject::tr("Exercise: %1 %2").arg(session->exerciseData()->exName, - session->exerciseData()->phName)); - else - nglPrinter.setCurrentExerciseName(QObject::tr("Exercise: Administration") ); - - LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance(); - nglPrinter.setOperatorName(sessionData->getOperatorName()); -} - - -void printView(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QAbstractItemView *view, - const QString& headingHtml) -{ - const QTableView *tableView = dynamic_cast(view); - const QTreeView *treeView = dynamic_cast(view); - QSet hiddenCols; - - int switchNum = 0; - if (tableView != nullptr) - { - switchNum = 1; - hiddenCols = LtsPrinting::Base::getHiddenCols(tableView); - } - else if (treeView != nullptr) - { - switchNum = 2; - hiddenCols = LtsPrinting::Base::getHiddenCols(treeView); - if (itemsHaveNoChildren(treeView)) switchNum = 1; - } - else if (dynamic_cast(view) != nullptr) - switchNum = 3; - else - { - return; - } - - QTextDocument doc; - QTextCursor cursor(&doc); - - doc.setMetaInformation(QTextDocument::DocumentTitle, dialogParent->windowTitle()); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(dialogParent->windowTitle()), - dialogParent->windowTitle(), - QPrinter::A4, QPrinter::Landscape); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator(session, nglPrinter); - - cursor.movePosition(QTextCursor::End); - QString htmlStr; - - switch (switchNum) - { - case 1: - nglPrinter.printTableData(htmlStr, view, QString(), hiddenCols); - break; - case 2: - nglPrinter.printTreeData(htmlStr, view, QString(), hiddenCols); - break; - case 3: - nglPrinter.printListData(htmlStr, view, QString()); - break; - } - cursor.insertHtml(headingHtml); - cursor.insertHtml(htmlStr); - nglPrinter.print(&doc); - -} - - -void printView(LtsSession::LtsSession *session, - QWidget *dialogParent, - QAbstractItemView *view, - const QString& headingHtml) -{ - const QAbstractItemView *itemView = static_cast(view); - printView(session, dialogParent, itemView, headingHtml); -} - - -void printImage(LtsSession::LtsSession *session, QWidget *dialogParent, - const QImage &image, const QString &imageTitle, const QString &description, - const QPrinter::PageSize &pageSize, const QPrinter::Orientation &pageOrientation) -{ - QTextDocument doc; - QTextCursor cursor(&doc); - - QString windowTitle = dialogParent->windowTitle(); - doc.setMetaInformation(QTextDocument::DocumentTitle, dialogParent->windowTitle()); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(windowTitle), - windowTitle, - pageSize, pageOrientation); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator(session, nglPrinter); - - cursor.movePosition(QTextCursor::End); - - nglPrinter.printDocImage(doc, image, imageTitle, description); - nglPrinter.print(&doc); -} - - -void printImages(LtsSession::LtsSession *session, QWidget *dialogParent, - const QString &docName, QVector &images, - QVector &imageTitles, QVector &descriptions, - const QPrinter::PageSize &pageSize, const QPrinter::Orientation &pageOrientation) - -{ - QTextDocument doc; - QTextCursor cursor(&doc); - - doc.setMetaInformation(QTextDocument::DocumentTitle, docName); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(docName), - docName, pageSize, pageOrientation); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator(session, nglPrinter); - - cursor.movePosition(QTextCursor::End); - - for ( int i = 0; i < images.size(); i++) - { - QString imageTitle, imageDescription; - if (imageTitles.size() > i && imageTitles[i].size() > 0) - imageTitle = imageTitles[i]; - if (descriptions.size() > i && descriptions[i].size() > 0) - imageDescription = descriptions[i]; - nglPrinter.printDocImage(doc, images[i], imageTitle, imageDescription); - nglPrinter.printSinglePage(&doc); - doc.clear(); - - if (i != images.size() -1) - nglPrinter.newPage(); - - } - - nglPrinter.printDone(); - - -} - - -void printPDFImages(QWidget *dialogParent, const QString &docName, const QString &printPath, QVector &images) - -{ - QPrinter printer; - - printer.setColorMode(QPrinter::ColorMode::Color); - printer.setOrientation(QPrinter::Orientation::Portrait); - printer.setPageSize(QPrinter::PageSize::A4); - QString fn = docName; - fn = fn.remove(QLatin1Char(' ')); - fn = fn.remove(QLatin1String("..")); - fn = fn.remove(QLatin1Char('/')); - fn = printPath + fn; - if (!fn.endsWith(QLatin1String(".pdf"), Qt::CaseInsensitive)) - fn += QLatin1String(".pdf"); -#ifndef WIN32 - printer.setOutputFileName(fn); -#endif - printer.setOutputFormat(QPrinter::NativeFormat); - - QPrintDialog *dialog = new QPrintDialog(&printer, dialogParent); - dialog->setWindowTitle(QObject::tr("Print %1").arg(docName)); - if (dialog->exec() != QDialog::Accepted) - return; - - QPainter painter; - painter.begin(&printer); - int lastItem = images.size() - 1; - for (int i = 0; i < images.size(); ++i) - { - QRect rect = painter.viewport(); - QSize size = images.at(i).size(); - size.scale(rect.size(), Qt::KeepAspectRatio); - painter.setViewport(rect.x(), rect.y(), - size.width(), size.height()); - painter.setWindow(images.at(i).rect()); - painter.drawImage(0, 0, images.at(i)); - if (i != lastItem) - printer.newPage(); - } - painter.end(); - -} - - - - - -void printReport( LtsSession::LtsSession *session, QWidget *dialogParent, KDReports::Report& report ) -{ - QTextDocument doc; - QTextCursor cursor(&doc); - - doc.setMetaInformation(QTextDocument::DocumentTitle, dialogParent->windowTitle()); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(dialogParent->windowTitle()), - dialogParent->windowTitle(), - QPrinter::A3, QPrinter::Landscape); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator( session, nglPrinter ); - - cursor.movePosition(QTextCursor::End); - - report.setWidthForEndlessPrinter(0); - report.setPageSize(QPrinter::A3); - report.setOrientation(QPrinter::Landscape); - - LtsPrinting::Base::printReportData(doc, report); - nglPrinter.print(&doc); -} - - -void printWidget(LtsSession::LtsSession *session, QWidget *dialogParent, QWidget *widget, const QString &title) -{ - QTextDocument doc; - QTextCursor cursor(&doc); - QImage image(widget->size(), QImage::Format_ARGB32); - widget->render(&image); - - QString windowTitle = dialogParent->windowTitle(); - doc.setMetaInformation(QTextDocument::DocumentTitle, dialogParent->windowTitle()); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(windowTitle), - windowTitle, - QPrinter::A3, QPrinter::Landscape); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator(session, nglPrinter); - - cursor.movePosition(QTextCursor::End); - - nglPrinter.printDocImage(doc, image, title); - nglPrinter.print(&doc); -} - -void printMessage(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QString &subject, const QString &subjectContent, - const QString &from, const QString &fromContent, - const QString &to, const QString &toContent, - const QString &msgTextContent, - const QString &time, const QString &timeContent) -{ - QTextDocument doc; - QString windowTitle = subject; - - doc.setMetaInformation(QTextDocument::DocumentTitle, subject); - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(windowTitle), - windowTitle, - QPrinter::A4, QPrinter::Portrait); - - QString str = QString(QStringLiteral("

%1 %2

")).arg(subject).arg(subjectContent); - str.append( QString(QStringLiteral("%1 %2
")).arg(time).arg(timeContent)); - str.append( QString(QStringLiteral("%1 %2
")).arg(from).arg(fromContent)); - str.append( QString(QStringLiteral("%1 %2
")).arg(to).arg(toContent)); - str.append( QString(QStringLiteral("%1")).arg(msgTextContent)); - - doc.setHtml(str); - if (!nglPrinter.printer()) - return; - - setNameOfExerciseAndOperator(session, nglPrinter); - - nglPrinter.print(&doc); -} - - -void printStoryboard(LtsSession::LtsSession *session, QWidget *dialogParent, QAbstractItemView *view, const QString &headingHtml) -{ - QTableWidget *tbl = dynamic_cast< QTableWidget *>(view); - QTextDocument doc; - QTextCursor cursor(&doc); - - QTextBlockFormat tbfFormat; - tbfFormat.setPageBreakPolicy(QTextFormat::PageBreakFlag::PageBreak_Auto); - - QString windowTitle = dialogParent->windowTitle(); - doc.setMetaInformation(QTextDocument::DocumentTitle, dialogParent->windowTitle()); - - LtsPrinting::Base nglPrinter(doc, dialogParent, - QObject::tr("Print %1").arg(windowTitle), - windowTitle, - QPrinter::A4, QPrinter::Portrait); - - if (!nglPrinter.printer()) - return; - - const int rows = tbl->rowCount(); - cursor.insertHtml(headingHtml); - - double currentHeight = 0; - double previousPagesHeight = 0; - QString htmlStr; - for (int rowCounter = 0; rowCounter < rows; rowCounter++) - { - - htmlStr.append(QStringLiteral("")); - QTextEdit *labelForFirstCell = (QTextEdit*)tbl->cellWidget(rowCounter, 0); - QString stringFirstColumn = labelForFirstCell->toHtml(); - - QTextEdit *labelSecondColumn = (QTextEdit*)tbl->cellWidget(rowCounter, 1); - QString stringSecondColumn = labelSecondColumn->toHtml(); - - QTextEdit *labelThirdColumn = (QTextEdit*)tbl->cellWidget(rowCounter, 2); - QString stringThirdColumn = labelThirdColumn->toHtml(); - - for (int rowInCellCounter = 0; rowInCellCounter < stringSecondColumn.split(QStringLiteral("
")).size(); ++rowInCellCounter) - { - htmlStr.append(QStringLiteral("")); - - if (rowInCellCounter < stringFirstColumn.split(QStringLiteral("
")).size()) - { - htmlStr.append(QStringLiteral("")); - } - else - { - htmlStr.append(QStringLiteral("")); - } - QString currentTextSecond = stringSecondColumn.split(QStringLiteral("
"))[rowInCellCounter]; - htmlStr.append(QStringLiteral("")); - - QString currentTextThird = stringThirdColumn.split(QStringLiteral("
"))[rowInCellCounter]; - htmlStr.append(QStringLiteral("")); - htmlStr.append(QStringLiteral("")); - } - QTextDocument tempDoc; - tempDoc.setPageSize(doc.pageSize()); - tempDoc.setHtml(htmlStr); - currentHeight = doc.size().height() - previousPagesHeight; - - if (currentHeight + tempDoc.size().height() > nglPrinter.pageSizeInMM().height()) - { - tbfFormat.setPageBreakPolicy(QTextFormat::PageBreakFlag::PageBreak_AlwaysBefore); - cursor.insertBlock(tbfFormat); - previousPagesHeight = doc.size().height(); - currentHeight = doc.size().height() - previousPagesHeight; - } - else - { - tbfFormat.setPageBreakPolicy(QTextFormat::PageBreakFlag::PageBreak_Auto); - currentHeight = doc.size().height() - previousPagesHeight; - } - cursor.insertHtml(htmlStr); - htmlStr.clear(); - } - htmlStr.append(QStringLiteral("
")); - htmlStr.append(stringFirstColumn.split(QStringLiteral("
"))[rowInCellCounter]); - htmlStr.append(QStringLiteral("
")); - htmlStr.append(QStringLiteral("")); - htmlStr.append(currentTextSecond); - htmlStr.append(QStringLiteral("")); - htmlStr.append(currentTextThird); - htmlStr.append(QStringLiteral("
")); - cursor.insertHtml(htmlStr); - - cursor.movePosition(QTextCursor::End); - - setNameOfExerciseAndOperator(session, nglPrinter); - nglPrinter.print(&doc); -} - - -} diff --git a/Lts/Common/LtsPrinting/printing.h b/Lts/Common/LtsPrinting/printing.h deleted file mode 100755 index cfa985002..000000000 --- a/Lts/Common/LtsPrinting/printing.h +++ /dev/null @@ -1,94 +0,0 @@ - - - -#pragma once - -#include - -#ifndef LTSPRINTING_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSPRINTING_EXPORT -# else -# if defined(MAKE_LTSPRINTING_LIB) -# define LTSPRINTING_EXPORT Q_DECL_EXPORT -# else -# define LTSPRINTING_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif - -class QWidget; -class QAbstractItemView; -class QImage; -namespace LtsSession -{ -class LtsSession; -} -namespace KDReports -{ -class Report; -} -namespace LtsPrinting -{ - - -LTSPRINTING_EXPORT void printView(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QAbstractItemView *view, - const QString &headingHtml = QString()); -LTSPRINTING_EXPORT void printView(LtsSession::LtsSession *session, - QWidget *dialogParent, - QAbstractItemView *view, - const QString &headingHtml = QString()); -LTSPRINTING_EXPORT void printStoryboard(LtsSession::LtsSession *session, - QWidget *dialogParent, - QAbstractItemView *view, - const QString &headingHtml = QString()); - - -LTSPRINTING_EXPORT void printImage(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QImage &image, - const QString &imageTitle, - const QString &description, - const QPrinter::PageSize &pageSize = QPrinter::A3, - const QPrinter::Orientation &pageOrientation = QPrinter::Landscape); - - - -LTSPRINTING_EXPORT void printImages(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QString &docName, - QVector < QImage> &images, - QVector < QString> &imageTitles, - QVector < QString> &descriptions, - const QPrinter::PageSize &pageSize = QPrinter::A3, - const QPrinter::Orientation &pageOrientation = QPrinter::Landscape); - - - -LTSPRINTING_EXPORT void printPDFImages(QWidget *dialogParent, - const QString &docName, - const QString &printPath, - QVector &images); - - - -LTSPRINTING_EXPORT void printReport( LtsSession::LtsSession *session, QWidget *dialogParent, KDReports::Report& report ); - - - -LTSPRINTING_EXPORT void printWidget(LtsSession::LtsSession *session, QWidget *dialogParent, QWidget *widget, const QString &title); - - - -LTSPRINTING_EXPORT void printMessage(LtsSession::LtsSession *session, - QWidget *dialogParent, - const QString &subject, const QString &subjectContent, - const QString &from, const QString &fromContent, - const QString &to, const QString &toContent, - const QString &msgTextContent, - const QString &time, const QString &timeContent); - -} - diff --git a/Lts/Common/LtsPrinting/printing_base.cpp b/Lts/Common/LtsPrinting/printing_base.cpp deleted file mode 100755 index a1a0c71f2..000000000 --- a/Lts/Common/LtsPrinting/printing_base.cpp +++ /dev/null @@ -1,1305 +0,0 @@ - - - - - - - - -#include "printing_base.h" -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - - -namespace LtsPrinting -{ -static inline double mmToInches(double mm) { return mm * 0.039370147; } -static inline double inchesToMM(double inches) { return inches * 25.4; } - -bool LtsPrinting::Base::m_printToFile = false; - -static int TitleDataHeight = 50; -static int DescriptionDataHeight = 50; - -static void spanSizes(const QAbstractItemView *view, - const QModelIndex &ms, - int &rowSpanSize, - int &columnSpanSize, - QVector > &spannedL); - - -Base::Base(QTextDocument &doc, - QWidget *dialogParent, - const QString &dialogTitle, - const QString &_fn, - QPrinter::PaperSize _ps, - QPrinter::Orientation _or, - QPrinter::ColorMode _cm) - : m_printer(new QPrinter), - m_painter(new QPainter), - m_iconFilenameNr(0), - m_tempdoc(nullptr), - leftmargin_(15.0), rightmargin_(15.0), topmargin_(15.0), - bottommargin_(15.0), spacing_(5.0), - headersize_(10), headerrule_(0.5), headertext_(HeaderText), - footersize_(10), footerrule_(0.5), footertext_(FooterText), - itemFontSize_(2), - m_settings(new NGL::Settings(NGL::findProjectConfigFile())), - m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("NGLPrinting")), - m_currentPage(1) -{ - QString fn = _fn; - fn = fn.remove(QLatin1Char(' ')); - fn = fn.remove(QLatin1String("..")); - fn = fn.remove(QLatin1Char('/')); - fn = m_settings->printPath() + fn; - if (!fn.endsWith(QLatin1String(".pdf"), Qt::CaseInsensitive)) - fn += QLatin1String(".pdf"); -#ifndef WIN32 - m_printer->setOutputFileName(fn); -#endif - m_printer->setOutputFormat(QPrinter::NativeFormat); - - - - - m_printer->setPaperSize(_ps); - m_printer->setOrientation(_or); - m_printer->setColorMode(_cm); - - m_printer->setFullPage(true); - - - - QTextOption tOption = doc.defaultTextOption(); - tOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - - doc.setDefaultTextOption(tOption); - - - - doc.setPageSize(QSizeF(printerPageSize())); - doc.setDefaultStyleSheet(styleSheet()); - - if (m_printToFile) - return; - QPrintDialog *dialog = new QPrintDialog(m_printer, dialogParent); - - - QAbstractPrintDialog::PrintDialogOptions op = - QAbstractPrintDialog::PrintToFile | - QAbstractPrintDialog::PrintSelection | - QAbstractPrintDialog::PrintPageRange | - QAbstractPrintDialog::PrintShowPageSize | - QAbstractPrintDialog::PrintCollateCopies; - dialog->setOptions(op); - QString title = dialogTitle; - if (_ps == QPrinter::A2) title += QLatin1String(" (A2)"); - if (_ps == QPrinter::A3) title += QLatin1String(" (A3)"); - if (_ps == QPrinter::A4) title += QLatin1String(" (A4)"); - if (_ps == QPrinter::A5) title += QLatin1String(" (A5)"); - dialog->setWindowTitle(title); - if (dialog->exec() != QDialog::Accepted) - { - - - delete m_printer; - m_printer = nullptr; - return; - } - - m_painter->begin(m_printer); -} - - -Base::~Base() -{ - delete m_printer; - delete m_painter; - delete m_tempdoc; - - for (int nr = 0; nr < m_iconFilenameNr; ++nr) - QFile::remove(iconFilename(nr)); - delete m_settings; -} - - -void Base::setPrintToFile(bool printToFile) -{ - m_printToFile = printToFile; -} - - -QPrinter *Base::printer() -{ - DEBUG_LOG(m_logger) << "Base::printer" ; - - return m_printer; -} - - -QString Base::styleSheet() -{ - QString ret; - - return ret; -} - - -QSize Base::printerPageSize() const -{ - return QSize(m_printer->pageRect().width() - 2 * DOC_MARGIN, - m_printer->pageRect().height() - 2 * DOC_MARGIN); -} - -bool Base::newPage() -{ - return m_printer->newPage(); -} - - -QSize Base::contentPageSize() const -{ - QSize ps(printerPageSize()); - - ps.setHeight(ps.height() - 50 - 50); - return ps; -} - - -void Base::printTitleData(QString &htmlStr, const QString &title, const QString &heading) -{ - if (!title.isEmpty()) - { - htmlStr += QLatin1String("
") + - QLatin1Char('<') + heading + QLatin1Char('>') + title + QLatin1String("') + - QLatin1String("
"); - } -} - - -bool Base::printListData(QString &str, - const QAbstractItemView *view, - const QString &title, - bool asIcons, - int iconExtent) -{ - if (!view) - return false; - printTitleData(str, title); - QAbstractItemModel *vm = view->model(); - if (!vm) - return false; - QItemSelectionModel *sm = view->selectionModel(); - QModelIndexList mil; - if (m_printer->printRange() == QPrinter::Selection) - mil = sm->selectedRows(); - else - { - int row = 0; - while (1) - { - QModelIndex mi = vm->index(row, 0, QModelIndex()); - if (mi.isValid()) - { - mil.append(mi); - row++; - continue; - } - break; - } - } - if (mil.count() == 0) - return false; - if (asIcons) - { - - int elemWidth = iconExtent + 40; - int tableWidth = contentPageSize().width(); - int nColums = tableWidth / elemWidth; - str += QLatin1String("") + - QLatin1String("") + - QLatin1String("") + - QLatin1String("") + - QLatin1String(""); - - int columIdx = 0; - for (const QModelIndex &mi :qAsConst(mil)) - { - - if (columIdx >= nColums) - { - - columIdx = 0; - str += QLatin1String(""); - } - - str += QLatin1String(""); - ++columIdx; - - } - str += QLatin1String("
"); - printDecorationData(str, vm, mi, iconExtent / 2); - - QVariant vdata = vm->data(mi, Qt::DisplayRole); - str += QLatin1String("
"); - QString displayText; - if (vdata.isValid()) - displayText = vdata.toString(); - else - displayText = QLatin1Char(' '); - str += QLatin1String("") + displayText + QLatin1String("") + - QLatin1String("
"); - } - else - { - - for (const QModelIndex &mi :qAsConst(mil)) - { - if (printItemData(str, vm, mi, iconExtent)) - str += QLatin1String("
"); - } - } - return true; -} - -static QModelIndexList getItemChildren(const QAbstractItemModel *model, - const QTreeView *tv, - const QModelIndex &root) -{ - QModelIndexList ret; - const int rc = model->rowCount(root); - ret.reserve(rc); - - for (int row = 0; row < rc; ++row) - { - const QModelIndex mi = model->index(row, 0, root); - ret += mi; - if (tv->isExpanded(mi)) - ret += getItemChildren(model, tv, mi); - } - - return ret; -} - - -bool Base::printTableData(QString &str, - const QAbstractItemView *view, - const QString &title_, - const QSet &suppressCols, - int iconExtent) -{ - if (!view) - return false; - QString title = title_; - printTitleData(str, title.remove(QLatin1String("[*]"))); - const QAbstractItemModel *imodel = view->model(); - QModelIndexList mil; - if (m_printer->printRange() == QPrinter::Selection) - { - const QItemSelectionModel *sm = view->selectionModel(); - mil += sm->selectedRows(); - } - else - { - const QTreeView *tv = qobject_cast(view); - if (tv) - { - const int rc = imodel->rowCount(); - mil.reserve(rc); - for (int row = 0; row < rc; ++row) - { - QModelIndex mi = imodel->index(row, 0); - mil += mi; - if (tv->isExpanded(mi)) - mil += getItemChildren(imodel, tv, mi); - } - } - else - { - const int rc = imodel->rowCount(); - mil.reserve(rc); - for (int row = 0; row < rc; ++row) - mil.append(imodel->index(row, 0)); - } - } - int colCount = imodel->columnCount(); - QStringList tableHeader; - for (int i = 0; i < colCount; i++) - { - if (suppressCols.contains(i)) - continue; - QVariant v = imodel->headerData(i, Qt::Horizontal); - if (v.isValid()) - tableHeader.append(v.toString()); - } - str += QLatin1String("") + - QLatin1String("") + - QLatin1String(""); - for (const QString &s : qAsConst(tableHeader)) - str += QLatin1String(""); - str += QLatin1String(""); - - - - - QVector > spannedL; - for (const QModelIndex &mi :qAsConst(mil)) - { - str += QLatin1String(""); - for (int ii = 0; ii < colCount; ii++) - { - if (suppressCols.contains(ii)) - continue; - const QModelIndex ms = mi.sibling(mi.row(), ii); - if (spannedL.contains(qMakePair(ms.row(), ms.column()))) - continue; - int rowSpanSize; - int columnSpanSize; - spanSizes(view, ms, rowSpanSize, columnSpanSize, spannedL); - printTableItemData(str, imodel, ms, rowSpanSize, columnSpanSize, - iconExtent); - } - str += QLatin1String(""); - } - str += QLatin1String("
") + s + QLatin1String("
"); - - DEBUG_LOG(m_logger) << qPrintable(str) ; - return true; -} - - -void spanSizes(const QAbstractItemView *view, - const QModelIndex &ms, - int &rowSpanSize, - int &columnSpanSize, - QVector > &spannedL) -{ - rowSpanSize = 1; - columnSpanSize = 1; - const QTableView *tview = qobject_cast(view); - if (tview) - { - rowSpanSize = tview->rowSpan(ms.row(), ms.column()); - columnSpanSize = tview->columnSpan(ms.row(), ms.column()); - for (int row = ms.row(); row < ms.row() + rowSpanSize; ++row) - spannedL << qMakePair(row, ms.column()); - for (int column = ms.column(); column < ms.column() + columnSpanSize; ++column) - spannedL << qMakePair(ms.row(), column); - } -} - - -bool Base::printTableItemData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int rowSpanSize, - int columnSpanSize, - int iconExtent) -{ - QVariant vdata; - QString htmlString; - - - vdata = model->data(mi, Qt::BackgroundRole); - if (vdata.isValid()) - { - QString bgColorString = vdata.value().color().name(); - htmlString += QLatin1String(" 1) - htmlString += QLatin1String(" rowspan=\"") + QString::number(rowSpanSize) + QLatin1Char('\"'); - if (columnSpanSize > 1) - htmlString += QLatin1String(" colspan=\"") + QString::number(columnSpanSize) + QLatin1Char('\"'); - - htmlString += QLatin1Char('>'); - - printItemData(htmlString, model, mi, iconExtent); - htmlString += QLatin1String(""); - str += htmlString; - return true; -} - - - - -bool Base::printTreeData(QString &str, - const QAbstractItemView *view, - const QString &title, - const QSet &suppressCols, - int iconExtent, - const QList &tabtexts) -{ - if (!view) - return false; - printTitleData(str, title); - QAbstractItemModel *vm = view->model(); - if (!vm) - return false; - QItemSelectionModel *sm = view->selectionModel(); - QModelIndexList milColumn0; - if (m_printer->printRange() == QPrinter::Selection) - milColumn0 = sm->selectedRows(); - else - { - int row = 0; - while (1) - { - QModelIndex mi = vm->index(row, 0); - if (mi.isValid()) - { - milColumn0.append(mi); - row++; - continue; - } - break; - } - } - if (milColumn0.count() == 0) - return false; - - str += QLatin1String("") + - QLatin1String(""); - - QStringList tableHeader; - if (tabtexts.size() > 0) - tableHeader = tabtexts; - else - { - for (int i = 0; i < vm->columnCount(); i++) - { - if (suppressCols.contains(i)) - continue; - QVariant v = vm->headerData(i, Qt::Horizontal); - if (v.isValid()) - tableHeader.append(v.toString()); - else - tableHeader.append(QString()); - } - } - - for (const QString &s : qAsConst(tableHeader)) - str += QLatin1String(""); - str += QLatin1String(""); - for (const QModelIndex &miColumn0 : qAsConst(milColumn0)) - printListPoint(str, view, vm, miColumn0, suppressCols, iconExtent); - - str += QLatin1String("

") + s + QLatin1String("



"); - - - - - - return true; -} - - -void Base::printListPoint(QString &str, - const QAbstractItemView *view, - const QAbstractItemModel *vm, - const QModelIndex &miColumn0, - const QSet &suppressCols, - int iconExtent) -{ - if (!miColumn0.isValid()) - return; - - str += QLatin1String(""); - for (int column = 0; column < vm->columnCount(); column++) - { - if (suppressCols.contains(column)) - continue; - QModelIndex ms = miColumn0.sibling(miColumn0.row(), column); - if (!ms.isValid()) - continue; - str += QLatin1String("
"); - if (column == 0) - { - - int hierCount = hierarchyCount(ms); - for (int genIdx = 0; genIdx < hierCount; ++genIdx) - str += QLatin1String("
"); - str += QLatin1String("
"); - printItemData(str, vm, ms, iconExtent); - str += QLatin1String("
"); - for (int genIdx = 0; genIdx < hierCount; ++genIdx) - str += QLatin1String("
"); - } - else - { - str += QLatin1String("
"); - printItemData(str, vm, ms, iconExtent); - str += QLatin1String("
"); - } - str += QLatin1String("
"); - } - str += QLatin1String(""); - - - const QTreeView *tv = qobject_cast(view); - const bool expanded = tv ? tv->isExpanded(miColumn0) : true; - if (expanded) - { - const int rc = vm->rowCount(miColumn0); - for (int row = 0; row < rc; row++) - { - QModelIndex cmi = vm->index(row, 0, miColumn0); - if (cmi.isValid()) - printListPoint(str, view, vm, cmi, suppressCols, iconExtent); - } - } -} - - -int Base::hierarchyCount(const QModelIndex &child) -{ - int count = 0; - const auto parent = child.parent(); - if (parent.isValid()) - count = hierarchyCount(parent) + 1; - return count; -} - - -bool Base::printItemData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int iconExtent) -{ - QVariant vdata; - QString displayString; - QString htmlString; - - - vdata = model->data(mi, Qt::CheckStateRole); - if (vdata.isValid()) - { - displayString += QLatin1String(""); - if (static_cast(vdata.toInt())) - displayString += QLatin1String("⊗ "); - else - displayString += QLatin1String("ο "); - displayString += QLatin1String(""); - } - - printDecorationData(displayString, model, mi, iconExtent / 2); - - displayString += QLatin1Char(' '); - vdata = model->data(mi, Qt::DisplayRole); - if (vdata.isValid()) - displayString += vdata.toString(); - - QString fgColorString; - vdata = model->data(mi, Qt::TextColorRole); - if (vdata.isValid()) - fgColorString = vdata.value().name(); - - vdata = model->data(mi, Qt::ForegroundRole); - if (vdata.isValid()) - fgColorString = vdata.value().color().name(); - - htmlString += QLatin1String("') + displayString + QLatin1String(""); - - str += htmlString; - return true; -} - - -void Base::printDecorationData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int iconExtent) -{ - QVariant vdata = model->data(mi, Qt::DecorationRole); - if (vdata.isNull() || !vdata.isValid()) - return; - QIcon icon = vdata.value(); - if (icon.isNull()) - return; - - QString fn = iconFilename(m_iconFilenameNr++); - QPixmap pm = icon.pixmap(1000); - pm.save(fn); - - str += QLatin1String("\"icon\""); -} - - -QString Base::iconFilename(int nr) const -{ - return m_settings->printPath() + QLatin1String("icon_") + QString::number(nr) + QLatin1String(".png"); -} - - -bool Base::printDocTextBlock(QTextDocument &doc, - const QString &text, - const QString &title) -{ - QTextCursor cursor(&doc); - cursor.movePosition(QTextCursor::End); - - if (!title.isEmpty()) - { - QString htmlStr; - printTitleData(htmlStr, title); - cursor.insertHtml(htmlStr); - } - cursor.insertBlock(); - - QTextBlockFormat bf = cursor.blockFormat(); - bf.setNonBreakableLines(false); - cursor.setBlockFormat(bf); - - QTextCharFormat cf = cursor.blockCharFormat(); - cf.setFontWeight(QFont::Normal); - cursor.setBlockCharFormat(cf); - - cursor.insertText(text); - return true; -} - - -bool Base::printDocImage(QTextDocument &doc, const QImage &image, - const QString &title, const QString &description, const qreal alreadyUsedHeight) const -{ - if (image.isNull()) - return false; - QTextCursor cursor(&doc); - - static int iImageCounter = 0; - QString name = QStringLiteral("printing://image_%1.png").arg(iImageCounter++); - doc.addResource(QTextDocument::ImageResource, - QUrl(name), - QVariant(image)); - - QString htmlStr; - if (!title.isEmpty()) - printTitleData(htmlStr, title); - if (!description.isEmpty()) - printTitleData(htmlStr, description, QLatin1String("h4")); - double rel = (double)image.height() / (double)image.width(); - - double dwMax = contentPageSize().width(); - double dhMax = contentPageSize().height() - alreadyUsedHeight; - if (!title.isEmpty()) - dhMax -= TitleDataHeight; - if (!description.isEmpty()) - dhMax -= DescriptionDataHeight; - double dh = dhMax; - double dw = dwMax; - - if (rel >= 1) - { - - - double new_dw = dhMax / rel; - if (new_dw > dwMax) - { - - dh = dwMax * rel; - } - else - dw = new_dw; - } - else - { - - - double new_dh = dw * rel; - if (new_dh > dhMax) - { - - dw = dhMax / rel; - } - else - dh = new_dh; - } - - QTextImageFormat tif; - tif.setName(name); - tif.setHeight(dh); - tif.setWidth(dw); - - cursor.insertHtml(htmlStr); - cursor.insertImage(tif); - return true; -} - - -bool Base::printReportData(QTextDocument &doc, KDReports::Report &report ) -{ - QTextCursor cursor(&doc); - cursor.movePosition(QTextCursor::End); - - QTemporaryFile tempFile; - if (!tempFile.open()) - return false; - if (!report.exportToHtml(tempFile.fileName())) - return false; - cursor.insertHtml(QString::fromUtf8(tempFile.readAll())); - return true; -} - - -QPrinter::PageSize Base::pageSize() const -{ - return m_printer->pageSize(); -} - - -void Base::setPageSize(QPrinter::PageSize size) -{ - m_printer->setPageSize(size); -} - - -QPrinter::Orientation Base::orientation() const -{ - return m_printer->orientation(); -} - - -void Base::setOrientation(QPrinter::Orientation orientation) -{ - m_printer->setOrientation(orientation); -} - - -double Base::leftMargin() const -{ - return leftmargin_; -} - - -void Base::setLeftMargin(double margin) -{ - if ((margin > 0) && (margin < m_printer->paperRect().width() / 2)) - leftmargin_ = margin; - else - leftmargin_ = 0; -} - - -double Base::rightMargin() const -{ - return rightmargin_; -} - - -void Base::setRightMargin(double margin) -{ - if ((margin > 0) && (margin < m_printer->paperRect().width() / 2)) - rightmargin_ = margin; - else - rightmargin_ = 0; -} - - -double Base::topMargin() const -{ - return topmargin_; -} - - -void Base::setTopMargin(double margin) -{ - if ((margin > 0) && (margin < m_printer->paperRect().height() / 4)) - topmargin_ = margin; - else - topmargin_ = 0; -} - - -double Base::bottomMargin() const -{ - return bottommargin_; -} - - -void Base::setBottomMargin(double margin) -{ - if ((margin > 0) && (margin < m_printer->paperRect().height() / 4)) - bottommargin_ = margin; - else - bottommargin_ = 0; -} - - -void Base::setMargins(double margin) -{ - if ((margin > 0) - && (margin < m_printer->paperRect().height() / 2) - && (margin < m_printer->paperRect().width() / 2)) - leftmargin_ = rightmargin_ = topmargin_ = bottommargin_ = margin; - else - leftmargin_ = rightmargin_ = topmargin_ = bottommargin_ = 0; -} - - -double Base::spacing() const -{ - return spacing_; -} - - -void Base::setSpacing(double spacing) -{ - if ((spacing > 0) && (spacing <= m_printer->paperRect().height() / 8)) - spacing_ = spacing; - else - spacing_ = 0; -} - - -double Base::headerSize() const -{ - return headersize_; -} - - -void Base::setHeaderSize(double size) -{ - if ((size > 0) && (size <= m_printer->paperRect().height() / 8)) - headersize_ = size; - else - headersize_ = 0; -} - - -double Base::headerRule() const -{ - return headerrule_; -} - - -void Base::setHeaderRule(double pointsize) -{ - headerrule_ = qMax(0.0, pointsize); -} - - -const QString &Base::headerText() const -{ - return headertext_; -} - - - - - - - - - - -void Base::setHeaderText(const QString &text) -{ - headertext_ = text; -} - - -double Base::footerSize() const -{ - return footersize_; -} - - -void Base::setFooterSize(double size) -{ - if ((size > 0) && (size <= m_printer->paperRect().height() / 8)) - footersize_ = size; - else - footersize_ = 0; -} - - -double Base::footerRule() const -{ - return footerrule_; -} - - - -void Base::setFooterRule(double pointsize) -{ - footerrule_ = qMax(0.0, pointsize); -} - - -const QString &Base::footerText() const -{ - return footertext_; -} - - - - - - - - - -void Base::setFooterText(const QString &text) -{ - footertext_ = text; -} - - -void Base:: setItemFontSize(int itemFontSize) -{ - itemFontSize_ = itemFontSize; -} - - -QRectF Base::paperRect(QPaintDevice *device) -{ - - QRectF rect = m_printer->paperRect(); - - rect.setWidth(rect.width() * - device->logicalDpiX() / m_printer->logicalDpiX()); - rect.setHeight(rect.height() * - device->logicalDpiY() / m_printer->logicalDpiY()); - - return rect; -} - - -QRectF Base::contentRect(QPaintDevice *device) -{ - - QRectF rect = paperRect(device); - - rect.adjust(mmToInches(leftmargin_) * device->logicalDpiX(), - mmToInches(topmargin_) * device->logicalDpiY(), - -mmToInches(rightmargin_) * device->logicalDpiX(), - -mmToInches(bottommargin_) * device->logicalDpiY()); - - - if (headersize_ > 0) - { - rect.adjust(0, mmToInches(headersize_) * device->logicalDpiY(), 0, 0); - rect.adjust(0, mmToInches(spacing_) * device->logicalDpiY(), 0, 0); - } - - if (footersize_ > 0) - { - rect.adjust(0, 0, 0, -mmToInches(footersize_) * device->logicalDpiY()); - rect.adjust(0, 0, 0, -mmToInches(spacing_) * device->logicalDpiY()); - } - - return rect; -} - - -QRectF Base::headerRect(QPaintDevice *device) -{ - QRectF rect = paperRect(device); - rect.adjust(mmToInches(leftmargin_) * device->logicalDpiX(), - mmToInches(topmargin_) * device->logicalDpiY(), - -mmToInches(rightmargin_) * device->logicalDpiX(), 0); - - rect.setBottom(rect.top() + - mmToInches(headersize_) * device->logicalDpiY()); - - return rect; -} - - -QRectF Base::footerRect(QPaintDevice *device) -{ - QRectF rect = paperRect(device); - rect.adjust(mmToInches(leftmargin_) * device->logicalDpiX(), 0, - -mmToInches(rightmargin_) * device->logicalDpiX(), - -mmToInches(bottommargin_) * device->logicalDpiY()); - - rect.setTop(rect.bottom() - - mmToInches(footersize_) * device->logicalDpiY()); - - return rect; -} - - -void Base::print(QTextDocument *doc) -{ - m_tempdoc = doc->clone(); - print(); - delete m_tempdoc; - m_tempdoc = nullptr; -} - - - -void Base::printSinglePage(QTextDocument *doc) -{ - m_tempdoc = doc->clone(); - printSinglePage(); - delete m_tempdoc; - m_tempdoc = nullptr; -} - - -void Base::printSinglePage() -{ - if (!m_printer || !m_tempdoc) return; - - m_tempdoc->setUseDesignMetrics(true); - m_tempdoc->documentLayout()->setPaintDevice(m_printer); - m_tempdoc->setPageSize(contentRect(m_printer).size()); - - QTextFrameFormat fmt = m_tempdoc->rootFrame()->frameFormat(); - fmt.setMargin(0); - m_tempdoc->rootFrame()->setFrameFormat(fmt); - - - - - - int doccopies; - int pagecopies; - if (m_printer->collateCopies()) - { - doccopies = 1; - pagecopies = m_printer->numCopies(); - } - else - { - doccopies = m_printer->numCopies(); - pagecopies = 1; - } - - - int firstpage = m_printer->fromPage(); - int lastpage = m_printer->toPage(); - if (firstpage == 0 && lastpage == 0) - { - firstpage = 1; - lastpage = m_tempdoc->pageCount(); - } - - - bool ascending = true; - if (m_printer->pageOrder() == QPrinter::LastPageFirst) - { - int tmp = firstpage; - firstpage = lastpage; - lastpage = tmp; - ascending = false; - } - - - m_painter->setRenderHints(QPainter::Antialiasing | - QPainter::TextAntialiasing | - QPainter::SmoothPixmapTransform, true); - for (int dc = 0; dc < doccopies; dc++) - { - int pagenum = firstpage; - - while (true) - { - for (int pc = 0; pc < pagecopies; pc++) - { - if (m_printer->printerState() == QPrinter::Aborted || - m_printer->printerState() == QPrinter::Error) - return; - - paintPage( m_tempdoc, pagenum); - if (pc < pagecopies - 1) - m_printer->newPage(); - } - if (pagenum == lastpage) - break; - if (ascending) - pagenum++; - else - pagenum--; - - { - m_printer->newPage(); - } - } - - if (dc < doccopies - 1) - { - m_printer->newPage(); - } - } -} - - -void Base::printDone() -{ - m_painter->end(); - m_currentPage = 1; -} - - - -void Base::print() -{ - m_currentPage = 1; - printSinglePage(); - m_painter->end(); - m_currentPage = 1; -} - - - -void Base::paintPage(QTextDocument *document, - int pagenum) -{ - QRectF rect; - double onepoint = m_painter->device()->logicalDpiY() / 72.0; - - - if (headersize_ > 0) - { - rect = headerRect(m_painter->device()); - if (headerrule_ > 0.0) - { - m_painter->save(); - - m_painter->translate(0, onepoint + (headerrule_ * onepoint / 2.0)); - m_painter->setPen(QPen(Qt::black, headerrule_ * onepoint)); - m_painter->drawLine(rect.bottomLeft(), rect.bottomRight()); - m_painter->restore(); - } - - - QString header = headertext_; - QString title = document->metaInformation(QTextDocument::DocumentTitle).remove(QLatin1String("[*]")); - header.replace(QLatin1String("&exercise;"), currentExerciseName()); - header.replace(QLatin1String("&title;"), title); - header.replace(QLatin1String("&logo;"), m_settings->logoProject()); - header.replace(QLatin1String("&logoHeight;"), QString::number(ProjectIconHeight)); - - - m_painter->save(); - m_painter->translate(rect.left(), rect.top()); - QRectF clip(0, 0, rect.width(), rect.height()); - QTextDocument doc; - doc.setUseDesignMetrics(true); - doc.setHtml(header); - doc.documentLayout()->setPaintDevice(m_painter->device()); - doc.setPageSize(rect.size()); - - - double newtop = clip.bottom() - doc.size().height(); - clip.setHeight(doc.size().height()); - m_painter->translate(0, newtop); - - doc.drawContents(m_painter, clip); - m_painter->restore(); - } - - - if (footersize_ > 0) - { - rect = footerRect(m_painter->device()); - if (footerrule_ > 0.0) - { - m_painter->save(); - - m_painter->translate(0, -onepoint + (-footerrule_ * onepoint / 2.0)); - m_painter->setPen(QPen(Qt::black, footerrule_ * onepoint)); - m_painter->drawLine(rect.topLeft(), rect.topRight()); - m_painter->restore(); - } - - - QString footer = footertext_; - footer.replace(QLatin1String("&date;"), QObject::tr("Printed on: ") + DTG::toString(QDateTime::currentDateTime()) ); - footer.replace(QLatin1String("&user;"), operatorName()); - footer.replace(QLatin1String("&footertitle;"), footertitle_); - footer.replace(QLatin1String("&page;"), QString::number(m_currentPage++)); - - m_painter->save(); - m_painter->translate(rect.left(), rect.top()); - QRectF clip(0, 0, rect.width(), rect.height()); - QTextDocument doc; - doc.setUseDesignMetrics(true); - doc.setHtml(footer); - doc.documentLayout()->setPaintDevice(m_painter->device()); - doc.setPageSize(rect.size()); - doc.drawContents(m_painter, clip); - m_painter->restore(); - } - - - m_painter->save(); - - rect = contentRect(m_painter->device()); - m_painter->translate(rect.left(), rect.top() - (pagenum - 1) * rect.height()); - QRectF clip(0, (pagenum - 1) * rect.height(), rect.width(), rect.height()); - - document->drawContents(m_painter, clip); - - m_painter->restore(); -} - -void Base::setCurrentExerciseName( const QString& exerciseName ) -{ - m_exerciseName = exerciseName; -} - - -QString Base::currentExerciseName() const -{ - return m_exerciseName; -} - -void Base::setOperatorName( const QString& operatorName ) -{ - m_operatorName = operatorName; -} - - -QString Base::operatorName() const -{ - return m_operatorName; -} - - - -QSize Base::pageSizeInMM() -{ - QRectF rect = contentRect(m_printer); - return rect.size().toSize(); -} -} diff --git a/Lts/Common/LtsPrinting/printing_base.h b/Lts/Common/LtsPrinting/printing_base.h deleted file mode 100755 index b7e59bb7d..000000000 --- a/Lts/Common/LtsPrinting/printing_base.h +++ /dev/null @@ -1,299 +0,0 @@ - - -#pragma once - -#include -#include - -namespace NGL -{ -class INGLLogger; -class Settings; -} -namespace KDReports -{ -class Report; -} - -class QPainter; -class QAbstractItemView; -class QAbstractItemModel; -class QModelIndex; -class QTextDocument; - -namespace LtsPrinting -{ - -static constexpr int DECORATION_EXTENT = 50; -static constexpr int DOC_MARGIN = 60; -static constexpr int ProjectIconHeight = 40; - - -static const QLatin1String HeaderText( - "" - " " - " " - " " - " " - " " - "
&exercise;&title;\"Logo\"
"); - -static const QLatin1String FooterText( - "" - " " - " " - " " - " " - " " - "
&date; -- &user;&footertitle;&page;
"); - - -class Base -{ -public: - - - Base(QTextDocument &doc, - QWidget *dialogParent, - const QString &dialogTitle, - const QString &_fn, - QPrinter::PaperSize _ps = QPrinter::A4, - QPrinter::Orientation _or = QPrinter::Portrait, - QPrinter::ColorMode _cm = QPrinter::Color); - - - - virtual ~Base(); - - - - QPrinter *printer(); - - - - static void setPrintToFile(bool printToFile); - - - - static QString styleSheet(); - - - QPrinter::PageSize pageSize() const; - - void setPageSize(QPrinter::PageSize size); - - QPrinter::Orientation orientation() const; - - void setOrientation(QPrinter::Orientation orientation); - - bool newPage(); - - - double leftMargin() const; - - void setLeftMargin(double margin); - - double rightMargin() const; - - void setRightMargin(double margin); - - double topMargin() const; - - void setTopMargin(double margin); - - double bottomMargin() const; - - void setBottomMargin(double margin); - - void setMargins(double margin); - - - double spacing() const; - - void setSpacing(double spacing); - - - double headerSize() const; - - void setHeaderSize(double size); - - double headerRule() const; - - void setHeaderRule(double pointsize); - - const QString &headerText() const; - - void setHeaderText(const QString &text); - - - double footerSize() const; - - void setFooterSize(double size); - - double footerRule() const; - - void setFooterRule(double pointsize); - - const QString &footerText() const; - - void setFooterText(const QString &text); - - void setFooterTitle(const QString &footertitle) { footertitle_ = footertitle; } - - - void setItemFontSize(int itemFontSize); - - - - static void printTitleData(QString &htmlStr, const QString &title, const QString &heading = QStringLiteral("h3")); - - - - bool printListData(QString &str, - const QAbstractItemView *view, - const QString &title, - bool asIcons = false, - int iconExtent = DECORATION_EXTENT); - - - bool printTableData(QString &str, - const QAbstractItemView *view, - const QString &title = QString(), - const QSet &suppressCols = QSet(), - int iconExtent = DECORATION_EXTENT); - - - bool printTreeData(QString &str, - const QAbstractItemView *view, - const QString &title = QString(), - const QSet &suppressCols = QSet(), - int iconExtent = DECORATION_EXTENT, - const QList &tabtexts = QList()); - - - - static bool printDocTextBlock(QTextDocument &doc, - const QString &text, - const QString &title = QString()); - - - - bool printDocImage(QTextDocument &doc, const QImage &image, - const QString &title = QString(), const QString &description = QString(), const qreal alreadyUsedHeight = 0) const; - - - - static bool printReportData(QTextDocument &doc, KDReports::Report &report); - - - - virtual void print(QTextDocument *doc); - - - virtual void printSinglePage(QTextDocument *doc); - - - virtual void printDone(); - - - - - template - static QSet getHiddenCols(T *view) - { - QSet hiddenCols; - int colCount = view->model()->columnCount(); - for (int i = 0; i < colCount; ++i) - { - if (view->isColumnHidden(i)) - hiddenCols += i; - } - return hiddenCols; - } - - - - void setCurrentExerciseName( const QString& exerciseName ); - - - - void setOperatorName( const QString& operatorName ); - - - void paintPage(QTextDocument *document, - int pagenum); - - QSize pageSizeInMM(); - -protected: - bool printTableItemData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int rowSpanSize, - int columnSpanSize, - int iconExtent = DECORATION_EXTENT); - bool printItemData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int iconExtent = DECORATION_EXTENT); - void printDecorationData(QString &str, - const QAbstractItemModel *model, - const QModelIndex &mi, - int iconExtent = DECORATION_EXTENT); - void printListPoint(QString &str, - const QAbstractItemView *view, - const QAbstractItemModel *vm, - const QModelIndex &miColumn0, - const QSet &suppressCols = QSet(), - int iconExtent = DECORATION_EXTENT); - static int hierarchyCount(const QModelIndex &child); - QString iconFilename(int nr) const; - QSize printerPageSize() const; - QSize contentPageSize() const; - - QString currentExerciseName() const; - QString operatorName() const; - - - QRectF paperRect(QPaintDevice *device); - - QRectF contentRect(QPaintDevice *device); - QRectF headerRect(QPaintDevice *device); - QRectF footerRect(QPaintDevice *device); - - - - void printSinglePage(); - void print(); - -private: - QPrinter *m_printer; - QPainter *m_painter; - static bool m_printToFile; - int m_iconFilenameNr; - - QTextDocument *m_tempdoc; - - double leftmargin_; - double rightmargin_; - double topmargin_; - double bottommargin_; - double spacing_; - - double headersize_; - double headerrule_; - QString headertext_; - double footersize_; - double footerrule_; - QString footertext_; - QString footertitle_; - int itemFontSize_; - -private: - NGL::Settings *m_settings; - NGL::INGLLogger &m_logger; - QString m_exerciseName; - QString m_operatorName; - int m_currentPage; -}; -} diff --git a/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt b/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt deleted file mode 100755 index 8fa649ca9..000000000 --- a/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Christian Roeskens, NLSE, 2018-03-02 - - -SET(LtsProjectionWorkstation_SRCS - - lts_projection_workstation.h - lts_projection_workstation.cpp - - lts_projection_workstation_export.h - - vnc_server_manager.h - vnc_server_manager.cpp - - logger.cpp - logger.h - - WebSocket/json_keys.h - WebSocket/messages.h - WebSocket/messages.cpp - WebSocket/message_factory.h - WebSocket/message_factory.cpp -) - -NGL_ADD_LIBRARY(WS LtsProjectionWorkstation SHARED ${LtsProjectionWorkstation_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsProjectionWorkstation - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsProjectionWorkstation - PUBLIC - Qt5::Core - PRIVATE - Qt5::Gui - NGLLogger - nglsettings - LtsWebSocketServer - LtsSession - LtsSessionData -) - -NGL_INSTALL_TARGET(LtsProjectionWorkstation) diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp b/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp deleted file mode 100755 index 7678fdc5a..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp +++ /dev/null @@ -1,63 +0,0 @@ - - - -#include "client_message.h" -#include "json_keys.h" -#include "message_handler.h" - -namespace LtsProjectionWorkstation -{ - - - -LtsWebSocket::AbstractClientMessage *ClientMessageFactory::createMessage(LtsWebSocket::ReplyHandler *client, const QString &message) -{ - LtsWebSocket::AbstractClientMessage *result = checkAttributes(client, message); - - if (result) - return result; - - if (m_type == JsonKeys::LOGIN_REQUEST()) - result = new LoginRequest(m_messageHandler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::LOGOUT_REQUEST()) - result = new LoginRequest(m_messageHandler, client, m_type, m_uuid, m_data); - - return checkResult(client, message, result); -} - - - -void LoginRequest::process() -{ - if (hasValidData()) - { - int userId = m_data.toObject().value(JsonKeys::USER_ID()).toInt(-1), - userPriority = m_data.toObject().value(JsonKeys::USER_PRIORITY()).toInt(-1), - exerciseId = m_data.toObject().value(JsonKeys::EXERCISE_ID()).toInt(-1); - m_client->addReply(m_messageHandler->handleLoginRequest(userId, userPriority, exerciseId)); - } -} - -bool LoginRequest::hasValidData() const -{ - return m_data.isObject() - && m_data.toObject().value(JsonKeys::USER_ID()).toInt(-1) > -1 - && m_data.toObject().value(JsonKeys::USER_PRIORITY()).toInt(-1) > -1 - && m_data.toObject().value(JsonKeys::EXERCISE_ID()).toInt(-1) > -1; -} - - - -void LogoutRequest::process() -{ - m_client->addReply(m_messageHandler->handleLogoutRequest()); -} - -bool LogoutRequest::hasValidData() const -{ - return true; -} - - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h b/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h deleted file mode 100755 index 1ab4fe499..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h +++ /dev/null @@ -1,65 +0,0 @@ - - - -#pragma once - -#include - -#include "logger.h" - - -namespace LtsProjectionWorkstation -{ -class MessageHandler; - -class ClientMessageFactory : public LtsWebSocket::AbstractClientMessageFactory -{ -public: - explicit ClientMessageFactory(MessageHandler *messageHandler) - : LtsWebSocket::AbstractClientMessageFactory(getLogger("ClientMessageFactory")) - , m_messageHandler(messageHandler) - {} - - - LtsWebSocket::AbstractClientMessage *createMessage(LtsWebSocket::ReplyHandler *client, const QString &message) override final; - -private: - MessageHandler *m_messageHandler; -}; - -class ClientMessage : public LtsWebSocket::AbstractClientMessage -{ -public: - explicit ClientMessage(MessageHandler *messageHandler, LtsWebSocket::ReplyHandler *client, const QString &type, const QString &uuid, const QJsonValue &data) - : AbstractClientMessage(client, type, uuid, data) - , m_messageHandler(messageHandler) - {} - -protected: - MessageHandler *m_messageHandler; - -}; - -class LoginRequest : public ClientMessage -{ -public: - explicit LoginRequest(MessageHandler *messageHandler, LtsWebSocket::ReplyHandler *client, const QString &type, const QString &uuid, const QJsonValue &data) - : ClientMessage(messageHandler, client, type, uuid, data) - {} - - void process(); - bool hasValidData() const; -}; - -class LogoutRequest : public ClientMessage -{ -public: - explicit LogoutRequest(MessageHandler *messageHandler, LtsWebSocket::ReplyHandler *client, const QString &type, const QString &uuid, const QJsonValue &data) - : ClientMessage(messageHandler, client, type, uuid, data) - {} - - void process(); - bool hasValidData() const; -}; - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h b/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h deleted file mode 100644 index a8673fc52..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h +++ /dev/null @@ -1,35 +0,0 @@ - - -#pragma once - -#include - -namespace LtsProjectionWorkstation::JsonKeys { - -inline QString ERROR_MESSAGE() { return QStringLiteral("errorMessage"); } - -inline QString LOGIN_REQUEST() { return QStringLiteral("loginRequest"); } -inline QString LOGOUT_REQUEST() { return QStringLiteral("logoutRequest"); } -inline QString DISCONNECT_REQUEST() { - return QStringLiteral("disconnectRequest"); -} - -inline QString LOGIN_REPLY() { return QStringLiteral("loginReply"); } -inline QString LOGOUT_REPLY() { return QStringLiteral("logoutReply"); } -inline QString DEDICAED_DISCONNECT() { - return QStringLiteral("dedicatedDisconnect"); -} - -inline QString USER_ID() { return QStringLiteral("userId"); } -inline QString USER_PRIORITY() { return QStringLiteral("userPriority"); } -inline QString EXERCISE_ID() { return QStringLiteral("exerciseId"); } -inline QString NAME() { return QStringLiteral("name"); } -inline QString PORT() { return QStringLiteral("port"); } -inline QString WIDTH() { return QStringLiteral("width"); } -inline QString HEIGHT() { return QStringLiteral("height"); } -inline QString REASON() { return QStringLiteral("reason"); } -inline QString SUCCESS() { return QStringLiteral("success"); } -inline QString SCREENS() { return QStringLiteral("screens"); } -inline QString SETTINGS_DATA() { return QStringLiteral("settingsData"); } - -} // namespace LtsProjectionWorkstation::JsonKeys diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp b/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp deleted file mode 100755 index e89573346..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "message_factory.h" -#include "messages.h" -#include "json_keys.h" - -#include -#include - -#include - -namespace LtsProjectionWorkstation -{ - -LtsWebSocket::AbstractClientMessage *MessageFactory::createMessage(LtsWebSocket::ReplyHandler *client, const QString &message) -{ - - - if (m_uuid.isEmpty()) - { - WARNING_LOG(m_logger) << "Got message without uuid: " << message << "'"; - WARNING_LOG(m_logger) << "Created UUID for error message!"; - return new ErrorMessage(QUuid::createUuid().toString(), QStringLiteral("Got message without uuid")); - } - - LtsWebSocket::AbstractClientMessage *result = checkAttributes(client, message); - - if (result) - return result; - - if (m_type == JsonKeys::DEDICAED_DISCONNECT()) - result = new STC::DedicatedDisconnect(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::LOGIN_REPLY()) - result = new STC::LoginRequestReply(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::LOGOUT_REPLY()) - result = new STC::LoginRequestReply(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::LOGIN_REQUEST()) - result = new CTS::LoginRequest(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::LOGOUT_REQUEST()) - result = new CTS::LogoutRequest(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == JsonKeys::DISCONNECT_REQUEST()) - result = new CTS::DisconnectRequest(m_handler, client, m_type, m_uuid, m_data); - - if (m_type == LtsWebSocket::JsonKeys::ERROR_()) - return nullptr; - - return checkResult(client, message, result); -} - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h b/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h deleted file mode 100755 index 9155b5e8f..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace LtsProjectionWorkstation -{ - -class AbstractMessageHandler; - -class LTSPROJECTIONWORKSTATION_EXPORT MessageFactory - : public LtsWebSocket::AbstractClientMessageFactory -{ - -public: - MessageFactory(AbstractMessageHandler *handler, LOGGER_T &logger) - : LtsWebSocket::AbstractClientMessageFactory(logger) - , m_handler(handler) - {} - - - LtsWebSocket::AbstractClientMessage *createMessage(LtsWebSocket::ReplyHandler *client, const QString &message) override final; - -private: - AbstractMessageHandler *m_handler; -}; - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp b/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp deleted file mode 100755 index 01d47524a..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp +++ /dev/null @@ -1,210 +0,0 @@ - - - -#include "messages.h" - -#include -#include - -namespace LtsProjectionWorkstation -{ - - -STC::LoginRequestReply::LoginRequestReply(const QString &uuid, Reason reason, int reasonUserId, const QVector &screens) - : Message(nullptr, nullptr, JsonKeys::LOGIN_REPLY(), uuid, QJsonValue()) - , m_screens(screens) - , m_reasonUserId(reasonUserId) - , m_reason(reason) -{ - QJsonObject result; - QJsonArray arr; - for (const VncScreen &vncScreen : screens) - { - QJsonObject screen; - screen.insert(JsonKeys::NAME(), vncScreen.name); - screen.insert(JsonKeys::PORT(), vncScreen.port); - screen.insert(JsonKeys::WIDTH(), vncScreen.geometry.width()); - screen.insert(JsonKeys::HEIGHT(), vncScreen.geometry.height()); - arr.append(screen); - } - result.insert(JsonKeys::REASON(), reason); - result.insert(JsonKeys::USER_ID(), reasonUserId); - result.insert(JsonKeys::SCREENS(), arr); - m_replyData = QJsonValue(result); -} - - -void STC::LoginRequestReply::process() -{ - if (!m_handler || !m_client) return; - QJsonObject dataObj = m_data.toObject(); - m_reason = Reason(dataObj.value(JsonKeys::REASON()).toInt()); - m_reasonUserId = dataObj.value(JsonKeys::USER_ID()).toInt(); - const auto arr = dataObj.value(JsonKeys::SCREENS()).toArray(); - for (const QJsonValue &val : arr) - { - const QJsonObject obj(val.toObject()); - VncScreen screen; - screen.name = obj.value(JsonKeys::NAME()).toString(QStringLiteral("err")); - screen.port = obj.value(JsonKeys::PORT()).toInt(-1); - screen.geometry = QRect(0, 0, - obj.value(JsonKeys::WIDTH()).toInt(-1), - obj.value(JsonKeys::HEIGHT()).toInt(-1)); - m_screens.append(std::move(screen)); - } - m_handler->handleMessage(this); -} - - -bool STC::LoginRequestReply::hasValidData() const -{ - if (!m_handler || !m_client) return false; - QJsonObject obj = m_data.toObject(); - if (obj.value(JsonKeys::REASON()).isUndefined()) return false; - if (obj.value(JsonKeys::USER_ID()).isUndefined()) return false; - if (obj.value(JsonKeys::SCREENS()).isUndefined()) return false; - return true; -} - - - -STC::DedicatedDisconnect::DedicatedDisconnect(const QString &uuid, int userId) - : Message(nullptr, nullptr, JsonKeys::DEDICAED_DISCONNECT(), uuid, QJsonValue()) - , m_userId(userId) -{ - QJsonObject result; - result.insert(JsonKeys::USER_ID(), m_userId); - m_replyData = result; -} - - -void STC::DedicatedDisconnect::process() -{ - if (!m_handler || !m_client) return; - QJsonObject obj = m_data.toObject(); - m_userId = obj.value(JsonKeys::USER_ID()).toInt(-1); - m_handler->handleMessage(this); -} - - -bool STC::DedicatedDisconnect::hasValidData() const -{ - if (!m_handler || !m_client) return false; - QJsonObject obj = m_data.toObject(); - if (obj.value(JsonKeys::USER_ID()).isUndefined()) return false; - return true; -} - - - -CTS::LoginRequest::LoginRequest(const QString &uuid, int userId, int priority, int exerciseId, const QByteArray &settingsData) - : Message(nullptr, nullptr, JsonKeys::LOGIN_REQUEST(), uuid, QJsonValue()) - , m_settingsData(settingsData), m_userId(userId), m_priority(priority), m_exerciseId(exerciseId) -{ - QJsonObject result; - result.insert(JsonKeys::USER_ID(), m_userId); - result.insert(JsonKeys::USER_PRIORITY(), m_priority); - result.insert(JsonKeys::EXERCISE_ID(), m_exerciseId); - result.insert(JsonKeys::SETTINGS_DATA(), QString::fromLatin1(m_settingsData.toBase64())); - m_replyData = result; -} - - -void CTS::LoginRequest::process() -{ - if (!m_handler || !m_client) return; - QJsonObject obj = m_data.toObject(); - m_userId = obj.value(JsonKeys::USER_ID()).toInt(-1); - m_priority = obj.value(JsonKeys::USER_PRIORITY()).toInt(-1); - m_exerciseId = obj.value(JsonKeys::EXERCISE_ID()).toInt(-1); - m_settingsData = QByteArray::fromBase64(obj.value(JsonKeys::SETTINGS_DATA()).toString().toLatin1()); - m_handler->handleMessage(this); -} - - -bool CTS::LoginRequest::hasValidData() const -{ - if (!m_handler || !m_client) return false; - QJsonObject obj = m_data.toObject(); - if (obj.value(JsonKeys::USER_ID()).isUndefined()) return false; - if (obj.value(JsonKeys::EXERCISE_ID()).isUndefined()) return false; - if (obj.value(JsonKeys::USER_PRIORITY()).isUndefined()) return false; - if (obj.value(JsonKeys::SETTINGS_DATA()).isUndefined()) return false; - return true; -} - - - -void CTS::LogoutRequest::process() -{ - if (!m_handler || !m_client) return; - m_handler->handleMessage(this); -} - - -bool CTS::LogoutRequest::hasValidData() const -{ - if (!m_handler || !m_client) return false; - return true; -} - - - -CTS::DisconnectRequest::DisconnectRequest(const QString &uuid, int userId) -: Message(nullptr, nullptr, JsonKeys::DISCONNECT_REQUEST(), uuid, QJsonValue()) -, m_userId(userId) -{ - qDebug() << " 1 m_userId = " << m_userId; - QJsonObject result; - result.insert(JsonKeys::USER_ID(), m_userId); - m_replyData = result; -} - -void CTS::DisconnectRequest::process() -{ - if (!m_handler || !m_client) return; - QJsonObject obj = m_data.toObject(); - m_userId = obj.value(JsonKeys::USER_ID()).toInt(-1); - qDebug() << " 2 m_userId = " << m_userId; - m_handler->handleMessage(this); -} - -bool CTS::DisconnectRequest::hasValidData() const -{ - if (!m_handler || !m_client) return false; - QJsonObject obj = m_data.toObject(); - if (obj.value(JsonKeys::USER_ID()).isUndefined()) return false; - return true; -} - - - -ErrorMessage::ErrorMessage(const QString &uuid, const QString &reason) - : Message(nullptr, nullptr, JsonKeys::ERROR_MESSAGE(), uuid, QJsonValue()) - , m_reason(reason) -{ - QJsonObject result; - result.insert(JsonKeys::REASON(), m_reason); - m_replyData = result; -} - - -void ErrorMessage::process() -{ - if (!m_handler || !m_client) return; - QJsonObject obj = m_data.toObject(); - m_reason = obj.value(JsonKeys::REASON()).toString(QStringLiteral("err")); - m_handler->handleMessage(this); -} - - -bool ErrorMessage::hasValidData() const -{ - if (!m_handler || !m_client) return false; - QJsonObject obj = m_data.toObject(); - if (obj.value(JsonKeys::REASON()).isUndefined()) return false; - return true; -} - - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h b/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h deleted file mode 100755 index 84b793157..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h +++ /dev/null @@ -1,192 +0,0 @@ - - - -#pragma once - -#include "json_keys.h" - -#include -#include - -#include -#include - -#include - -namespace LtsWebSocket { class ReplyHandler; } - -namespace LtsProjectionWorkstation -{ - -class AbstractMessageHandler -{ -public: - virtual ~AbstractMessageHandler() = default; - virtual void handleMessage(LtsWebSocket::AbstractClientMessage *message) = 0; -}; - - -class Reply : public LtsWebSocket::ReplyMessage -{ -public: - Reply(const QString &type, const int typeId, const QString &uuid, const QJsonObject &data) - : LtsWebSocket::ReplyMessage(type, typeId, uuid) - { - m_jsonRepresentation.insert(LtsWebSocket::JsonKeys::DATA(), data); - } -}; - - -class Message : public LtsWebSocket::AbstractClientMessage -{ -public: - Message(AbstractMessageHandler *handler, LtsWebSocket::ReplyHandler *client, const QString &type, const QString &uuid, const QJsonValue &data = QJsonValue()) - : LtsWebSocket::AbstractClientMessage(client, type, 1, uuid, data) - , m_handler(handler) - {} - - LtsWebSocket::ReplyMessage *toReplyMessage() const - { - if (!m_handler || !m_client) - return new Reply(m_type, m_typeId, m_uuid, m_replyData.toObject()); - return nullptr; - } - - void addReply(LtsWebSocket::ReplyMessage *reply) - { - if (m_client) - m_client->addReply(reply); - } - -protected: - AbstractMessageHandler *m_handler; - QJsonValue m_replyData; - -}; - - -class LTSPROJECTIONWORKSTATION_EXPORT ErrorMessage : public Message -{ -public: - using Message::Message; - ErrorMessage(const QString &uuid, const QString &reason); - - - void process() override final; - bool hasValidData() const override final; - -private: - QString m_reason; -}; - -namespace STC -{ - -class LTSPROJECTIONWORKSTATION_EXPORT LoginRequestReply : public Message -{ -public: - enum Reason - { - LoginSuccessful, - OtherUser, - }; - - using Message::Message; - LoginRequestReply(const QString &uuid, Reason reason, int reasonUserId, const QVector &screens); - - - void process() override final; - bool hasValidData() const override final; - - int reasonUserId() const { return m_reasonUserId; } - Reason reason() const { return m_reason; } - const QVector &screens() const { return m_screens; } - -private: - QVector m_screens; - int m_reasonUserId = -1; - Reason m_reason = LoginSuccessful; -}; - -class LTSPROJECTIONWORKSTATION_EXPORT DedicatedDisconnect : public Message -{ -public: - using Message::Message; - DedicatedDisconnect(const QString &uuid, int userId); - - - void process() override final; - bool hasValidData() const override final; - - int userid() const { return m_userId; } - -private: - int m_userId = -1; - -}; - - -} - - -namespace CTS -{ - -class LTSPROJECTIONWORKSTATION_EXPORT LoginRequest : public Message -{ -public: - using Message::Message; - LoginRequest(const QString &uuid, int userId, int priority, int exerciseId, const QByteArray &settingsData); - - - void process() override final; - bool hasValidData() const override final; - - int userId() const { return m_userId; } - int priority() const { return m_priority; } - int exerciseId() const { return m_exerciseId; } - const QByteArray &settingsData() const { return m_settingsData; } - -private: - QByteArray m_settingsData; - int m_userId = 0; - int m_priority = 0; - int m_exerciseId = 0; - -}; - - -class LTSPROJECTIONWORKSTATION_EXPORT LogoutRequest : public Message -{ -public: - using Message::Message; - explicit LogoutRequest(const QString &uuid) - : Message(nullptr, nullptr, JsonKeys::LOGOUT_REQUEST(), uuid, QJsonValue()) - {} - - - void process() override final; - bool hasValidData() const override final; - -}; - - -class LTSPROJECTIONWORKSTATION_EXPORT DisconnectRequest : public Message -{ -public: - using Message::Message; - explicit DisconnectRequest(const QString &uuid, int userId); - - - void process() override final; - bool hasValidData() const override final; - - int userId() const { return m_userId; } -private: - int m_userId{0}; - -}; - -} - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp b/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp deleted file mode 100755 index 6ca832899..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp +++ /dev/null @@ -1,21 +0,0 @@ - - - -#include "reply_message.h" - -namespace LtsProjectionWorkstation -{ - -LoginReply::LoginReply(const QJsonObject &data, const QString &uuid) - : LtsWebSocket::ReplyMessage(JsonKeys::LOGIN_REPLY(), uuid) -{ - m_jsonRepresentation.insert(LtsWebSocket::JsonKeys::DATA(), data); -} - -LogoutReply::LogoutReply(const QJsonObject &data, const QString &uuid) - : LtsWebSocket::ReplyMessage(JsonKeys::LOGOUT_REPLY(), uuid) -{ - m_jsonRepresentation.insert(LtsWebSocket::JsonKeys::DATA(), data); -} - -} diff --git a/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h b/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h deleted file mode 100755 index e3f4235e7..000000000 --- a/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h +++ /dev/null @@ -1,24 +0,0 @@ - - - -#include "json_keys.h" - -#include -#include - -namespace LtsProjectionWorkstation -{ - -class LoginReply : public LtsWebSocket::ReplyMessage -{ -public: - explicit LoginReply(const QJsonObject &data, const QString &uuid); -}; - -class LogoutReply : public LtsWebSocket::ReplyMessage -{ -public: - explicit LogoutReply(const QJsonObject &data, const QString &uuid); -}; - -} diff --git a/Lts/Common/LtsProjectionWorkstation/logger.cpp b/Lts/Common/LtsProjectionWorkstation/logger.cpp deleted file mode 100755 index 07b63f904..000000000 --- a/Lts/Common/LtsProjectionWorkstation/logger.cpp +++ /dev/null @@ -1,32 +0,0 @@ - - - -#include "logger.h" - -#include - -namespace LtsProjectionWorkstation -{ - -LOGGER_T getLogger(const char *name) -{ - static const char ns[] = "PWS."; - std::string loggerName; - const size_t len = strlen(name); - loggerName.reserve(sizeof(ns) + len); - loggerName.append(ns, sizeof(ns) - 1); - loggerName.append(name, len); - - try - { - return NGL::NGLLoggerBootstrap::getNGLRootLogger(). - getNGLLogger(loggerName); - } - catch (const std::exception &ex) - { - fprintf(stderr, "Error retrieving logger: %s", ex.what()); - abort(); - } -} - -} diff --git a/Lts/Common/LtsProjectionWorkstation/logger.h b/Lts/Common/LtsProjectionWorkstation/logger.h deleted file mode 100755 index 5c34eddf4..000000000 --- a/Lts/Common/LtsProjectionWorkstation/logger.h +++ /dev/null @@ -1,18 +0,0 @@ - - - -#pragma once - -#include -#include "lts_projection_workstation_export.h" - -namespace LtsProjectionWorkstation -{ -using LOGGER_T = NGL::INGLLogger&; - -LTSPROJECTIONWORKSTATION_EXPORT LOGGER_T getLogger(const char *name); -inline LOGGER_T getLogger(const QString &loggerName) -{ - return getLogger(loggerName.toUtf8().constData()); -} -} diff --git a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp b/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp deleted file mode 100755 index d826cf6af..000000000 --- a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp +++ /dev/null @@ -1,247 +0,0 @@ - - - -#include "lts_projection_workstation.h" -#include "WebSocket/message_factory.h" -#include "WebSocket/json_keys.h" - -#include -#include - -#include - -#include -#include -#include -#include - -namespace LtsProjectionWorkstation -{ - -static LtsProjectionWorkstation *s_ltsProjectionWorkstation = nullptr; - -LtsProjectionWorkstation *LtsProjectionWorkstation::getInstance() -{ - if (!s_ltsProjectionWorkstation) - { - s_ltsProjectionWorkstation = new LtsProjectionWorkstation; - qAddPostRoutine(&cleanupLtsProjectionWorkstation); - } - return s_ltsProjectionWorkstation; -} - -void LtsProjectionWorkstation::cleanupLtsProjectionWorkstation() -{ - delete s_ltsProjectionWorkstation; - s_ltsProjectionWorkstation = nullptr; -} - -bool LtsProjectionWorkstation::hasInstance() -{ - return !!s_ltsProjectionWorkstation; -} - -LtsProjectionWorkstation::LtsProjectionWorkstation(QObject *parent) - : QObject(parent) - , m_logger(getLogger("LtsProjetionWorkstation")) - , m_messageFactory(new MessageFactory(this, m_logger)) - , m_webSocketServer(new LtsWebSocket::LtsWebSocketServer(m_messageFactory, this)) - , m_pendingLoginRequest(nullptr) - , m_vncManager(new VncServerManager(m_logger, this)) -{ - connect(m_webSocketServer, &LtsWebSocket::LtsWebSocketServer::clientDisconnected, - this, &LtsProjectionWorkstation::clientDisconnected); - connect(m_webSocketServer, &LtsWebSocket::LtsWebSocketServer::clientConnected, - this, &LtsProjectionWorkstation::clientConnected); - - int port = NGL::Connections::port(NGL::Connections::eConnection::PWS_WEBSOCKET); - - if (m_webSocketServer->init(QStringLiteral("LtsProjectionWorkstation"), port)) - { - if (m_vncManager->init(qApp->screens())) - m_vncManager->startScreens(); - } -} - -LtsProjectionWorkstation::~LtsProjectionWorkstation() -{ - delete m_vncManager; - delete m_messageFactory; - delete m_webSocketServer; -} - -LoginInformation LtsProjectionWorkstation::pendingLoginRequest() -{ - if (!m_pendingLoginRequest) - return {}; - - LoginInformation result = *m_pendingLoginRequest; - - INFO_LOG(m_logger) << " reset pendingLoginRequest.."; - - delete m_pendingLoginRequest; - m_pendingLoginRequest = nullptr; - - return result; -} - -void LtsProjectionWorkstation::handleMessage(LtsWebSocket::AbstractClientMessage *message) -{ - CTS::LoginRequest *login = dynamic_cast(message); - if (login) - { - INFO_LOG(m_logger) << "Login request received!" - << ", login->userId()=" << login->userId() - << " with login->priority()=" << login->priority() - << " ;;; m_lastLoginRequest.m_userId=" << m_lastLoginRequest.m_userId - << ", m_priority=" << m_lastLoginRequest.m_priority; - - if (login->userId() > 0 && login->priority() > 0 && login->exerciseId() > 0) - { - - if ( login->userId() == m_lastLoginRequest.m_userId || - (login->priority() > m_lastLoginRequest.m_priority) - ) - { - - m_webSocketServer->sendToAllClients( (new STC::DedicatedDisconnect(QUuid::createUuid().toString(), login->userId()))->toReplyMessage() ); - - - - DEBUG_LOG(m_logger) << "Login request received and DedicatedDisconnect distributed, wait some seconds..."; - int i=0; - while(i<20) - { - DEBUG_LOG(m_logger) << "Login request received and DedicatedDisconnect distributed, wait some seconds...i=" << i; - qApp->processEvents(QEventLoop::AllEvents, 50); - QThread::msleep(50); - ++i; - } - INFO_LOG(m_logger) << "Login request received and DedicatedDisconnect distributed, wait some seconds... done"; - - - login->addReply((new STC::LoginRequestReply(QUuid::createUuid().toString(), - STC::LoginRequestReply::LoginSuccessful, - login->userId(), - m_vncManager->screens()) - )->toReplyMessage()); - - m_lastLoginRequest.m_userId = login->userId(); - m_lastLoginRequest.m_priority = login->priority(); - m_lastLoginRequest.m_exerciseId = login->exerciseId(); - m_lastLoginRequest.m_settingsData = login->settingsData(); - - slotLoginRequestReceived(login->userId(), login->exerciseId()); - - INFO_LOG(m_logger) << "Valid login request received -> userId: " << login->userId() - << " priority: " << login->priority() - << " exerciseId: " << login->exerciseId(); - DEBUG_LOG(m_logger) << "Logging in now!"; - - } - else - { - login->addReply((new STC::LoginRequestReply(QUuid::createUuid().toString(), - STC::LoginRequestReply::OtherUser, - m_lastLoginRequest.m_userId, - QVector()) - )->toReplyMessage()); - - DEBUG_LOG(m_logger) << "Higher prioritized login received -> userId: " << login->userId() - << " priority: " << login->priority() - << " exerciseId: " << login->exerciseId(); - - DEBUG_LOG(m_logger) << "Last login data was -> userId: " << m_lastLoginRequest.m_userId - << " priority: " << m_lastLoginRequest.m_priority - << " exerciseId: " << m_lastLoginRequest.m_exerciseId; - } - } - else - ERROR_LOG(m_logger) << "invalid login request received -> userId: " << login->userId() - << " priority: " << login->priority() - << " exerciseId: " << login->exerciseId(); - - return; - } - - CTS::LogoutRequest *logout = dynamic_cast(message); - if (logout) - { - DEBUG_LOG(m_logger) << "Logging out now!"; - slotLogoutRequestReceived(); - return; - } - - CTS::DisconnectRequest *discon = dynamic_cast(message); - if (discon) - { - - - - - - - - - int disconnectingUserId = discon->userId(); - INFO_LOG(m_logger) << "Disconnecting now! " - << ", disconnectingUserId=" << disconnectingUserId - << ", m_pendingLoginRequest=" << m_pendingLoginRequest; - m_lastLoginRequest = LoginInformation(); - return; - } -} - -void LtsProjectionWorkstation::slotLoginRequestReceived(int userId, int exerciseId) -{ - INFO_LOG(m_logger) << "slotLoginRequestReceived.." - << ", userId=" << userId - << ", exerciseId=" << exerciseId; - delete m_pendingLoginRequest; - m_pendingLoginRequest = new LoginInformation(m_lastLoginRequest); - emit signalLoginRequestReceived(userId, exerciseId); -} - -void LtsProjectionWorkstation::slotLogoutRequestReceived() -{ - emit signalLogoutRequestReceived(); -} - - -void LtsProjectionWorkstation::slotLogoffDone() -{ - if (hasPendingLoginRequest()) - return; - for (LtsWebSocket::ClientConnection *con : qAsConst(m_connections)) - con->webSocket()->close(); -} - - -void LtsProjectionWorkstation::clientDisconnected(LtsWebSocket::ClientConnection *client) -{ - INFO_LOG(m_logger) << LOGFUNCTION << " START" - << ", m_connections.size = " << m_connections.size(); - - m_connections.removeOne(client); - - if ( m_connections.size() == 0 && (m_lastLoginRequest.m_userId != 0 || m_lastLoginRequest.m_priority != 0)) - { - INFO_LOG(m_logger) << LOGFUNCTION << " reset m_lastLoginRequest"; - m_lastLoginRequest = LoginInformation(); - } - INFO_LOG(m_logger) << LOGFUNCTION << " END" - << ", m_connections.size = " << m_connections.size(); -} - -void LtsProjectionWorkstation::clientConnected(LtsWebSocket::ClientConnection *client) -{ - m_connections.append(client); -} - -void LtsProjectionWorkstation::setProjectionOperatorPreset(const QString &operatorName, const QString &operatorPassword) -{ - m_projectionOperatorPreset.first = operatorName; - m_projectionOperatorPreset.second = operatorPassword; -} - -} diff --git a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h b/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h deleted file mode 100755 index aedeb160a..000000000 --- a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h +++ /dev/null @@ -1,84 +0,0 @@ - - - -#pragma once - -#include "lts_projection_workstation_export.h" -#include "vnc_server_manager.h" -#include "WebSocket/messages.h" -#include "logger.h" - -#include - -#include -#include - -namespace LtsWebSocket { class LtsWebSocketServer; } -namespace LtsProjectionWorkstation -{ - -struct LoginInformation -{ - LoginInformation() = default; - LoginInformation(int userId, int priority, int exerciseId, const QByteArray &settingsData) - : m_settingsData(settingsData), m_userId(userId), m_priority(priority), m_exerciseId(exerciseId) {} - - QByteArray m_settingsData; - int m_userId = 0; - int m_priority = 0; - int m_exerciseId = 0; -}; - -class MessageFactory; -class LTSPROJECTIONWORKSTATION_EXPORT LtsProjectionWorkstation : public QObject, public AbstractMessageHandler -{ - Q_OBJECT - -public: - static LtsProjectionWorkstation *getInstance(); - static bool hasInstance(); - - const LoginInformation &lastLoginRequest() const { return m_lastLoginRequest; } - LoginInformation pendingLoginRequest(); - bool hasPendingLoginRequest() const { return !!m_pendingLoginRequest; } - void setProjectionOperatorPreset(const QString &operatorName, const QString &operatorPassword); - const QPair &projectionOperatorPreset() const { return m_projectionOperatorPreset; } - - - void handleMessage(LtsWebSocket::AbstractClientMessage *message) override final; - -public slots: - void slotLoginRequestReceived(int userId, int exerciseId); - void slotLogoutRequestReceived(); - - void slotLogoffDone(); - -signals: - void signalLoginRequestReceived(int userId, int exerciseId); - void signalLogoutRequestReceived(); - -private slots: - void clientDisconnected(LtsWebSocket::ClientConnection* client); - void clientConnected(LtsWebSocket::ClientConnection* client); - -private: - explicit LtsProjectionWorkstation(QObject *parent = nullptr); - ~LtsProjectionWorkstation(); - static void cleanupLtsProjectionWorkstation(); - - LOGGER_T m_logger; - MessageFactory *m_messageFactory; - LtsWebSocket::LtsWebSocketServer *m_webSocketServer; - - - LoginInformation m_lastLoginRequest; - - LoginInformation *m_pendingLoginRequest; - QPair m_projectionOperatorPreset; - - QVector m_connections; - - VncServerManager *m_vncManager; -}; - -} diff --git a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h b/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h deleted file mode 100755 index a65af195b..000000000 --- a/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include - -#ifndef LTSPROJECTIONWORKSTATION_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSPROJECTIONWORKSTATION_EXPORT -# else -# if defined(MAKE_LTSPROJECTIONWORKSTATION_LIB) -# define LTSPROJECTIONWORKSTATION_EXPORT Q_DECL_EXPORT -# else -# define LTSPROJECTIONWORKSTATION_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif diff --git a/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp b/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp deleted file mode 100755 index ce89f931a..000000000 --- a/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp +++ /dev/null @@ -1,194 +0,0 @@ - - - -#include "vnc_server_manager.h" - -#include -#include -#include - -namespace LtsProjectionWorkstation -{ - -VncServerManager::VncServerManager(LOGGER_T &logger, QObject *parent) - : QObject(parent) - , m_logger(logger) -{ -} - - -VncServerManager::~VncServerManager() -{ - stopScreens(); -} - -QList sortScreen(const QList &screens) -{ - auto ret(screens); - - auto doSortScreens = [](const QScreen *s1, const QScreen *s2) -> bool - { - const auto tl1 = s1->geometry().topLeft(); - const auto tl2 = s2->geometry().topLeft(); - if (tl1.y() > tl2.y()) - return true; - if (tl1.y() < tl2.y()) - return false; - return tl1.x() < tl2.x(); - }; - std::sort(ret.begin(), ret.end(), doSortScreens); - return ret; -} - -bool VncServerManager::init(const QList &screens) -{ - killScreens(); - createPwdFile(); - - const auto sortedScreens = sortScreen(screens); - - DEBUG_LOG(m_logger) << "Setup VNCScreens with " << screens.size() << " screens"; - - int count = 1; - - static constexpr char VNC_START_ARGS[] = "-rfbauth,%1/.vnc/passwd,-clip,%2x%3+%4+%5,-loop100,-forever,-display,:0,-threads,-noxdamage"; - - - for (QScreen *screen : sortedScreens) - { - VncScreen vncScreen; - - vncScreen.geometry = screen->geometry(); - vncScreen.name = QStringLiteral("Display%1").arg(count); - vncScreen.proc = new QProcess(this); - QString startArgs = QString::fromLatin1(VNC_START_ARGS).arg(QDir::home().absolutePath()) - .arg(screen->geometry().width()).arg(screen->geometry().height()) - .arg(screen->geometry().x()).arg(screen->geometry().y()); - - vncScreen.proc->setProgram(QStringLiteral("x11vnc")); - vncScreen.proc->setArguments(startArgs.split(QLatin1Char(','))); - - NOTICE_LOG(m_logger) << "Started VNC for Screen " << count << " with geometry " - << screen->geometry().width() << "," << screen->geometry().height() << "+" - << screen->geometry().x() << "+" << screen->geometry().y(); - - m_vncScreens.push_back(vncScreen); - count++; - } - - DEBUG_LOG(m_logger) << m_vncScreens.size() << " VNCScreen(s) created - Setup completed."; - return true; -} - -void VncServerManager::startScreens() -{ - DEBUG_LOG(m_logger) << "Starting " << m_vncScreens.size() << " VNCScreen(s) ... "; - for (const VncScreen &s : qAsConst(m_vncScreens)) - { - connect(s.proc, &QProcess::readyReadStandardOutput, - this, &VncServerManager::slotReadStandardOutput, Qt::UniqueConnection); - connect(s.proc, &QProcess::errorOccurred, - this, &VncServerManager::slotErrorOccured, Qt::UniqueConnection); - s.proc->start(); - } - for (const VncScreen &s : qAsConst(m_vncScreens)) - { - if (!s.proc->waitForStarted(2000)) - ERROR_LOG(m_logger) << "Can not start process '" << s.proc->program() << "': " - << s.proc->errorString(); - } - DEBUG_LOG(m_logger) << "Done"; -} - - -void VncServerManager::stopScreens() -{ - DEBUG_LOG(m_logger) << "Stopping " << m_vncScreens.size() << " VNCScreen(s) ..."; - - for (const VncScreen &s : qAsConst(m_vncScreens)) - { - s.proc->disconnect(this); - s.proc->terminate(); - } - for (const VncScreen &s : qAsConst(m_vncScreens)) - { - if (!s.proc->waitForFinished(2000)) - s.proc->kill(); - } - DEBUG_LOG(m_logger) << "Done"; -} - - -void VncServerManager::killScreens() -{ - DEBUG_LOG(m_logger) << "Killing all exisiting x11vnc processes ..."; - - SingleShotProcess ssp(QStringLiteral("killall x11vnc")); - ssp.execute(); - DEBUG_LOG(m_logger) << "Done"; -} - - -void VncServerManager::createPwdFile() -{ - DEBUG_LOG(m_logger) << "(Re)creating x11vnc password-file ..."; - - SingleShotProcess ssp(QStringLiteral("x11vnc -storepasswd ctc11de %1/.vnc/passwd") - .arg(QDir::home().absolutePath())); - ssp.execute(); - DEBUG_LOG(m_logger) << "Done"; -} - - -void VncServerManager::slotReadStandardOutput() -{ - QProcess *p = qobject_cast(QObject::sender()); - if (!p) - return; - - QString stdOut = QString::fromLocal8Bit(p->readAllStandardOutput()); - QRegularExpressionMatch match = QRegularExpression(QStringLiteral("PORT=(.*?)\\s")).match(stdOut); - if (!match.hasMatch()) - { - WARNING_LOG(m_logger) << "Could not retrieve port information from stdout of " << p->program(); - return; - } - const quint16 port = match.capturedRef(1).toInt(); - if (port == 0) - return; - for (VncScreen &screen : m_vncScreens) - { - if (screen.proc != p) - continue; - screen.port = port; - disconnect(screen.proc, &QProcess::readyReadStandardOutput, nullptr, nullptr); - break; - } -} - - -void VncServerManager::slotErrorOccured() -{ - QProcess *p = qobject_cast(QObject::sender()); - if (p) - ERROR_LOG(m_logger) << p->errorString(); -} - - - - -SingleShotProcess::SingleShotProcess(const QString &cmd, bool startImmediatly) - : m_cmd(cmd) -{ - if (startImmediatly) execute(); -} - -bool SingleShotProcess::execute() -{ - m_process.start(m_cmd); - return m_process.waitForFinished(); -} - - - -} diff --git a/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h b/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h deleted file mode 100755 index 3e1f01441..000000000 --- a/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h +++ /dev/null @@ -1,69 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include -#include - -#include "logger.h" - -class QScreen; - -namespace LtsProjectionWorkstation -{ - -class SingleShotProcess -{ -public: - explicit SingleShotProcess(const QString &cmd, bool startImmediatly = false); - - bool execute(); - -private: - QProcess m_process; - QString m_cmd; -}; - - -struct VncScreen -{ - QString name; - QProcess *proc = nullptr; - QRect geometry; - quint16 port = 0; -}; - - -class VncServerManager : public QObject -{ - Q_OBJECT -public: - explicit VncServerManager(LOGGER_T &logger, QObject *parent = nullptr); - - ~VncServerManager(); - - bool init(const QList &screens); - - const QVector &screens() const { return m_vncScreens; } - - - - void startScreens(); - void stopScreens(); - void killScreens(); - void createPwdFile(); - -private slots: - void slotReadStandardOutput(); - void slotErrorOccured(); - -private: - QVector m_vncScreens; - LOGGER_T &m_logger; -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/CMakeLists.txt b/Lts/Common/LtsScriptTimeline/CMakeLists.txt deleted file mode 100755 index 738b921a6..000000000 --- a/Lts/Common/LtsScriptTimeline/CMakeLists.txt +++ /dev/null @@ -1,70 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2017-03-15 -# - - -# Test -#ADD_SUBDIRECTORY(Test) Not needed anymore + old 'api' - - -# Library -SET(LtsScriptTimeline_SRCS - tools.h - tools.cpp - indicator.h - indicator.cpp - script_timeline.h - script_timeline.cpp - track_content_widget.h - track_content_widget.cpp - track_entry.h - track_entry_painters.h - track_entry_painters.cpp - track_entry_previewer.h - track_entry_previewer.cpp - track_entry_preview_widget.h - track_entry_preview_widget.cpp - track_select_handler.h - track_select_handler.cpp - track_resize_handler.cpp - track_resize_handler.h - track_drag_handler.h - track_drag_handler.cpp - track_widget.h - track_widget.cpp - track_header.h - track_header.cpp - filter_track_widget.h - filter_track_widget.cpp - filter_background_widget.h - filter_background_widget.cpp - temporary_pointer_vector.h -) - -SET(LtsScriptTimeline_UICS - Ui/indicator.ui - Ui/script_timeline.ui - Ui/track_widget.ui - Ui/filter_track_widget.ui - Ui/track_entry_preview_widget.ui -) - -NGL_WRAP_UI(LtsScriptTimeline_SRCS ${LtsScriptTimeline_UICS}) - -NGL_ADD_LIBRARY2(WS LtsScriptTimeline SHARED ${LtsScriptTimeline_SRCS} ${LtsScriptTimeline_UICS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsScriptTimeline - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsScriptTimeline - Qt5::Core - Qt5::Widgets - LtsWidgets - LtsSessionData -) diff --git a/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt b/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt deleted file mode 100755 index 76ec481d5..000000000 --- a/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2017-03-15 -# - -# Test-Executable - -SET(LtsScriptTimelineTest_SRCS - main.cpp - mainwindow.h - mainwindow.cpp -) - -SET(LtsScriptTimelineTest_UICS - mainwindow.ui -) - -NGL_WRAP_UI(LtsScriptTimelineTest_SRCS ${LtsScriptTimelineTest_UICS}) - -NGL_ADD_EXECUTABLE(TOOLSWS LtsScriptTimelineTest ${LtsScriptTimelineTest_SRCS}) -NGL_TARGET_LINK_LIBRARIES(LtsScriptTimelineTest LtsScriptTimeline) diff --git a/Lts/Common/LtsScriptTimeline/Test/main.cpp b/Lts/Common/LtsScriptTimeline/Test/main.cpp deleted file mode 100755 index 30c682cea..000000000 --- a/Lts/Common/LtsScriptTimeline/Test/main.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "mainwindow.h" -#include - - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - MainWindow w; - w.show(); - - return a.exec(); -} diff --git a/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp b/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp deleted file mode 100755 index f5acea730..000000000 --- a/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" - -#include - -#include -#include - - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) - , m_selectedTe(nullptr) - , m_currentNewTopicEditor(nullptr) - , m_currentEditTopicEditor(nullptr) -{ - ui->setupUi(this); - - ui->masterSplitter->setStretchFactor(ui->masterSplitter->indexOf(ui->masterSplitter), 20); - ui->masterSplitter->setStretchFactor(ui->masterSplitter->indexOf(ui->toolBox), 0); - - ui->detailSplitter->setStretchFactor(ui->masterSplitter->indexOf(ui->scriptTimeline), 20); - ui->detailSplitter->setStretchFactor(ui->masterSplitter->indexOf(ui->detailLayout), 0); - - - connect(ui->scriptTimeline, &LtsScriptTimeline::ScriptTimeline::signalTrackEntrySelectionChanged, this, &MainWindow::slotTrackEntrySelectionChanged); - - connect(ui->pbTopicAddAudio, &QPushButton::clicked, - this, [this]() - { - LtsScriptTimeline::TrackEntry *te = new LtsScriptTimeline::TrackEntry(LtsScriptTimeline::TrackEntryType::Audio); - te->time = -1; - te->length = 30; - te->topicId = 3; - - if (m_currentNewTopicEditor) - delete m_currentNewTopicEditor; - - LtsScriptTimeline::AudioTopicEditor *ate = new LtsScriptTimeline::AudioTopicEditor(te); - connect(ate, &LtsScriptTimeline::AudioTopicEditor::destroyed, this, [this]() { m_currentNewTopicEditor = nullptr; }); - m_currentNewTopicEditor = ate; - ui->newTopicTab->layout()->addWidget(ate); - ui->detailLayout->setCurrentWidget(ui->newTopicTab); - }); - - - ui->scriptTimeline->addTrack(ui->scriptTimeline->newTrackName()); - - - - - qCritical() << "THIS EXAMPLE NO LONGER WORKS!"; - - - -} - - -void MainWindow::disconnectTrackEntry() -{ - if (!m_selectedTe) - return; - - m_selectedTe = nullptr; - - if (m_currentEditTopicEditor) - { - - delete m_currentEditTopicEditor; - m_currentEditTopicEditor = nullptr; - ui->lblNoTopicSelected->setVisible(true); - } - - ui->lblNoTopicSelected->setVisible(true); -} - - -void MainWindow::slotTrackEntrySelectionChanged(LtsScriptTimeline::TrackEntry *te) -{ - qDeleteAll(ui->topicEditorTab->layout()->children()); - disconnectTrackEntry(); - - if (!te) - return; - - ui->lblNoTopicSelected->setVisible(false); - - m_selectedTe = te; - - - - - LtsScriptTimeline::AudioTopicEditor *ate = new LtsScriptTimeline::AudioTopicEditor(te); - connect(ate, &LtsScriptTimeline::AudioTopicEditor::destroyed, - this, [this]() { m_currentEditTopicEditor = nullptr; }); - m_currentEditTopicEditor = ate; - ui->topicEditorTab->layout()->addWidget(ate); -} - - -MainWindow::~MainWindow() -{ - delete ui; -} diff --git a/Lts/Common/LtsScriptTimeline/Test/mainwindow.h b/Lts/Common/LtsScriptTimeline/Test/mainwindow.h deleted file mode 100755 index 60c094c8a..000000000 --- a/Lts/Common/LtsScriptTimeline/Test/mainwindow.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -namespace Ui -{ -class MainWindow; -} - -namespace LtsScriptTimeline -{ -class TrackEntry; -} - - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - -private: - Ui::MainWindow *ui; - LtsScriptTimeline::TrackEntry *m_selectedTe; - QWidget *m_currentNewTopicEditor; - QWidget *m_currentEditTopicEditor; - - void disconnectTrackEntry(); - -private slots: - void slotTrackEntrySelectionChanged(LtsScriptTimeline::TrackEntry *te); -}; diff --git a/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui b/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui deleted file mode 100755 index 7057f2935..000000000 --- a/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui +++ /dev/null @@ -1,564 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1084 - 834 - - - - MainWindow - - - - - - - Qt::Horizontal - - - - Qt::Vertical - - - - - 0 - 0 - - - - - - 1 - - - - New Topic - - - - - - Topic Editor - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - No Topic selected. - - - - - - - - Layout Editor - - - - - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 212 - 127 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 212 - 127 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 85 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 85 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - - - - - - - - 0 - 0 - - - - 0 - - - - - 0 - 0 - 453 - 782 - - - - Add Topics - - - - - - Add Audio Topic - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - LtsScriptTimeline::ScriptTimeline - QWidget -
LtsScriptTimeline/script_timeline.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui b/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui deleted file mode 100755 index ba98d128d..000000000 --- a/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui +++ /dev/null @@ -1,126 +0,0 @@ - - - FilterTrackWidget - - - - 0 - 0 - 828 - 43 - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - - - Filters - - - - - - - - - - true - - - - - - - - 0 - 0 - - - - Delete - - - - - - - - - - - 0 - 0 - - - - - 0 - 40 - - - - - 16777215 - 40 - - - - - - - - - LtsScriptTimeline::FilterBackgroundWidget - QWidget -
filter_background_widget.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/Ui/indicator.ui b/Lts/Common/LtsScriptTimeline/Ui/indicator.ui deleted file mode 100755 index f614f31f8..000000000 --- a/Lts/Common/LtsScriptTimeline/Ui/indicator.ui +++ /dev/null @@ -1,63 +0,0 @@ - - - Indicator - - - - 0 - 0 - 822 - 88 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - - - - Tracks - - - - - - - - - - - - - LtsScriptTimeline::TrackHeader - QWidget -
track_header.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui b/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui deleted file mode 100755 index 3aef25760..000000000 --- a/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui +++ /dev/null @@ -1,205 +0,0 @@ - - - ScriptTimeline - - - - 0 - 0 - 570 - 397 - - - - - 0 - 0 - - - - Form - - - - 8 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - true - - - - - 0 - 0 - 568 - 370 - - - - - - - 0 - - - - - - 0 - 50 - - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - 0 - 0 - - - - P - - - - - - - - 0 - 0 - - - - 1 - - - 6 - - - 1 - - - 1 - - - Qt::Horizontal - - - true - - - false - - - true - - - - - - - - 0 - 0 - - - - L - - - true - - - false - - - - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - true - - - - - - - - - - LtsScriptTimeline::Indicator - QWidget -
indicator.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui b/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui deleted file mode 100755 index 88eb706df..000000000 --- a/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui +++ /dev/null @@ -1,99 +0,0 @@ - - - TrackEntryPreviewWidget - - - - 0 - 0 - 296 - 78 - - - - - 0 - 0 - - - - Form - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Drag & Drop: - - - - - - - [Topic id:] - - - - - - - - 200 - 50 - - - - - - - - ? - - - - - - - [Topic length:] - - - - - - - ? - - - - - - - - - - LtsScriptTimeline::TrackEntryPreviewer - QWidget -
LtsScriptTimeline/track_entry_previewer.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui b/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui deleted file mode 100755 index d00bd2b5e..000000000 --- a/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui +++ /dev/null @@ -1,245 +0,0 @@ - - - TrackWidget - - - - 0 - 0 - 925 - 120 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 300 - 0 - - - - - - - - - - - - - - Name of Track: - - - - - - - 128 - - - - - - - - 0 - 0 - - - - Edit - - - - - - - - 0 - 0 - - - - Apply - - - - - - - - 0 - 0 - - - - Cancel - - - - - - - - 0 - 0 - - - - More - - - - - - - - 0 - 0 - - - - Add filter track - - - + - - - - - - - - - - - 0 - 0 - - - - - 0 - 40 - - - - - 16777215 - 40 - - - - - - - - - - - - 0 - 0 - - - - - 0 - 40 - - - - - 16777215 - 16777215 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - LtsScriptTimeline::TrackContentWidget - QWidget -
track_content_widget.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp b/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp deleted file mode 100755 index 988a14c3e..000000000 --- a/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "filter_background_widget.h" -#include "track_content_widget.h" -#include "tools.h" -#include "track_entry.h" -#include "script_timeline.h" - -#include - - -namespace LtsScriptTimeline -{ - -FilterBackgroundWidget::FilterBackgroundWidget(QWidget *parent) - : QWidget(parent) - , m_readonly(false) - , m_scriptTimeline(nullptr) - , m_tools(nullptr) - , m_trackWidget(nullptr) -{ - -} - - -void FilterBackgroundWidget::setReadonly(bool readonly) -{ - m_readonly = readonly; - - for (auto i = m_filterContentWidgets.begin(); i != m_filterContentWidgets.end(); ++i) - { - TrackContentWidget *tcw = *i; - tcw->setReadonly(readonly); - } -} - - -void FilterBackgroundWidget::paintEvent(QPaintEvent *event) -{ - (void)event; - - QPainter painter(this); - - painter.save(); - - painter.setBrush(QColor("lightgray")); - painter.drawRect(rect()); - - painter.restore(); -} - - -void FilterBackgroundWidget::setTools(Tools *tools) -{ - m_tools = tools; -} - - -void FilterBackgroundWidget::adjustFilterContentWidgets(const TrackEntry *te) -{ - TrackContentWidget *tcw = m_filterContentWidgets.value(te->m_topicId, nullptr); - - if (tcw == nullptr) - { - trackEntryAdded(te); - return; - } - - QRect currentGeo = tcw->geometry(); - int currentX = currentGeo.x(); - int currentWidth = currentGeo.width(); - - - - int x = m_tools->getPos(te->m_startTime); - int width = m_tools->getLength(te->m_startTime, te->m_length); - - if (currentX != x || currentWidth != width) - { - - tcw->setGeometry(x, currentGeo.y(), width, currentGeo.height()); - - } - else - { - - } -} - - -void FilterBackgroundWidget::slotChangesMade() -{ - QObject *tcwObj = sender(); - TrackContentWidget *tcw = qobject_cast(tcwObj); - - if (tcw == nullptr) - { - return; - } - - int topicId = m_filterContentWidgets.key(tcw, -1); - - if (topicId == -1) - { - return; - } - - emit signalChangesMade(topicId); -} - - -void FilterBackgroundWidget::trackEntryDeleted(const int topicId) -{ - auto fcwI = m_filterContentWidgets.find(topicId); - - if (fcwI != m_filterContentWidgets.end()) - { - TrackContentWidget *tcw = *fcwI; - m_filterContentWidgets.erase(fcwI); - tcw->deleteLater(); - } -} - - -void FilterBackgroundWidget::addFilterEntry(TrackEntry *main, TrackEntry *filter) -{ - auto fcwI = m_filterContentWidgets.find(main->m_topicId); - - if (fcwI != m_filterContentWidgets.end()) - { - TrackContentWidget *tcw = *fcwI; - tcw->addTrackEntry(filter, false); - emit tcw->signalChangesMade(); - } -} - - -void FilterBackgroundWidget::trackEntryAdded(const TrackEntry *te) -{ - auto fcwI = m_filterContentWidgets.find(te->m_topicId); - - if (fcwI == m_filterContentWidgets.end()) - { - TrackContentWidget *tcw = new TrackContentWidget(this, true, m_tools, te->m_topicId); - - tcw->setTrackWidget(m_trackWidget); - tcw->setReadonly(m_readonly); - tcw->setScriptTimeline(m_scriptTimeline); - tcw->setGeometry(-1, 0, 0, height()); - tcw->show(); - - connect(tcw, &TrackContentWidget::signalChangesMade, - this, &FilterBackgroundWidget::slotChangesMade); - connect(tcw, &TrackContentWidget::signalUnselectAll, - this, &FilterBackgroundWidget::signalUnselectAll); - connect(tcw, &TrackContentWidget::signalTrackEntrySelectionChanged, - this, &FilterBackgroundWidget::signalTrackEntrySelectionChanged); - connect(tcw, &TrackContentWidget::signalTrackEntryDeleted, - this, &FilterBackgroundWidget::signalTrackEntryDeleted); - connect(tcw, &TrackContentWidget::signalEditEntry, - this, &FilterBackgroundWidget::signalEditEntry); - - connect(tcw, &TrackContentWidget::signalStartRubberband, - m_scriptTimeline, &ScriptTimeline::slotStartRubberband); - connect(tcw, &TrackContentWidget::signalModifyRubberband, - m_scriptTimeline, &ScriptTimeline::slotModifyRubberband); - connect(tcw, &TrackContentWidget::signalEscapeRubberband, - m_scriptTimeline, &ScriptTimeline::slotEscapeRubberband); - connect(tcw, &TrackContentWidget::signalStopRubberband, - m_scriptTimeline, &ScriptTimeline::slotStopRubberband); - connect(tcw, &TrackContentWidget::signalSelectAllGlobal, - m_scriptTimeline, &ScriptTimeline::selectAll); - connect(tcw, &TrackContentWidget::signalCopyTopics, - m_scriptTimeline, &ScriptTimeline::slotCopyTopics); - connect(tcw, &TrackContentWidget::signalPasteTopics, - m_scriptTimeline, &ScriptTimeline::slotPasteTopics); - connect(tcw, &TrackContentWidget::signalCutTopics, - m_scriptTimeline, &ScriptTimeline::slotCutTopics); - connect(tcw, &TrackContentWidget::signalDeleteSelectedTopics, - m_scriptTimeline, &ScriptTimeline::slotDeleteSelectedTopics); - connect(tcw, &TrackContentWidget::signalSave, - m_scriptTimeline, &ScriptTimeline::signalSave); - connect(tcw, &TrackContentWidget::signalMoveAll, - m_scriptTimeline, &ScriptTimeline::slotMoveAll); - - m_filterContentWidgets.insert(te->m_topicId, tcw); - } - - adjustFilterContentWidgets(te); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/filter_background_widget.h b/Lts/Common/LtsScriptTimeline/filter_background_widget.h deleted file mode 100755 index ecb45321a..000000000 --- a/Lts/Common/LtsScriptTimeline/filter_background_widget.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "track_content_widget.h" - -#include -#include - - -namespace LtsScriptTimeline -{ -class TrackEntry; -class ScriptTimeline; -class Tools; - - -class FilterBackgroundWidget : public QWidget -{ - Q_OBJECT - -public: - explicit FilterBackgroundWidget(QWidget *parent = nullptr); - const QMap &filterContentWidgets() const { return m_filterContentWidgets; } - void adjustFilterContentWidgets(const TrackEntry *te); - void trackEntryAdded(const TrackEntry *te); - void trackEntryDeleted(const int topicId); - void addFilterEntry(TrackEntry *main, TrackEntry *filter); - void setReadonly(bool readonly); - void setScriptTimeline(ScriptTimeline *scriptTimeline) { m_scriptTimeline = scriptTimeline; } - void setTools(Tools *tools); - void setTrackWidget(TrackWidget *trackWidget) { m_trackWidget = trackWidget; } - -signals: - void signalChangesMade(int topicId); - void signalUnselectAll(); - void signalTrackEntrySelectionChanged(TrackEntry *te); - void signalTrackEntryDeleted(const int topicId); - void signalEditEntry(const int topicId); - -private slots: - void slotChangesMade(); - -private: - QMap m_filterContentWidgets; - bool m_readonly; - ScriptTimeline *m_scriptTimeline; - Tools *m_tools; - TrackWidget *m_trackWidget; - -protected: - void paintEvent(QPaintEvent *event); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp b/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp deleted file mode 100755 index df74bf358..000000000 --- a/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "filter_track_widget.h" -#include "ui_filter_track_widget.h" -#include "track_entry.h" -#include "script_timeline.h" - -#include -#include -#include -#include - - -namespace LtsScriptTimeline -{ - -FilterTrackWidget::FilterTrackWidget(int baseWidth, bool readonly, QWidget *parent) - : QWidget(parent) - , ui(new Ui::FilterTrackWidget) - , m_baseWidth(baseWidth) - , m_readonly(readonly) - , m_tools(nullptr) - , m_trackWidget(nullptr) -{ - ui->setupUi(this); - ui->cbSelect->hide(); - - setReadonly(readonly); - setBaseWidth(baseWidth); - - connect(ui->pbDelete, &QPushButton::clicked, - this, &FilterTrackWidget::signalDeleteMe); - - connect(ui->fbw, &FilterBackgroundWidget::signalChangesMade, - this, &FilterTrackWidget::signalChangesMade); - connect(ui->fbw, &FilterBackgroundWidget::signalUnselectAll, - this, &FilterTrackWidget::signalUnselectAll); - connect(ui->fbw, &FilterBackgroundWidget::signalTrackEntrySelectionChanged, - this, &FilterTrackWidget::signalTrackEntrySelectionChanged); - connect(ui->fbw, &FilterBackgroundWidget::signalEditEntry, - this, &FilterTrackWidget::signalEditEntry); - connect(ui->fbw, &FilterBackgroundWidget::signalTrackEntryDeleted, - this, &FilterTrackWidget::signalTrackEntryDeleted); -} - - -FilterTrackWidget::~FilterTrackWidget() -{ - - delete ui; - -} - - -void FilterTrackWidget::setTrackWidget(TrackWidget *trackWidget) -{ - m_trackWidget = trackWidget; - ui->fbw->setTrackWidget(trackWidget); -} - - -void FilterTrackWidget::setReadonly(bool readonly) -{ - m_readonly = readonly; - ui->pbDelete->setVisible(!readonly); - ui->fbw->setReadonly(readonly); -} - - -void FilterTrackWidget::setTools(Tools *tools) -{ - m_tools = tools; - ui->fbw->setTools(tools); -} - - -void FilterTrackWidget::setScriptTimeline(ScriptTimeline *scriptTimeline) -{ - m_scriptTimeline = scriptTimeline; - ui->fbw->setScriptTimeline(m_scriptTimeline); -} - - -FilterBackgroundWidget *FilterTrackWidget::fbw() -{ - return ui->fbw; -} - -const FilterBackgroundWidget *FilterTrackWidget::fbw() const -{ - return ui->fbw; -} - - - - - - - - -void FilterTrackWidget::setBaseWidth(int width) -{ - ui->left->setMinimumWidth(width); - ui->left->setMaximumWidth(width); -} - - -void FilterTrackWidget::addFilterEntry(TrackEntry *main, TrackEntry *filter) -{ - filter->m_topicId = m_scriptTimeline->nextUniqueId(); - ui->fbw->addFilterEntry(main, filter); -} - - -bool FilterTrackWidget::isSelected() const -{ - return (ui->cbSelect->checkState() == Qt::CheckState::Checked); -} - - -void FilterTrackWidget::unselectAll() -{ - -} - - -void FilterTrackWidget::updateMe() -{ - update(); - -} - - - - - - - - -void FilterTrackWidget::paintEvent(QPaintEvent *event) -{ - - - - - - - - - - - - - QWidget::paintEvent(event); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/filter_track_widget.h b/Lts/Common/LtsScriptTimeline/filter_track_widget.h deleted file mode 100755 index 9209d9329..000000000 --- a/Lts/Common/LtsScriptTimeline/filter_track_widget.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include "track_entry_painters.h" - -#include - - -namespace Ui -{ -class FilterTrackWidget; -} - - -namespace LtsScriptTimeline -{ -class TrackEntry; -class TrackContentWidget; -class FilterBackgroundWidget; -class ScriptTimeline; -class Tools; -class TrackWidget; - - -class LTSSCRIPTTIMELINE_EXPORT FilterTrackWidget : public QWidget -{ - Q_OBJECT - -public: - explicit FilterTrackWidget(int baseWidth, bool readonly = false, QWidget *parent = nullptr); - ~FilterTrackWidget(); - - FilterBackgroundWidget *fbw(); - const FilterBackgroundWidget *fbw() const; - - void setReadonly(bool readonly); - void updateMe(); - void unselectAll(); - bool isSelected() const; - QString trackName() const; - void addFilterEntry(TrackEntry *main, TrackEntry *filter); - void setTrackName(const QString &name); - - void setBaseWidth(int width); - - - void setScriptTimeline(ScriptTimeline *scriptTimeline); - void setTools(Tools *tools); - void setTrackWidget(TrackWidget *trackWidget); - -private: - Ui::FilterTrackWidget *ui; - int m_baseWidth; - bool m_readonly; - ScriptTimeline *m_scriptTimeline; - Tools *m_tools; - TrackWidget *m_trackWidget; - -protected: - void paintEvent(QPaintEvent *event); - -signals: - void signalUnselectAll(); - void signalSelectTrack(); - void signalTrackEntrySelectionChanged(TrackEntry *te); - void signalChangesMade(int topicId); - void signalDeleteMe(); - void signalEditEntry(const int topicId); - void signalTrackEntryDeleted(const int topicId); - -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/indicator.cpp b/Lts/Common/LtsScriptTimeline/indicator.cpp deleted file mode 100755 index cb4e42ff6..000000000 --- a/Lts/Common/LtsScriptTimeline/indicator.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "indicator.h" -#include "ui_indicator.h" -#include - -namespace LtsScriptTimeline -{ - -Indicator::Indicator(QWidget *parent) - : QWidget(parent) - , ui(new Ui::Indicator) -{ - ui->setupUi(this); - - connect(ui->splitter, &QSplitter::splitterMoved, - this, &Indicator::slotSplitterMoved); - - connect(ui->m_widgetTrackHeader, &TrackHeader::signalHeaderIndicatorTimeChanged, - this, &Indicator::signalHeaderIndicatorTimeChanged); - connect(ui->m_widgetTrackHeader, &TrackHeader::signalHeaderIndicatorTimeUserChanged, - this, &Indicator::signalHeaderIndicatorTimeUserChanged); - - QList sizes; - sizes << 100 << 900; - ui->splitter->setSizes(sizes); - - QTimer::singleShot(500, this, [this]() - { - slotSplitterMoved(); - emit signalHeaderIndicatorTimeUserChanged(ui->m_widgetTrackHeader->indicatorTime()); - ui->m_widgetTrackHeader->recreatePositionLabels(); - }); -} - - -Indicator::~Indicator() -{ - delete ui; -} - - -void Indicator::setTools(Tools *tools) -{ - ui->m_widgetTrackHeader->setTools(tools); -} - - -void Indicator::lockIndicator(bool lock) -{ - ui->m_widgetTrackHeader->lockIndicator(lock); -} - - -void Indicator::setIndicatorTime(int time) -{ - ui->m_widgetTrackHeader->setIndicatorTime(time); -} - - -void Indicator::updateMe() -{ - ui->m_widgetTrackHeader->updateMe(); - update(); -} - - -void Indicator::resizeEvent(QResizeEvent *) -{ - slotSplitterMoved(); -} - - -void Indicator::slotSplitterMoved() -{ - int newSize = ui->m_widgetTrackLabel->width() + ui->splitter->handleWidth(); - - if (newSize < 0) - newSize = 0; - - emit signalHandleResized(newSize); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/indicator.h b/Lts/Common/LtsScriptTimeline/indicator.h deleted file mode 100755 index 2b9201a53..000000000 --- a/Lts/Common/LtsScriptTimeline/indicator.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -namespace Ui -{ -class Indicator; -} - -namespace LtsScriptTimeline -{ -class Tools; - -class Indicator : public QWidget -{ - Q_OBJECT - -public: - explicit Indicator(QWidget *parent = nullptr); - ~Indicator(); - - void updateMe(); - void setIndicatorTime(int time); - void lockIndicator(bool lock = true); - void setTools(Tools *tools); - -private: - void resizeEvent(QResizeEvent *event); - - Ui::Indicator *ui; - -signals: - void signalHandleResized(int width); - void signalHeaderIndicatorTimeChanged(int time); - void signalHeaderIndicatorTimeUserChanged(int time); - -private slots: - void slotSplitterMoved(); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/newsrc.tar.xz b/Lts/Common/LtsScriptTimeline/newsrc.tar.xz deleted file mode 100755 index 25e76c824604fc14d90b787838b229c7beedece6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52292 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 z 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

OBFqTj=TCxp<>px>sojZ}Zm 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 - -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsScriptTimeline -{ - -ScriptTimeline::ScriptTimeline(QWidget *parent) - : QWidget(parent) - , ui(new Ui::ScriptTimeline) - , m_uniqueId(0) - , m_scriptId(-1) - , m_baseTrackWidgetWidth(150) - , m_readonly(false) - , m_time(0) - , m_tools(new Tools(this)) - , m_mlmsMaster(nullptr) - , m_ignoreChanges(false) -{ - ui->setupUi(this); - - this->setFocusPolicy(Qt::StrongFocus); - - ui->indicator->setTools(m_tools); - - - ui->trackScrollBar->setMinimum(0); - ui->trackScrollBar->setMaximum(5 * 60); - - connect(ui->zoomSlider, &QSlider::valueChanged, this, &ScriptTimeline::slotZoomValueChanged); - - connect(ui->trackScrollBar, &QScrollBar::sliderReleased, this, &ScriptTimeline::slotScrollbarValueChanged); - connect(ui->trackScrollBar, &QScrollBar::actionTriggered, this, &ScriptTimeline::slotScrollbarValueChanged); - - connect(ui->indicator, &Indicator::signalHandleResized, - this, &ScriptTimeline::slotHeaderHandleResized); - connect(ui->indicator, &Indicator::signalHeaderIndicatorTimeChanged, - this, &ScriptTimeline::slotHeaderIndicatorTimeChanged); - connect(ui->indicator, &Indicator::signalHeaderIndicatorTimeUserChanged, - this, &ScriptTimeline::signalHeaderIndicatorTimeUserChanged); - - - QTimer *t = new QTimer(this); - t->setInterval(1500); - connect(t, &QTimer::timeout, - this, &ScriptTimeline::slotSetNewScrollBarMaximum); - t->start(); - - - - - slotZoomValueChanged(ui->zoomSlider->value()); -} - - -ScriptTimeline::~ScriptTimeline() -{ - m_copyTopics.clear(); - m_ignoreChanges = true; - qDeleteAll(m_clipboard); - delete ui; -} - - -void ScriptTimeline::selectAll() -{ - const QList vecTrackWidgets = getTrackWidgets(); - for (TrackWidget *tw : vecTrackWidgets) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - tcw->selectAll(); - } -} - - -bool ScriptTimeline::checkNewLength(TrackEntry *te, int newLength) -{ - const QList tws = getTrackWidgets(); - TrackContentWidget *tesTcw = nullptr; - - - for (TrackWidget *tw : tws) - { - const QVector &tes = tw->getTrackContentWidget()->getTrackEntries(); - - auto it = std::find_if(tes.begin(), tes.end(), [te](const TrackEntry * a) - { - return (a->m_topicId == te->m_topicId); - }); - - if (it == tes.end()) - continue; - - tesTcw = tw->getTrackContentWidget(); - break; - } - - if (tesTcw == nullptr) - return false; - - - TrackEntry newTe(te); - newTe.m_length = newLength; - return tesTcw->checkForCollisionsSimple(newTe); -} - - -void ScriptTimeline::lockIndicator(bool lock) -{ - ui->indicator->lockIndicator(lock); -} - - -void ScriptTimeline::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Delete) - { - slotDeleteSelectedTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_C) - { - slotCopyTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_V) - { - slotPasteTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_X) - { - slotCutTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_A) - { - selectAll(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_S) - { - emit signalSave(); - } - else - { - const QList vecTrackWidgets = getTrackWidgets(); - for (auto w : vecTrackWidgets) - w->keyPressEvent(event); - } -} - - -void ScriptTimeline::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() != Qt::LeftButton) - return; - slotStartRubberband(event->pos()); -} - - -void ScriptTimeline::mouseMoveEvent(QMouseEvent *) -{ - slotModifyRubberband(); -} - - -void ScriptTimeline::mouseReleaseEvent(QMouseEvent *) -{ - const QList vecTrackWidgets = getTrackWidgets(); - for (auto w : vecTrackWidgets) - w->unselectAll(); - - slotStopRubberband(); -} - - -void ScriptTimeline::slotScroll(int incr) -{ - int oldValue = ui->trackScrollBar->value(); - ui->trackScrollBar->setValue(oldValue + incr); - slotScrollbarValueChanged(); -} - - -void ScriptTimeline::setReadonly(bool readonly) -{ - m_readonly = readonly; - - const QList vecTrackWidgets = getTrackWidgets(); - for (TrackWidget *tw : vecTrackWidgets) - tw->setReadonly(readonly); -} - - -void ScriptTimeline::clear() -{ - m_copyTopics.clear(); - - const QList trackWidgetList = getTrackWidgets(); - - - for (TrackWidget *tw : trackWidgetList) - delete tw; - - qDeleteAll(m_clipboard); - m_clipboard.clear(); - - m_uniqueId = 0; - - updateEverything(); -} - - -void ScriptTimeline::slotSetNewScrollBarMaximum() -{ - int max = 5 * 60; - - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - const QVector &trackEntries = tw->getTrackContentWidget()->getTrackEntries(); - - for (const TrackEntry *te : trackEntries) - { - int maxTeTime = te->m_startTime + te->m_length; - - - if (maxTeTime > max) - { - max = maxTeTime + 5 * 60; - } - } - } - - - ui->trackScrollBar->setMaximum(max); - ui->trackScrollBar->setPageStep(60); - m_tools->m_timelineLength = max; - - updateEverything(); -} - - -void ScriptTimeline::setIndicatorTime(int time) -{ - ui->indicator->setIndicatorTime(time); -} - - -int ScriptTimeline::getCurrentScriptEndTime() -{ - int currEndTime = 0; - const QList listTrackWidgets = getTrackWidgets(); - for (const auto ltw : listTrackWidgets) - { - const auto cont = ltw->getTrackEntries(); - for (const auto pTE : cont) - { - int endTime = pTE->m_startTime + pTE->m_length; - currEndTime = qMax(currEndTime, endTime); - } - } - - return currEndTime; -} - - -void ScriptTimeline::slotHeaderIndicatorTimeChanged(int time) -{ - m_time = time; - - - - - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - tw->setIndicatorTime(time); - - emit signalHeaderIndicatorTimeChanged(time); -} - - -void ScriptTimeline::slotHeaderHandleResized(int newSize) -{ - m_baseTrackWidgetWidth = newSize; - - updateEverything(); - - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - tw->setBaseWidth(newSize); -} - - -void ScriptTimeline::slotDeleteTracks() -{ - int count = 0; - - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - if (tw->isSelected()) - count++; - } - - if (count < 1) - { - QApplication::beep(); - return; - } - - if (QMessageBox::question(this, tr("Delete selected tracks"), tr("Are you sure you want to delete all %1 tracks?").arg(count), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) - return; - - for (TrackWidget *tw : trackWidgetList) - { - if (tw->isSelected()) - tw->deleteLater(); - } - - -} - - -void ScriptTimeline::slotZoomValueChanged(int value) -{ - if (value < 1) - value = 1; - - m_tools->m_zoomFactor = value; - - - - updateEverything(); -} - - - -void ScriptTimeline::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - - { - QPixmap p(QStringLiteral(":/style/portrait-mode.svg")); - int h = ui->trackScrollBar->height(); - QSize s(h, h); - ui->lbPortrait->setPixmap(p.scaled(s, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - ui->lbPortrait->setFixedSize(s); - } - { - QPixmap p(QStringLiteral(":/style/landscape-mode.svg")); - int h = ui->trackScrollBar->height(); - QSize s(h, h); - ui->lbLandscape->setPixmap(p.scaled(s, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - ui->lbLandscape->setFixedSize(s); - } - - updateEverything(); -} - - -void ScriptTimeline::slotUnselectAll() -{ - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - tw->unselectAll(); -} - - -int ScriptTimeline::getFilterTrackEntryRealTime(TrackEntry *te) -{ - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - { - for (FilterTrackWidget *ftw : tw->filterLines()) - { - const QMap &filterContentWidgets = ftw->fbw()->filterContentWidgets(); - - int foundMainTrackEntryId = -1; - for (auto itFcw = filterContentWidgets.begin(); itFcw != filterContentWidgets.end(); ++itFcw) - { - const TrackContentWidget *tcw = itFcw.value(); - const QVector &trackEntries = tcw->getTrackEntries(); - - for (const TrackEntry *tempFilterTe : trackEntries) - { - if (tempFilterTe->m_topicId == te->m_topicId) - { - foundMainTrackEntryId = itFcw.key(); - break; - } - } - if (foundMainTrackEntryId != -1) - break; - } - - if (foundMainTrackEntryId != -1) - { - const QVector &trackEntries = tw->getTrackContentWidget()->getTrackEntries(); - for (const TrackEntry *mainTe : trackEntries) - { - if (mainTe->m_topicId == foundMainTrackEntryId) - return mainTe->m_startTime + te->m_startTime; - } - } - } - } - - return -1; -} - - -QList ScriptTimeline::getTrackWidgets() const -{ - return ui->trackScrollArea->findChildren(); -} - - -TrackEntry *ScriptTimeline::getTrackEntryWithFiltersAsEntries(int topicId) -{ - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - const QVector trackEntries = tw->getTrackEntriesFiltersAsEntries(); - for (TrackEntry *te : qAsConst(trackEntries)) - { - if (te->m_topicId == topicId) - return te; - } - } - - return nullptr; -} - - -TrackEntry *ScriptTimeline::getTrackEntry(int topicId) -{ - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - const QVector trackEntries = tw->getTrackEntries(); - for (TrackEntry *te : trackEntries) - { - if (te->m_topicId == topicId) - return te; - } - } - - return nullptr; -} - - -QVector ScriptTimeline::getTrackEntriesWithFiltersAsEntries(bool selectedEntriesOnly) const -{ - QVector tes; - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - tes += tw->getTrackEntriesFiltersAsEntries(selectedEntriesOnly); - - return tes; -} - - -QMap > ScriptTimeline::getTrackEntriesWithFilters(bool selectedEntriesOnly) const -{ - QMap > trackList; - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - QString trackName(tw->trackName()); - const QVector trackEntries = tw->getTrackEntriesWithFilters(selectedEntriesOnly); - - trackList.insert(trackName, trackEntries); - } - - return trackList; -} - - -QVector ScriptTimeline::getTrackEntries(bool selectedEntriesOnly) const -{ - QVector tes; - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - tes += tw->getTrackEntries(selectedEntriesOnly); - - return tes; -} - - -bool ScriptTimeline::importTrackEntries(const QVector &importTes) -{ - - int importLength = 0; - for (TrackEntry *te : qAsConst(importTes)) - { - int teLen = te->m_startTime + te->m_length; - if (teLen > importLength) - importLength = teLen; - } - - - - const QVector tes = getTrackEntries(); - int pasteTime = m_time; - - for (TrackEntry *te : qAsConst(tes)) - { - int start = te->m_startTime; - int sum = start + te->m_length; - - if (start < m_time - && sum > m_time - && pasteTime < sum) - { - pasteTime = sum; - } - } - - int diff = pasteTime - m_time; - - - - for (TrackEntry *te : qAsConst(tes)) - { - if (te->m_startTime >= m_time) - te->m_startTime += importLength + diff; - } - - - - QVector finalImportTes(importTes); - - for (TrackEntry *te : finalImportTes) - { - - te->m_startTime += pasteTime; - - - if (!addTopic(te)) - { - qCritical() << "could not add te #" << te->m_topicId; - delete te; - } - } - - - - m_time = pasteTime; - setIndicatorTime(m_time); - - - return true; -} - - -bool ScriptTimeline::addTopic(TrackEntry *te) -{ - if (te->m_topicId == 0) - te->m_topicId = nextUniqueId(); - - if (te->m_channelName.isEmpty()) - te->m_channelName = tr("Unnamed Track"); - - te->isSet = true; - - - const QList trackWidgetList = getTrackWidgets(); - for (TrackWidget *tw : trackWidgetList) - { - if (tw->trackName() == te->m_channelName) - { - m_ignoreChanges = true; - bool result = tw->addTrackEntry(te, false); - m_ignoreChanges = false; - - if (!result) - return false; - - tw->updateMe(); - return true; - } - } - - - return false; -} - - -QVector ScriptTimeline::getSelectedEntriesWithFilterEntries() const -{ - QVector tes; - - const QList tws = getTrackWidgets(); - - for (const TrackWidget *tw : qAsConst(tws)) - tes += tw->getTrackEntriesFiltersAsEntries(true); - - return tes; -} - - -QString ScriptTimeline::newTrackName() const -{ - QString newTrackName; - int add = 1; - const QList trackWidgetList = getTrackWidgets(); - bool newTrackNameFound = false; - while (!newTrackNameFound) - { - newTrackNameFound = true; - newTrackName = tr("New Track %1").arg(add); - for (const TrackWidget *tw : trackWidgetList) - { - if (tw->trackName() == newTrackName) - { - - add++; - newTrackNameFound = false; - break; - } - } - } - - return newTrackName; -} - - -void ScriptTimeline::updateAllTracks() -{ - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - tw->updateMe(); -} - - -QVector ScriptTimeline::getSelectedFilterEntries() const -{ - QVector result; - - const QList tws = getTrackWidgets(); - for (const TrackWidget *tw : qAsConst(tws)) - { - result += tw->getSelectedFilterEntries(); - } - - return result; -} - - -void ScriptTimeline::slotDeleteSelectedTopics() -{ - int topicCount = 0; - - const QList tws = getTrackWidgets(); - for (const TrackWidget *tw : qAsConst(tws)) - { - topicCount += tw->getSelectedFilterEntries().count(); - - TrackContentWidget *tcw = tw->getTrackContentWidget(); - topicCount += tcw->getSelectedEntries().count(); - } - - if (!topicCount) - { - QApplication::beep(); - return; - } - - connect(new LtsWidgets::LtsMessageDialogNew(this, tr("Are you sure that you want to delete %1 topics?").arg(topicCount), - QString(), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No), - &LtsWidgets::LtsMessageDialogNew::yes, - this, [this]() - { - const QList tws = getTrackWidgets(); - for (const TrackWidget *tw : qAsConst(tws)) - { - - const QVector &ftws = tw->filterLines(); - - for (const FilterTrackWidget *ftw : qAsConst(ftws)) - { - const FilterBackgroundWidget *fbw = ftw->fbw(); - const QMap &fcws = fbw->filterContentWidgets(); - - for (TrackContentWidget *tcw: fcws) - tcw->deleteSelectedTopics(); - } - - TrackContentWidget *tcw = tw->getTrackContentWidget(); - tcw->deleteSelectedTopics(); - } - }); -} - - -void ScriptTimeline::slotCutTopics() -{ - slotCopyTopics(); - - const QList tws = getTrackWidgets(); - for (const TrackWidget *tw : qAsConst(tws)) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - tcw->deleteSelectedTopics(); - } -} - - -void ScriptTimeline::slotCopyTopics() -{ - const QList tws = getTrackWidgets(); - int firstTeTime = -1; - - - - for (const TrackWidget *tw : qAsConst(tws)) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - const QVector &selected = tcw->getSelectedEntries(); - - if (tcw->getSelectedEntries().isEmpty()) - continue; - - for (const TrackEntry *te : qAsConst(selected)) - { - if (firstTeTime > te->m_startTime || firstTeTime == -1) - firstTeTime = te->m_startTime; - } - } - - if (firstTeTime == -1) - { - QApplication::beep(); - return; - } - - - m_copyTopics.clear(); - - - - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - if (tcw->getSelectedEntries().isEmpty()) - continue; - - - connect(tw, &QObject::destroyed, - this, [this](QObject *obj) - { - m_copyTopics.remove(static_cast(obj)); - }); - - m_copyTopics.insert(tw, TrackContentWidget::itemsToData(tw->getTrackEntriesWithFilters(true), firstTeTime, false)); - } -} - - -void ScriptTimeline::slotPasteTopics() -{ - if (m_copyTopics.isEmpty() || m_time < 0) - { - QApplication::beep(); - return; - } - - - for (auto i = m_copyTopics.constBegin(); i != m_copyTopics.constEnd(); ++i) - { - const TrackWidget *tw = i.key(); - const QByteArray &data = i.value(); - - TrackContentWidget *tcw = tw->getTrackContentWidget(); - int dummy = 0; - const TemporaryPointerVector tempTes(TrackContentWidget::dataToItems(data, dummy)); - if (!tcw->canPasteTopics(tempTes, 0, m_time)) - { - new LtsWidgets::LtsMessageDialogNew(this, tr("Topics can't be pasted here as one or more topics intersects with another existing topic.")); - return; - } - } - - - for (auto i = m_copyTopics.constBegin(); i != m_copyTopics.constEnd(); ++i) - { - TrackWidget *tw = i.key(); - TrackContentWidget *tcw = tw->getTrackContentWidget(); - const QByteArray &data = i.value(); - - int dummy = 0; - const TemporaryPointerVector tempTes(TrackContentWidget::dataToItems(data, dummy)); - tcw->forcePasteTopics(tempTes, 0, m_time); - } -} - - -void ScriptTimeline::slotMoveAll(int direction) -{ - int factor = 1; - - if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - factor = 10; - - QList tws = getTrackWidgets(); - - - for (TrackWidget *tw : qAsConst(tws)) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - if (!tcw->canMoveAll(direction * factor)) - { - QApplication::beep(); - qWarning() << "cols and/or parls @" << tcw; - return; - } - } - - - for (TrackWidget *tw : qAsConst(tws)) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - tcw->forceMoveAll(direction * factor); - } -} - - -static inline bool containsFailedTrackContentWidget(const QHash &mlmsTCWStates) -{ - auto end = mlmsTCWStates.end(); - for (auto it = mlmsTCWStates.begin(); it != end; ++it) - { - if (it.value() == false) - return true; - } - return false; -} - -TrackWidget *ScriptTimeline::addTrack(const QString &name, bool filtersAllowed) -{ - TrackWidget *tw = new TrackWidget(m_baseTrackWidgetWidth, m_readonly, m_tools, filtersAllowed); - tw->setScriptTimeline(this); - tw->setTrackName(name); - - ui->trackLayout->insertWidget(1, tw); - - connect(tw, &TrackWidget::signalTrackEntrySelectionChanged, - this, &ScriptTimeline::slotTrackEntrySelectionChanged); - connect(tw, &TrackWidget::signalTrackEntryDeleted, - this, &ScriptTimeline::signalTrackEntryDeleted); - connect(tw, &TrackWidget::signalUnselectAll, - this, &ScriptTimeline::slotUnselectAll); - connect(tw, &TrackWidget::signalSelectTrack, - this, &ScriptTimeline::slotSelectTrack); - connect(tw, &TrackWidget::signalChangesMade, - this, &ScriptTimeline::signalChangesMade); - connect(tw, &TrackWidget::signalEditEntry, - this, &ScriptTimeline::signalEditEntry); - - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - connect(tcw, &TrackContentWidget::signalStartRubberband, - this, &ScriptTimeline::slotStartRubberband); - connect(tcw, &TrackContentWidget::signalStopRubberband, - this, &ScriptTimeline::slotStopRubberband); - connect(tcw, &TrackContentWidget::signalEscapeRubberband, - this, &ScriptTimeline::slotEscapeRubberband); - connect(tcw, &TrackContentWidget::signalModifyRubberband, - this, &ScriptTimeline::slotModifyRubberband); - connect(tcw, &TrackContentWidget::signalSelectAllGlobal, - this, &ScriptTimeline::selectAll); - connect(tcw, &TrackContentWidget::signalCopyTopics, - this, &ScriptTimeline::slotCopyTopics); - connect(tcw, &TrackContentWidget::signalPasteTopics, - this, &ScriptTimeline::slotPasteTopics); - connect(tcw, &TrackContentWidget::signalCutTopics, - this, &ScriptTimeline::slotCutTopics); - connect(tcw, &TrackContentWidget::signalDeleteSelectedTopics, - this, &ScriptTimeline::slotDeleteSelectedTopics); - connect(tcw, &TrackContentWidget::signalSave, - this, &ScriptTimeline::signalSave); - connect(tcw, &TrackContentWidget::signalMoveAll, - this, &ScriptTimeline::slotMoveAll); - connect(tcw, &TrackContentWidget::signalScroll, - this, &ScriptTimeline::slotScroll); - - - - connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionFailed, - this, [this]() - { - TrackContentWidget *senderTcw = qobject_cast(sender()); - - if (senderTcw == nullptr) - return; - - m_mlmsTCWStates.insert(senderTcw, false); - - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - { - - TrackContentWidget *tcw = tw->getTrackContentWidget(); - tcw->setDragPossible(false); - } - }); - - - connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionOkay, - this, [this]() - { - - - - TrackContentWidget *senderTcw = qobject_cast(sender()); - - if (senderTcw == nullptr) - return; - - m_mlmsTCWStates.insert(senderTcw, true); - - if (containsFailedTrackContentWidget(m_mlmsTCWStates)) - return; - - - - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - if (tcw == senderTcw) - continue; - - tcw->setDragPossible(true); - } - }); - - - connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionDone, - this, [this](int dropPos) - { - TrackContentWidget *senderTcw = qobject_cast(sender()); - - if (senderTcw == nullptr) - return; - - - - - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - if (tcw == senderTcw) - continue; - - if (containsFailedTrackContentWidget(m_mlmsTCWStates) || dropPos == -1) - { - QApplication::beep(); - tcw->dragEnd(); - } - else - tcw->dragDrop(dropPos); - } - - m_mlmsMaster = nullptr; - m_mlmsTCWStates.clear(); - m_ignoreChanges = false; - - updateAllTracks(); - }); - - - connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionMove, - this, [this](int x) - { - TrackContentWidget *senderTcw = qobject_cast(sender()); - - if (senderTcw == nullptr) - return; - - - if (senderTcw != m_mlmsMaster) - return; - - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - if (tcw == senderTcw) - continue; - - tcw->dragMove(x); - } - }); - - - connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionStart, - this, [this](int dragPosTime) - { - if (m_mlmsMaster != nullptr) - return; - - TrackContentWidget *senderTcw = qobject_cast(sender()); - - if (senderTcw == nullptr) - return; - - m_mlmsMaster = senderTcw; - m_ignoreChanges = true; - - const QList tws = getTrackWidgets(); - - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - if (tcw == senderTcw) - continue; - - tcw->dragEnter(tcw->selectedItemsToData(dragPosTime)); - } - }); - - - return tw; -} - - -void ScriptTimeline::slotEscapeRubberband() -{ - if (m_rubberbandAction.rubberBand == nullptr) - return; - - m_rubberbandAction.rubberBand->deleteLater(); -} - - -void ScriptTimeline::slotStopRubberband() -{ - if (m_rubberbandAction.rubberBand == nullptr) - return; - - QRect geo(m_rubberbandAction.rubberBand->geometry()); - - m_rubberbandAction.rubberBand->deleteLater(); - - if (m_readonly) - return; - - QPoint rubberbandLength = geo.topLeft() - geo.bottomRight(); - if (rubberbandLength.manhattanLength() <= 6) - return; - - - const QList tws = getTrackWidgets(); - for (TrackWidget *tw : tws) - { - TrackContentWidget *tcw = tw->getTrackContentWidget(); - - QRect tcwGeo(tcw->geometry()); - QPoint topLeft(tcw->mapTo(this, tcwGeo.topLeft())); - QPoint bottomRight(tcw->mapTo(this, tcwGeo.bottomRight())); - - if (QRect(topLeft, bottomRight).intersects(geo)) - { - QPoint tcwTL(tcw->mapFrom(this, geo.topLeft())); - QPoint tcwBR(tcw->mapFrom(this, geo.bottomRight())); - - int fromPos = tcwTL.x(); - int toPos = tcwBR.x(); - - tcw->selectEntriesAtPositions(fromPos, toPos); - } - } -} - - -void ScriptTimeline::slotStartRubberband(const QPoint &) -{ - if (m_readonly) - return; - - if (m_rubberbandAction.rubberBand == nullptr) - { - QPoint cursorPos(mapFromGlobal(QCursor::pos())); - m_rubberbandAction.origin = cursorPos; - m_rubberbandAction.rubberBand = new QRubberBand(QRubberBand::Rectangle, this); - - m_rubberbandAction.rubberBand->setGeometry(QRect(cursorPos, QSize())); - m_rubberbandAction.rubberBand->show(); - } -} - -void ScriptTimeline::slotModifyRubberband() -{ - if (!m_rubberbandAction.rubberBand) - return; - - m_rubberbandAction.rubberBand->setGeometry(QRect(m_rubberbandAction.origin, mapFromGlobal(QCursor::pos())).normalized()); -} - - -void ScriptTimeline::slotTrackEntrySelectionChanged(TrackEntry *) -{ - if (m_ignoreChanges) - return; - - const QList tws = getTrackWidgets(); - - - bool hasSelections = false; - bool activateMultiLineMultiSelectionMode = false; - for (const TrackWidget *tw : tws) - { - if (!hasSelections) - { - if (!tw->getSelectedEntries().isEmpty() || !tw->getSelectedFilterEntries().isEmpty()) - hasSelections = true; - } - else if (!tw->getSelectedEntries().isEmpty()) - { - - activateMultiLineMultiSelectionMode = true; - break; - } - } - - - for (TrackWidget *tw : qAsConst(tws)) - tw->getTrackContentWidget()->setMultiLineMultiSelectionMode(activateMultiLineMultiSelectionMode); - - emit signalTrackEntrySelectionChanged(nullptr); -} - - -void ScriptTimeline::slotSelectTrack() -{ - const QList trackWidgetList = getTrackWidgets(); - for (const TrackWidget *tw : trackWidgetList) - { - if (tw->isSelected()) - { - - return; - } - } - - -} - - -void ScriptTimeline::updateEverything() -{ - - - - TrackWidget *tw = ui->trackScrollArea->findChild(); - - if (tw != nullptr) - m_tools->m_paintWidth = tw->width(); - - ui->indicator->updateMe(); - - updateAllTracks(); - update(); -} - - -void ScriptTimeline::slotScrollbarValueChanged() -{ - m_tools->m_scrollPos = ui->trackScrollBar->value(); - - updateEverything(); -} -} diff --git a/Lts/Common/LtsScriptTimeline/script_timeline.h b/Lts/Common/LtsScriptTimeline/script_timeline.h deleted file mode 100755 index 85e9f3af0..000000000 --- a/Lts/Common/LtsScriptTimeline/script_timeline.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include -#include "track_entry_painters.h" - -#include -#include -#include - - -namespace Ui -{ -class ScriptTimeline; -} - - -class QRubberBand; - - -namespace LtsScriptTimeline -{ -class TrackContentWidget; -class TrackWidget; -class TrackEntry; -class Tools; - -class LTSSCRIPTTIMELINE_EXPORT ScriptTimeline : public QWidget -{ - Q_OBJECT - -public: - explicit ScriptTimeline(QWidget *parent = nullptr); - ~ScriptTimeline(); - - TrackWidget *addTrack(const QString &name, bool filtersAllowed = true); - void setScriptId(int scriptId) { m_scriptId = scriptId; } - bool addTopic(TrackEntry *te); - QString newTrackName() const; - int nextUniqueId() { return ++m_uniqueId; } - void setUniqueIdCounter(int uniqueId) { m_uniqueId = uniqueId; } - QVector getTrackEntriesWithFiltersAsEntries(bool selectedEntriesOnly = false) const; - QMap > getTrackEntriesWithFilters(bool selectedEntriesOnly = false) const; - QVector getTrackEntries(bool selectedEntriesOnly = false) const; - QVector getSelectedEntriesWithFilterEntries() const; - QVector getSelectedFilterEntries() const; - TrackEntry *getTrackEntry(int topicId); - TrackEntry *getTrackEntryWithFiltersAsEntries(int topicId); - void setIndicatorTime(int time); - int getCurrentScriptEndTime(); - - void updateEverything(); - void clear(); - void setReadonly(bool readonly); - QList getTrackWidgets() const; - void lockIndicator(bool lock = true); - int getFilterTrackEntryRealTime(TrackEntry *te); - bool checkNewLength(TrackEntry *te, int newLength); - bool importTrackEntries(const QVector &importTes); - -protected: - void keyPressEvent(QKeyEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *) override; - void mouseReleaseEvent(QMouseEvent *) override; - -private: - Ui::ScriptTimeline *ui; - int m_uniqueId; - int m_scriptId; - int m_baseTrackWidgetWidth; - QVector m_clipboard; - - bool m_readonly; - int m_time; - Tools *m_tools; - TrackContentWidget *m_mlmsMaster; - QHash m_mlmsTCWStates; - bool m_ignoreChanges; - QHash m_copyTopics; - - struct RubberbandAction - { - QPoint origin; - QPointer rubberBand; - } m_rubberbandAction; - - void resizeEvent(QResizeEvent *event) override; - void updateAllTracks(); - -signals: - void signalTrackEntrySelectionChanged(TrackEntry *te); - void signalChangesMade(); - void signalHeaderIndicatorTimeChanged(int time); - void signalHeaderIndicatorTimeUserChanged(int time); - void signalEditEntry(const int topicId); - void signalTrackEntryDeleted(const int topicId); - void signalTrackEntryAdded(TrackEntry *te); - void signalSave(); - -public slots: - void slotUnselectAll(); - void slotEscapeRubberband(); - void slotStopRubberband(); - void slotStartRubberband(const QPoint &pos); - void slotModifyRubberband(); - void selectAll(); - void slotCopyTopics(); - void slotCutTopics(); - void slotPasteTopics(); - void slotDeleteSelectedTopics(); - void slotMoveAll(int direction); - void slotScroll(int incr); - -private slots: - void slotScrollbarValueChanged(); - void slotZoomValueChanged(int value); - void slotDeleteTracks(); - void slotSelectTrack(); - void slotHeaderHandleResized(int newSize); - void slotHeaderIndicatorTimeChanged(int time); - void slotTrackEntrySelectionChanged(TrackEntry *te); - void slotSetNewScrollBarMaximum(); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h b/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h deleted file mode 100755 index f07401d23..000000000 --- a/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include - - -namespace LtsScriptTimeline -{ - -template -class TemporaryPointerVector : public QVector -{ - static_assert(std::is_pointer::value, "Type must be of pointer type"); - -public: - TemporaryPointerVector() = default; - - explicit TemporaryPointerVector(const QVector &other) - { - discard(); - this->append(other); - } - - - ~TemporaryPointerVector() - { - discard(); - } - - - void setDiscardable(bool discardable) - { - m_discard = discardable; - } - - -private: - bool m_discard = true; - - - void discard() - { - if (m_discard) - qDeleteAll(this->begin(), this->end()); - this->clear(); - } - -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/tools.cpp b/Lts/Common/LtsScriptTimeline/tools.cpp deleted file mode 100755 index 71cba4058..000000000 --- a/Lts/Common/LtsScriptTimeline/tools.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "tools.h" - -#include - - -namespace LtsScriptTimeline -{ - - - - - - -int Tools::getLength(int startTime, int duration) -{ - return (getPos(startTime + duration) - getPos(startTime)); -} - - -int Tools::getDuration(int duration) -{ - double max = Tools::m_timelineLength; - max /= Tools::m_zoomFactor; - - int paintWidth = m_paintWidth; - - double x = duration; - x /= paintWidth; - x *= max; - - return lrint(x); -} - - -int Tools::getTime(int pos, bool neutral) -{ - double visibleMax = Tools::m_timelineLength; - visibleMax /= Tools::m_zoomFactor; - - if (!neutral) - visibleMax += Tools::m_scrollPos; - - int visibleMin = 0; - - if (!neutral) - visibleMin = Tools::m_scrollPos; - - double diff = visibleMax - visibleMin; - int paintWidth = m_paintWidth; - - double x = pos; - x /= paintWidth; - x *= diff; - x += visibleMin; - - return lrint(x); -} - - -int Tools::getPos(int time, bool neutral) -{ - double visibleMax = Tools::m_timelineLength; - visibleMax /= Tools::m_zoomFactor; - - if (!neutral) - visibleMax += Tools::m_scrollPos; - - int visibleMin = 0; - - if (!neutral) - visibleMin = Tools::m_scrollPos; - - double diff = visibleMax - visibleMin; - int paintWidth = m_paintWidth; - - double x = time; - x -= visibleMin; - x /= diff; - x *= paintWidth; - - return lrint(x); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/tools.h b/Lts/Common/LtsScriptTimeline/tools.h deleted file mode 100755 index a100012cd..000000000 --- a/Lts/Common/LtsScriptTimeline/tools.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - - -namespace LtsScriptTimeline -{ - -class Tools : public QObject -{ - Q_OBJECT - -public: - explicit Tools(QObject *parent) - : QObject(parent) - , m_timelineLength(5 * 60) - , m_zoomFactor(1) - , m_scrollPos(0) - , m_paintWidth(1) - {} - - int m_timelineLength; - float m_zoomFactor; - int m_scrollPos; - int m_paintWidth; - - int getDuration(int duration); - int getTime(int pos, bool neutral = false); - int getPos(int time, bool neutral = false); - int getLength(int startTime, int duration); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_content_widget.cpp b/Lts/Common/LtsScriptTimeline/track_content_widget.cpp deleted file mode 100755 index 5285dcfc0..000000000 --- a/Lts/Common/LtsScriptTimeline/track_content_widget.cpp +++ /dev/null @@ -1,1652 +0,0 @@ -#include "track_content_widget.h" -#include "track_entry.h" -#include "tools.h" -#include "track_entry_painters.h" -#include "track_widget.h" -#include "script_timeline.h" -#include "filter_background_widget.h" -#include "track_drag_handler.h" -#include "track_select_handler.h" -#include "track_resize_handler.h" -#include "temporary_pointer_vector.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#include - - -namespace LtsScriptTimeline -{ - -QDataStream &operator<<(QDataStream &ds, const TrackEntry &te) -{ - const QByteArray jsonContent = QJsonDocument(te.m_detailData).toBinaryData(); - ds << te.m_topicId - << te.m_startTime - << te.m_length - << static_cast(te.m_type) - << te.isSet - << te.index - << jsonContent - << te.x << te.y << te.h << te.w - << te.m_channelName - << te.m_description; - return ds; -} - - -QDataStream &operator>>(QDataStream &ds, TrackEntry &te) -{ - QByteArray jsonContent; - quint8 type; - ds >> te.m_topicId - >> te.m_startTime - >> te.m_length - >> type - >> te.isSet - >> te.index - >> jsonContent - >> te.x >> te.y >> te.h >> te.w - >> te.m_channelName - >> te.m_description; - - te.m_type = static_cast(type); - te.m_detailData = QJsonDocument::fromBinaryData(jsonContent).object(); - return ds; -} - - -bool isCollision(int start_s, int end_s, int toCompare_start_s, int toCompare_end_s) -{ - - - - - - - - - - - - - - - - - - - - - if (start_s >= toCompare_start_s && - - start_s < toCompare_end_s) - { - - return true; - } - - - if (start_s < toCompare_start_s && - - end_s > toCompare_start_s) - { - - return true; - } - - return false; -} - - -bool isCollision2(int startPreview_s, int endPreview_s, const TrackEntry *pTrackEntry) -{ - return isCollision(startPreview_s, endPreview_s, pTrackEntry->m_startTime, pTrackEntry->m_startTime + pTrackEntry->m_length); -} - - - - -void sortTrackEntriesChronologically(QVector &vecTrackEntries) -{ - std::sort(vecTrackEntries.begin(), vecTrackEntries.end(), - [](const TrackEntry *a, const TrackEntry *b) -> bool - { - return a->m_startTime < b->m_startTime; - }); -} - - -bool TrackContentWidget::checkForCollisionsSimple(const TrackEntry &previewEntry) const -{ - - const int startPreview = previewEntry.m_startTime; - if (startPreview < 0) - return false; - const int endPreview = startPreview + previewEntry.m_length; - - const QVector trackEntries = getTrackEntries(); - for (const TrackEntry *checkTe : trackEntries) - { - if (checkTe->m_topicId == previewEntry.m_topicId) - continue; - - if (LtsScriptTimeline::isCollision2(startPreview, endPreview, checkTe)) - return false; - } - - return true; -} - - -bool TrackContentWidget::checkForCollisions(const TrackContentWidget *tcw, const QVector &preview, int dropTime_s, QVector &vecCollisions, bool ignoreSelected, bool ignoreSelfCheck) -{ - QVector _preview = preview; - sortTrackEntriesChronologically(_preview); - - vecCollisions.clear(); - - QVector vecEntriesToCheck; - const QVector &trackEntries = tcw->getTrackEntries(); - - if (ignoreSelected) - { - - const QVector &selectedEntries = tcw->getSelectedEntries(); - for (TrackEntry *te : qAsConst(trackEntries)) - { - if (!selectedEntries.contains(te)) - vecEntriesToCheck.append(te); - } - } - else - vecEntriesToCheck = trackEntries; - - - auto leftMostI = std::min_element(_preview.constBegin(), _preview.constEnd(), [](const TrackEntry * a, const TrackEntry * b) - { - return a->m_startTime < b->m_startTime; - }); - - if (leftMostI == _preview.constEnd()) - return false; - - int startTimeMostLeft_s = (*leftMostI)->m_startTime; - int startOffset_s = dropTime_s - startTimeMostLeft_s; - - - for (TrackEntry *previewEntry : qAsConst(_preview)) - { - int startPreview_s = previewEntry->m_startTime + startOffset_s; - - if (startPreview_s < 0) - { - vecCollisions.append(previewEntry); - continue; - } - - int endPreview_s = startPreview_s + previewEntry->m_length; - - for (const TrackEntry *checkTe : qAsConst(vecEntriesToCheck)) - { - if (checkTe->m_topicId == previewEntry->m_topicId && !ignoreSelfCheck) - { - continue; - } - - if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, checkTe)) - { - vecCollisions.append(previewEntry); - break; - } - } - } - - return !vecCollisions.isEmpty(); -} - - -bool TrackContentWidget::checkForCollisions(const TrackContentWidget *pTCW, const QVector &vecPreviewTrackEntries, int dropTime_s) -{ - QVector dummy_vecCollidingTopics; - return TrackContentWidget::checkForCollisions(pTCW, vecPreviewTrackEntries, dropTime_s, dummy_vecCollidingTopics, true); -} - - -QString TrackContentWidget::parallelError() -{ - return tr("At least one topic of type Situation, Building, Audio or Video has been selected\nwhich can not be played parallel on both screens. Placement not possible."); -} - - - -bool TrackContentWidget::checkForParallelTopics(const TrackContentWidget *tcw, const QVector &preview, int dropTime_s, QVector &vecParallelTopics, bool ignoreSelectedTopics, bool hack) -{ - - { - QVector vecParallelForbiddenFilter = { TrackEntryType::Building, TrackEntryType::Audio, TrackEntryType::Video, TrackEntryType::SituationMap, TrackEntryType::Situation }; - QVector previewCopy(preview); - sortTrackEntriesChronologically(previewCopy); - QVector vecTrackEntries(tcw->getTrackEntries()); - - - auto itE = std::remove_if(previewCopy.begin(), previewCopy.end(), - [vecParallelForbiddenFilter](TrackEntry *pTE) - { - return !vecParallelForbiddenFilter.contains(pTE->m_type); - }); - previewCopy.erase(itE, previewCopy.end()); - - - if (ignoreSelectedTopics) - { - - itE = std::remove_if(previewCopy.begin(), previewCopy.end(), - [tcw](TrackEntry *pTE) - { - return tcw->getSelectedEntries().contains(pTE); - }); - previewCopy.erase(itE, previewCopy.end()); - } - - - - itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(), - [vecParallelForbiddenFilter](TrackEntry *pTE) - { - return !vecParallelForbiddenFilter.contains(pTE->m_type); - }); - vecTrackEntries.erase(itE, vecTrackEntries.end()); - - - - itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(), - [tcw](TrackEntry *pTE) - { - return tcw->getSelectedEntries().contains(pTE); - }); - vecTrackEntries.erase(itE, vecTrackEntries.end()); - - - - if (!previewCopy.isEmpty() && !vecTrackEntries.isEmpty()) - { - auto leftMostI = std::min_element(previewCopy.constBegin(), previewCopy.constEnd(), [](const TrackEntry *a, const TrackEntry *b) - { - return a->m_startTime < b->m_startTime; - }); - - if (leftMostI != previewCopy.constEnd()) - { - int startTimeMostLeft_s = (*leftMostI)->m_startTime; - - - - - for (TrackEntry *previewEntry : qAsConst(previewCopy)) - { - - - int startOffset_s = previewEntry->m_startTime; - - if (!hack) - startOffset_s -= startTimeMostLeft_s; - - - - for (TrackEntry *pTrackEntry : qAsConst(vecTrackEntries)) - { - - - if (previewEntry->m_type != pTrackEntry->m_type) - { - - continue; - } - - - if (previewEntry->m_topicId == pTrackEntry->m_topicId) - { - - continue; - } - - int startPreview_s = startOffset_s; - - if (!hack) - startPreview_s += dropTime_s; - - int endPreview_s = startPreview_s + previewEntry->m_length; - - if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, pTrackEntry)) - { - - vecParallelTopics.append(previewEntry); - break; - } - - - } - } - } - } - else - { - - } - } - - - - - { - - QVector previewCopy(preview); - sortTrackEntriesChronologically(previewCopy); - QVector > differentParallelForbidden = - { - { TrackEntryType::Situation, TrackEntryType::Snapshot }, - { TrackEntryType::Situation, TrackEntryType::SituationReplay }, - { TrackEntryType::Situation, TrackEntryType::SituationMap }, - }; - QVector vecTrackEntries(tcw->getTrackEntries()); - - - int step = 1; - auto findUnusedType = [differentParallelForbidden, &step](const TrackEntry *pTE) - { - auto x = std::find_if(differentParallelForbidden.constBegin(), differentParallelForbidden.constEnd(), [pTE, step](const QPair &pair) - { - bool x = (pair.first == pTE->m_type || pair.second == pTE->m_type); - if (x) - qDebug() << ">>"<m_type << "in type list - not removing"; - return x; - }); - - if (x == differentParallelForbidden.constEnd()) - qDebug() << ">>"<m_type << "not found in type list - removing!"; - - return (x == differentParallelForbidden.constEnd()); - }; - - - - auto itE = std::remove_if(previewCopy.begin(), previewCopy.end(), findUnusedType); - previewCopy.erase(itE, previewCopy.end()); - - - step = 2; - - itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(), findUnusedType); - vecTrackEntries.erase(itE, vecTrackEntries.end()); - - - if (ignoreSelectedTopics) - { - - itE = std::remove_if(previewCopy.begin(), previewCopy.end(), - [tcw](TrackEntry *pTE) - { - return tcw->getSelectedEntries().contains(pTE); - }); - previewCopy.erase(itE, previewCopy.end()); - } - - - - itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(), - [tcw](TrackEntry *pTE) - { - bool x = tcw->getSelectedEntries().contains(pTE); - - return x; - }); - vecTrackEntries.erase(itE, vecTrackEntries.end()); - - - - if (!previewCopy.size() || !vecTrackEntries.size()) - { - - return !vecParallelTopics.isEmpty(); - } - - - auto leftMostI = std::min_element(previewCopy.constBegin(), previewCopy.constEnd(), [](const TrackEntry * a, const TrackEntry * b) - { - return a->m_startTime < b->m_startTime; - }); - - if (leftMostI == previewCopy.constEnd()) - return !vecParallelTopics.isEmpty(); - - int startTimeMostLeft_s = (*leftMostI)->m_startTime; - - - - QVector correspondingDifferentTopicTypes; - - for (TrackEntry *previewEntry : qAsConst(previewCopy)) - { - correspondingDifferentTopicTypes.clear(); - - - int startOffset_s = previewEntry->m_startTime; - - if (!hack) - startOffset_s -= startTimeMostLeft_s; - - - for (const QPair &pair : qAsConst(differentParallelForbidden)) - { - if (pair.first == previewEntry->m_type) - correspondingDifferentTopicTypes += pair.second; - - if (pair.second == previewEntry->m_type) - correspondingDifferentTopicTypes += pair.first; - } - - Q_ASSERT(!correspondingDifferentTopicTypes.isEmpty()); - - if (correspondingDifferentTopicTypes.isEmpty()) - continue; - - for (const TrackEntryType correspondingDifferentTopicType : qAsConst(correspondingDifferentTopicTypes)) - { - - - for (TrackEntry *pTrackEntry : qAsConst(vecTrackEntries)) - { - - - if (pTrackEntry->m_type != correspondingDifferentTopicType) - { - - continue; - } - - - if (previewEntry->m_topicId == pTrackEntry->m_topicId) - { - - continue; - } - - int startPreview_s = startOffset_s; - - if (!hack) - startPreview_s += dropTime_s; - - int endPreview_s = startPreview_s + previewEntry->m_length; - - if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, pTrackEntry)) - { - - vecParallelTopics.append(previewEntry); - break; - } - - - } - } - } - } - - return !vecParallelTopics.isEmpty(); -} - - -bool TrackContentWidget::checkForParallelTopics(const TrackContentWidget *pTCW, const QVector &vecPreviewTrackEntries, int dropTime_s, bool hack) -{ - QVector dummy_vecParallelTopics; - return checkForParallelTopics(pTCW, vecPreviewTrackEntries, dropTime_s, dummy_vecParallelTopics, false, hack); -} - - -QString TrackContentWidget::mime = QStringLiteral("application/rheinmetall.script.topic"); - - -TrackContentWidget::TrackContentWidget(QWidget *parent, bool isFilter, Tools *tools, int mainTopicId) - : QWidget(parent) - , m_selectHandler(new SelectHandler(this)) - , m_resizeHandler(new ResizeHandler(this)) - , m_dragHandler(new DragHandler(this)) - , m_dragOffsetTime(-1) - , m_dragPos(-1) - , m_mainSelected(nullptr) - , m_width(0) - , m_cursorSet(false) - , m_oldEntryLength(-1) - , m_unselectAllImmune(false) - , m_indicatorTime(-1) - , m_isFilter(isFilter) - , m_readonly(false) - , m_trackWidget(nullptr) - , m_scriptTimeline(nullptr) - , m_tools(tools) - , m_isMultiLineMultiSelectionMode(false) - , m_multiLineMultiSelectionOkay(false) - , m_dragPossible(false) - , m_moveValid(false) - , m_mainTopicId(mainTopicId) -{ - setAcceptDrops(true); - setFocusPolicy(Qt::StrongFocus); - setMouseTracking(true); - - addFilterType(TrackEntryType::TacticalPlan); - addFilterType(TrackEntryType::SituationMapFilter); - - - if (m_isFilter) - { - addAllowedType(TrackEntryType::Text); - addAllowedType(TrackEntryType::Pdf); - addAllowedType(TrackEntryType::Image); - } -} - - -TrackContentWidget::~TrackContentWidget() -{ - delete m_selectHandler; - delete m_resizeHandler; - delete m_dragHandler; - - QVector entryList = m_entryList; - - - for (TrackEntry *te : entryList) - { - disconnect(te, &TrackEntry::destroyed, - this, nullptr); - } - - qDeleteAll(entryList); -} - - -bool TrackContentWidget::setDescriptionForSelected() -{ - if (getSelectedEntries().count() > 1) - { - QApplication::beep(); - return false; - } - - m_selectHandler->slotSetDescription(); - return true; -} - - -void TrackContentWidget::addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget) -{ - m_tcwCorresponding += pCorrespondingTrackWidget; - m_dragHandler->m_tcwCorresponding += pCorrespondingTrackWidget; - m_resizeHandler->m_tcwCorresponding += pCorrespondingTrackWidget; -} - - -void TrackContentWidget::setTools(Tools *tools) -{ - m_tools = tools; -} - - -void TrackContentWidget::setTrackWidget(TrackWidget *trackWidget) -{ - m_trackWidget = trackWidget; -} - - -void TrackContentWidget::setReadonly(bool readonly) -{ - m_readonly = readonly; -} - - -void TrackContentWidget::addFilterType(const TrackEntryType &tet) -{ - if (m_isFilter) - addAllowedType(tet); - else - addDisallowedType(tet); -} - - -void TrackContentWidget::setIndicatorTime(int time) -{ - m_indicatorTime = time; - update(); -} - - -void TrackContentWidget::mouseMoveEvent(QMouseEvent *event) -{ - if (!m_resizeHandler->mouseMoveEvent(event)) - m_dragHandler->mouseMoveEvent(event); - - emit signalModifyRubberband(); -} - - -TrackEntry *TrackContentWidget::getTrackEntryAt(int clickedTime) -{ - for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i) - { - TrackEntry *te = *i; - - if (te->m_startTime <= clickedTime - && (te->m_startTime + te->m_length) >= clickedTime) - return te; - } - - return nullptr; -} - - -void TrackContentWidget::mousePressEvent(QMouseEvent *event) -{ - if (m_readonly) - return; - - m_selectHandler->mousePressEvent(event); - m_resizeHandler->mousePressEvent(event); - m_dragHandler->mousePressEvent(event); - - if (!m_selected.isEmpty()) - return; - - if (event->buttons() != Qt::LeftButton) - return; - emit signalStartRubberband(event->pos()); -} - - -void TrackContentWidget::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - m_width = event->size().width(); -} - - -void TrackContentWidget::mouseReleaseEvent(QMouseEvent *event) -{ - if (m_readonly) - return; - - if (!m_resizeHandler->m_resizing) - m_selectHandler->mouseReleaseEvent(event); - - m_resizeHandler->mouseReleaseEvent(event); - - emit signalStopRubberband(); -} - - -void TrackContentWidget::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - - painter.save(); - - - - if (m_isFilter) - { - painter.setBrush(QGuiApplication::palette().window().color()); - painter.drawRect(rect()); - } - - - - painter.setPen(QColor(36, 36, 36)); - painter.drawLine(QPoint(0, 0), QPoint(0, height())); - - if (m_isFilter) - painter.drawLine(QPoint(width() - 1, 0), QPoint(width() - 1, height())); - - - - painter.setClipping(true); - painter.setClipRect(1, 1, width() - 1, height() - 1); - - - - for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i) - { - TrackEntry *te = *i; - - int startPx = m_tools->getPos(te->m_startTime, m_isFilter); - int endPx = m_tools->getPos(te->m_startTime + te->m_length, m_isFilter); - - if (endPx < 0 || startPx > width()) - continue; - - int heightPx = height() - 2; - int lengthPx = endPx - startPx; - - - painter.save(); - painter.translate(startPx, 1); - - bool isSelected = m_selected.contains(te); - QRect trackRect(0, 0, lengthPx, heightPx); - drawTrackEntry(te, painter, trackRect, isSelected); - - if (isSelected) - { - - - - } - - painter.restore(); - } - - - - if (m_dragPos > -1 && !m_preview.isEmpty()) - { - int firstTeTime = -1; - - for (const TrackEntry *te : qAsConst(m_preview)) - { - if (firstTeTime > te->m_startTime || firstTeTime == -1) - firstTeTime = te->m_startTime; - } - - int drop = m_tools->getTime(m_dragPos, m_isFilter); - int finalDrop = drop - m_dragOffsetTime; - int diff = finalDrop - firstTeTime; - int heightPx = height() - 2; - - for (auto i = m_preview.constBegin(); i != m_preview.constEnd(); ++i) - { - const TrackEntry *te = *i; - - if (m_dragHandler->m_vecCollisions.contains(*i) || m_dragHandler->m_vecParallel.contains(*i) || !isDragPossible() || !m_moveValid) - painter.setBrush(QColor("red")); - else - painter.setBrush(QColor("lightgreen")); - - int time = te->m_startTime + diff; - - int startPx = m_tools->getPos(time, m_isFilter); - int endPx = m_tools->getPos(time + te->m_length, m_isFilter); - - if (endPx < 0 || startPx > width()) - continue; - - int lengthPx = endPx - startPx; - painter.drawRect(startPx, 1, lengthPx, heightPx); - } - } - - - - if (m_indicatorTime > -1) - { - int pos = m_tools->getPos(m_indicatorTime, m_isFilter); - - if (pos > 0 && pos <= m_tools->m_paintWidth) - { - painter.setPen(QColor("cyan")); - painter.drawLine(pos, 0, pos, height()); - } - } - - - painter.restore(); -} - - -void TrackContentWidget::dragEnterEvent(QDragEnterEvent *event) -{ - if (m_readonly) - return; - - m_dragHandler->dragEnterEvent(event); -} - - -void TrackContentWidget::dragLeave() -{ - -} - - -void TrackContentWidget::dragLeaveEvent(QDragLeaveEvent *event) -{ - if (m_readonly) - return; - - m_dragHandler->dragLeaveEvent(event); -} - - -void TrackContentWidget::unselect() -{ - m_unselectAllImmune = true; - emit signalUnselectAll(); - m_unselectAllImmune = false; -} - - -void TrackContentWidget::forceLocalUnselect() -{ - m_mainSelected = nullptr; - m_selected.clear(); -} - - -void TrackContentWidget::selectAll() -{ - m_selected.clear(); - - for (TrackEntry *te : qAsConst(m_entryList)) - { - m_selected.append(te); - m_mainSelected = te; - } - - emit signalTrackEntrySelectionChanged(nullptr); - update(); -} - - -void TrackContentWidget::unselectAll() -{ - if (!m_unselectAllImmune) - forceLocalUnselect(); - else - { - if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - { - forceLocalUnselect(); - emit signalTrackEntrySelectionChanged(nullptr); - } - } - - update(); -} - - -TrackEntry *TrackContentWidget::getParentTopicOfFilter(TrackEntry *filterTe) const -{ - - if (m_isFilter && m_trackWidget != nullptr) - { - - int mainTopicId = m_trackWidget->getParentTopicIdOfFilter(filterTe); - TrackContentWidget *mainTcw = m_trackWidget->getTrackContentWidget(); - const QVector &teList = mainTcw->getTrackEntries(); - - for (auto i = teList.constBegin(); i != teList.constEnd(); ++i) - { - TrackEntry *mainTe = *i; - - if (mainTe->m_topicId != mainTopicId) - continue; - - return mainTe; - } - } - - return nullptr; -} - - -bool TrackContentWidget::addTrackEntry(TrackEntry *te, bool selectTrackEntry) -{ - if (!allowedByType(te)) - { - qWarning() << "TrackContentWidget::addTrackEntry: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed"; - return false; - } - - connect(te, &TrackEntry::destroyed, - this, [this]() - { - TrackEntry *o = static_cast(sender()); - m_entryList.removeAll(o); - m_selected.removeAll(o); - - if (m_mainSelected == o) - { - m_mainSelected = nullptr; - emit signalTrackEntrySelectionChanged(nullptr); - } - }); - - te->m_channelName = m_trackName; - m_entryList.append(te); - - - TrackEntry *mainTe = getParentTopicOfFilter(te); - - if (mainTe != nullptr) - { - - if ((te->m_startTime + te->m_length) > mainTe->m_length) - te->m_length = mainTe->m_length - te->m_startTime; - - te->m_filterParentChannel = mainTe->m_channelName; - } - - - if (selectTrackEntry && !m_readonly) - selectEntry(te); - - emit signalTrackEntryAdded(te); - - update(); - return true; -} - - -void TrackContentWidget::removeTrackEntryByTopicId(TrackEntry *te) -{ - auto itE = std::remove_if(m_entryList.begin(), m_entryList.end(), - [te](TrackEntry * pTE) - { - if (pTE->m_topicId == te->m_topicId) - { - pTE->deleteLater(); - return true; - } - - return false; - }); - m_entryList.erase(itE, m_entryList.end()); - - - itE = std::remove_if(m_selected.begin(), m_selected.end(), - [te](TrackEntry * pTE) - { - if (pTE->m_topicId == te->m_topicId) - { - pTE->deleteLater(); - return true; - } - - return false; - }); - m_selected.erase(itE, m_selected.end()); -} - - -void TrackContentWidget::dropEvent(QDropEvent *event) -{ - if (m_readonly) - return; - - m_dragHandler->dropEvent(event); -} - - -void TrackContentWidget::dragMoveEvent(QDragMoveEvent *event) -{ - if (m_readonly) - return; - - m_dragHandler->dragMoveEvent(event); -} - - -bool TrackContentWidget::canMoveAll(int offset) const -{ - if (m_selected.isEmpty()) - return true; - - auto leftMostI = std::min_element(m_selected.constBegin(), m_selected.constEnd(), [](const TrackEntry * a, const TrackEntry * b) - { - return a->m_startTime < b->m_startTime; - }); - - if (leftMostI == m_selected.constEnd()) - return false; - - int startTimeMostLeft = (*leftMostI)->m_startTime; - - - QVector cols; - - int newTime = startTimeMostLeft + offset; - checkForCollisions(this, m_selected, newTime, cols, true); - - QVector parls; - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - checkForParallelTopics(tcw, m_selected, newTime, parls); - - if (!cols.isEmpty() || !parls.isEmpty()) - return false; - - return true; -} - - -void TrackContentWidget::forceMoveAll(int offset) -{ - for (auto i = m_selected.begin(); i != m_selected.end(); ++i) - { - TrackEntry *te = *i; - - - int newTime = te->m_startTime + offset; - - if (newTime < 0) - newTime = 0; - - - - - te->m_startTime = newTime; - } - - update(); - emit signalChangesMade(); -} - - -void TrackContentWidget::deleteSelectedTopics() -{ - m_mainSelected = nullptr; - m_selectHandler->m_pressedEntry = nullptr; - - bool somethingSeleted = false; - for (auto &pTopic : m_selected) - { - m_preview.removeAll(pTopic); - m_entryList.removeAll(pTopic); - emit signalTrackEntryDeleted(pTopic->m_topicId); - pTopic->deleteLater(); - somethingSeleted = true; - } - - if (somethingSeleted) - emit signalTrackEntrySelectionChanged(nullptr); - - m_selected.clear(); - emit signalChangesMade(); - update(); -} - - -void TrackContentWidget::mouseDoubleClickEvent(QMouseEvent *event) -{ - if (m_readonly) - return; - - m_selectHandler->mouseDoubleClick(event); -} - - -void TrackContentWidget::keyPressEvent(QKeyEvent *event) -{ - if (m_readonly) - return; - - if (event->key() == Qt::Key_Delete) - { - emit signalDeleteSelectedTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_C) - { - emit signalCopyTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_V) - { - emit signalPasteTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_X) - { - emit signalCutTopics(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_A) - { - emit signalSelectAllGlobal(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_S) - { - emit signalSave(); - } - else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_D) - { - setDescriptionForSelected(); - } - else - { - m_resizeHandler->keyPressEvent(event); - m_selectHandler->keyPressEvent(event); - m_dragHandler->keyPressEvent(event); - } - - if (event->key() == Qt::Key_Escape) - { - emit signalEscapeRubberband(); - emit signalMultiLineMultiSelectionDone(-1); - } -} - - -void TrackContentWidget::selectEntry(int topicId) -{ - for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i) - { - TrackEntry *te = *i; - - if (te->m_topicId == topicId) - { - selectEntry((te)); - return; - } - } -} - - -void TrackContentWidget::selectEntriesAtPositions(int fromPos, int toPos) -{ - m_unselectAllImmune = true; - - for (TrackEntry *te : qAsConst(m_entryList)) - { - int start = m_tools->getPos(te->m_startTime); - int end = m_tools->getPos(te->m_startTime + te->m_length); - if (end >= fromPos && start <= toPos) - selectEntry(te); - } - - m_unselectAllImmune = false; -} - - -void TrackContentWidget::selectEntry(TrackEntry *te) -{ - bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier); - - if (!m_unselectAllImmune && !ctrl) - unselect(); - - if (te == nullptr) - return; - - if (!m_selected.contains(te)) - { - m_selected.append(te); - m_mainSelected = te; - - emit signalTrackEntrySelectionChanged(nullptr); - } - else - { - m_selected.removeAll(te); - if (m_mainSelected == te) - m_mainSelected = nullptr; - - emit signalTrackEntrySelectionChanged(nullptr); - } - - update(); -} - - -bool TrackContentWidget::canPasteTopics(const QVector &topics, int offsetTime, int pasteTime) const -{ - TemporaryPointerVector newTes; - - newTes.reserve(topics.size()); - for (TrackEntry *te : qAsConst(topics)) - { - TrackEntry *newTe = new TrackEntry(te); - newTe->m_startTime = (newTe->m_startTime - offsetTime + pasteTime); - newTes += newTe; - } - - - - QVector dummy_vecCollidingTopics; - - bool parl = false; - - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - { - parl = checkForParallelTopics(tcw, newTes, pasteTime, true ); - if (parl) - break; - } - - if (checkForCollisions(this, newTes, pasteTime, dummy_vecCollidingTopics, false, true) || parl) - return false; - - return true; - - - -} - - -void TrackContentWidget::forcePasteTopics(const QVector &topics, int offsetTime, int pasteTime) -{ - - - m_selected.clear(); - - - for (TrackEntry *te : qAsConst(topics)) - { - TrackEntry *newTe = new TrackEntry(te); - newTe->m_topicId = m_scriptTimeline->nextUniqueId(); - newTe->m_startTime = (newTe->m_startTime - offsetTime + pasteTime); - newTe->isSet = true; - - m_unselectAllImmune = true; - if (!m_trackWidget->addTrackEntry(newTe, false)) - { - qWarning() << "TrackContentWidget::forcePasteTopics: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed"; - delete newTe; - delete te; - continue; - } - m_unselectAllImmune = false; - } -} - - -bool TrackContentWidget::allowedByType(const TrackEntry *te) const -{ - if (m_disallowedTypes.contains(te->m_type)) - return false; - - if (m_allowedTypes.isEmpty()) - return true; - - return (m_allowedTypes.contains(te->m_type)); -} - - -void TrackContentWidget::setTrackName(const QString &trackName) -{ - m_trackName = trackName; - - for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i) - { - TrackEntry *te = *i; - te->m_channelName = m_trackName; - } -} - - -TrackEntry *TrackContentWidget::getTrackEntryOnPos(QPoint globalPos) -{ - QPoint clickedPos = mapFromGlobal(globalPos); - int clickedPosY = clickedPos.y(); - int clickedPosX = clickedPos.x(); - - if (clickedPosY < 0 || clickedPosY > height()) - return nullptr; - - int clickedTime = m_tools->getTime(clickedPosX, m_isFilter); - - return getTrackEntryAt(clickedTime); -} - - -QVector TrackContentWidget::dataToItems(const QByteArray &data, int &offset) -{ - if (data.isEmpty()) - return {}; - - QDataStream dataStream(data); - - int count; - dataStream >> count; - dataStream >> offset; - - QVector tes; - tes.reserve(count); - for (int i = 0; i < count; i++) - { - TrackEntry *te = new TrackEntry; - dataStream >> *te; - te->m_startTime -= offset; - tes += te; - } - - return tes; -} - - -bool TrackContentWidget::dragEnter(const QByteArray &itemData) -{ - dragEnd(); - - TemporaryPointerVector tes(dataToItems(itemData, m_dragOffsetTime)); - - if (tes.isEmpty()) - return false; - - for (TrackEntry *te : qAsConst(tes)) - { - if (!allowedByType(te)) - { - qWarning() << "TrackContentWidget::dragEnter: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed"; - return false; - } - } - - if (m_isFilter && m_mainTopicId != -1) - { - auto it = std::find_if(tes.begin(), tes.end(), [this](const TrackEntry * a) - { - return (a->m_topicId == m_mainTopicId); - }); - - if (it != tes.end()) - return false; - } - - const QVector &thisTes = getTrackEntries(); - - - if (m_isMultiLineMultiSelectionMode) - { - for (TrackEntry *te : qAsConst(tes)) - { - bool found = false; - - for (TrackEntry *thisTe : qAsConst(thisTes)) - { - if (thisTe->m_topicId == te->m_topicId) - { - found = true; - break; - } - } - - if (!found) - return false; - } - } - - sortTrackEntriesChronologically(tes); - - - - if (!m_isFilter && tes.size() == 1) - { - TrackEntry *pPreview = tes.front(); - bool hasJustBeenAddedByUser = true; - - for (auto pTE : qAsConst(m_entryList)) - { - if (pPreview->m_topicId == pTE->m_topicId) - { - hasJustBeenAddedByUser = false; - break; - } - } - - - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - { - for (auto pTE : qAsConst(tcw->m_entryList)) - { - if (pPreview->m_topicId == pTE->m_topicId) - { - hasJustBeenAddedByUser = false; - break; - } - } - } - - if (hasJustBeenAddedByUser) - { - - m_selected.clear(); - - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - tcw->m_selected.clear(); - } - } - - tes.setDiscardable(false); - m_preview = tes; - return true; -} - -void TrackContentWidget::clearDrag() -{ - qDeleteAll(m_preview); - m_preview.clear(); - m_dragOffsetTime = -1; - m_dragPos = -1; - m_dragPossible = false; - m_moveValid = false; - update(); -} - -int TrackContentWidget::getDropPos() const -{ - return (m_dragHandler == nullptr) ? -1 : m_dragHandler->m_dropPos; -} - - -void TrackContentWidget::dragEnd() -{ - clearDrag(); -} - -bool TrackContentWidget::dragDrop(int pos) -{ - if (!isDragPossible() || !m_moveValid) - { - dragEnd(); - return false; - } - - int dropTime = m_tools->getTime(pos, m_isFilter) - m_dragOffsetTime; - QVector selectedOld = m_selected; - - if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && !m_isMultiLineMultiSelectionMode) - unselect(); - - int firstTeTime = -1; - - for (const TrackEntry *te : qAsConst(m_preview)) - { - if (firstTeTime > te->m_startTime || firstTeTime == -1) - firstTeTime = te->m_startTime; - } - - int selected = firstTeTime; - int diff = dropTime - selected; - - - for (auto i = m_preview.constBegin(); i != m_preview.constEnd(); ++i) - { - TrackEntry *prevTe = *i; - TrackEntry *newTe = new TrackEntry(prevTe); - newTe->m_topicId = m_scriptTimeline->nextUniqueId(); - newTe->m_startTime = prevTe->m_startTime + diff; - newTe->isSet = true; - - - - m_unselectAllImmune = true; - if (m_isFilter) - { - addTrackEntry(newTe, true); - } - else - { - if (m_trackWidget == nullptr || !m_trackWidget->addTrackEntry(newTe, true)) - { - delete newTe; - return false; - } - } - - update(); - - - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - tcw->removeTrackEntryByTopicId(prevTe); - - m_unselectAllImmune = false; - } - - dragEnd(); - - if (m_isMultiLineMultiSelectionMode) - { - - for (TrackEntry *oldSel : qAsConst(selectedOld)) - emit signalTrackEntryDeleted(oldSel->m_topicId); - qDeleteAll(selectedOld); - - emit signalMultiLineMultiSelectionDone(pos); - } - - emit signalChangesMade(); - return true; -} - -bool TrackContentWidget::dragMove(int pos) -{ - m_dragPos = pos; - - - int dropTime_s = m_tools->getTime(m_dragPos, m_isFilter) - m_dragOffsetTime; - checkForCollisions(this, m_preview, dropTime_s, m_dragHandler->m_vecCollisions, true); - - m_dragHandler->m_vecParallel.clear(); - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - checkForParallelTopics(tcw, m_preview, dropTime_s, m_dragHandler->m_vecParallel); - - m_moveValid = m_dragHandler->m_vecCollisions.isEmpty() && m_dragHandler->m_vecParallel.isEmpty(); - - if (!m_moveValid || !isDragPossible()) - { - - setCursor(Qt::ForbiddenCursor); - } - else - { - setCursor(Qt::DragMoveCursor); - } - - update(); - - if (m_isMultiLineMultiSelectionMode) - { - if (m_moveValid) - emit signalMultiLineMultiSelectionOkay(); - else - emit signalMultiLineMultiSelectionFailed(); - } - - - - static int scrollByPixels = 1; - - if (pos >= (m_width - 60)) - emit signalScroll(scrollByPixels); - else if (pos < 60) - emit signalScroll(-scrollByPixels); - - return m_moveValid; -} - - -TrackEntry *TrackContentWidget::trackEntryAtPos(int x) const -{ - int time = m_tools->getTime(x); - const QVector tes = getTrackEntries(); - - for (TrackEntry *te : tes) - { - int start = te->m_startTime; - if (time >= start && time <= (start + te->m_length)) - return te; - } - - return nullptr; -} - - -QByteArray TrackContentWidget::itemsToData(const QVector &items, int dragPosTime, bool calculateFirstTeTime) -{ - int count = items.count(); - if (!count) - return {}; - - QByteArray itemData; - QDataStream dataStream(&itemData, QIODevice::WriteOnly); - - dataStream << count; - - if (dragPosTime != -1) - { - int firstTeTime = -1; - - if (calculateFirstTeTime) - { - for (TrackEntry *te : items) - { - if (firstTeTime > te->m_startTime || firstTeTime == -1) - firstTeTime = te->m_startTime; - } - } - else - firstTeTime = 0; - - int finalOffsetTime = dragPosTime - firstTeTime; - dataStream << finalOffsetTime; - } - else - dataStream << 0; - - - for (auto i = items.constBegin(); i != items.constEnd(); ++i) - { - TrackEntry *te = *i; - dataStream << *te; - } - - return itemData; -} - - -QByteArray TrackContentWidget::selectedItemsToData(int dragPosTime, bool calculateFirstTeTime) -{ - return itemsToData(m_selected, dragPosTime, calculateFirstTeTime); -} - - -void TrackContentWidget::setDragPossible(bool possible) -{ - m_dragPossible = possible; - update(); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_content_widget.h b/Lts/Common/LtsScriptTimeline/track_content_widget.h deleted file mode 100755 index a29435791..000000000 --- a/Lts/Common/LtsScriptTimeline/track_content_widget.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include - -#include - - -namespace LtsScriptTimeline -{ -class TrackEntry; -enum class TrackEntryType; -class TrackWidget; -class ScriptTimeline; -class Tools; -class DragHandler; -class ResizeHandler; -class SelectHandler; - -LTSSCRIPTTIMELINE_EXPORT QDataStream &operator<<(QDataStream &ds, const TrackEntry &te); -LTSSCRIPTTIMELINE_EXPORT QDataStream &operator>>(QDataStream &ds, TrackEntry &te); -LTSSCRIPTTIMELINE_EXPORT bool isCollision(int start_s, int end_s, int toCompare_start_s, int toCompare_end_s); -LTSSCRIPTTIMELINE_EXPORT bool isCollision2(int startPreview_s, int endPreview_s, const TrackEntry *pTrackEntry); - -class LTSSCRIPTTIMELINE_EXPORT TrackContentWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TrackContentWidget(QWidget *parent = nullptr, bool isFilter = false, Tools *tools = nullptr, int mainTopicId = -1); - ~TrackContentWidget() override; - - bool addTrackEntry(TrackEntry *te, bool selectTrackEntry); - void removeTrackEntryByTopicId(TrackEntry *te); - - void selectAll(); - void unselectAll(); - const QVector &getTrackEntries() const { return m_entryList; } - const QVector &getSelectedEntries() const { return m_selected; } - void setIndicatorTime(int time); - void setTrackName(const QString &trackName); - void deleteSelectedTopics(); - void selectEntry(int topicId); - void setTopicFilterId(int id) { m_topicFilterId = id; } - int topicFilterId() const { return m_topicFilterId; } - void setReadonly(bool readonly); - void addAllowedType(const TrackEntryType &tet) { m_allowedTypes.append(tet); } - void addDisallowedType(const TrackEntryType &tet) { m_disallowedTypes.append(tet); } - void setTrackWidget(TrackWidget *trackWidget); - void setScriptTimeline(ScriptTimeline *scriptTimeline) { m_scriptTimeline = scriptTimeline; } - void setTools(Tools *tools); - void addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget); - static QString parallelError(); - bool isMultiLineMultiSelectionMode() const { return m_isMultiLineMultiSelectionMode; } - void setMultiLineMultiSelectionMode(bool active) { m_isMultiLineMultiSelectionMode = active; } - bool isDragPossible() const { return (!m_isMultiLineMultiSelectionMode || m_dragPossible); } - void selectEntriesAtPositions(int fromPos, int toPos); - void forceLocalUnselect(); - - static bool checkForParallelTopics(const TrackContentWidget *pTCW, const QVector &vecPreviewTrackEntries, int dropTime_s, bool hack = false); - static bool checkForParallelTopics(const TrackContentWidget *tcw, const QVector &preview, int dropTime_s, QVector &vecParallelTopics, bool ignoreSelectedTopics = false, bool hack = false); - static bool checkForCollisions(const TrackContentWidget *tcw, const QVector &preview, int dropTime_s, - QVector &vecCollisions, bool ignoreSelected = false, bool ignoreSelfCheck = false); - static bool checkForCollisions(const TrackContentWidget *pTCW, const QVector &vecPreviewTrackEntries, int dropTime_s); - bool checkForCollisionsSimple(const TrackEntry &previewEntry) const; - - QByteArray selectedItemsToData(int dragPosTime = 0, bool calculateFirstTeTime = true); - static QByteArray itemsToData(const QVector &items, int dragPosTime = 0, bool calculateFirstTeTime = true); - static QVector dataToItems(const QByteArray &data, int &offset); - bool canPasteTopics(const QVector &topics, int offsetTime, int pasteTime) const; - void forcePasteTopics(const QVector &topics, int offsetTime, int pasteTime); - bool canMoveAll(int offset) const; - void forceMoveAll(int offset); - bool setDescriptionForSelected(); - - static QString mime; - - void dragEnd(); - void setDragPossible(bool possible); - void dragLeave(); - bool dragEnter(const QByteArray &itemData); - bool dragMove(int pos); - bool dragDrop(int pos); - void clearDrag(); - int getDropPos() const; - - TrackWidget *trackWidget() { return m_trackWidget; } - TrackEntry *trackEntryAtPos(int x) const; - - void keyPressEvent(QKeyEvent *event) override; - -protected: - void paintEvent(QPaintEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - -private: - TrackEntry *getTrackEntryOnPos(QPoint globalPos); - bool allowedByType(const TrackEntry *te) const; - TrackEntry *getParentTopicOfFilter(TrackEntry *filterTe) const; - - int m_topicFilterId = -1; - - friend class SelectHandler; - SelectHandler *m_selectHandler; - - friend class ResizeHandler; - ResizeHandler *m_resizeHandler; - - friend class DragHandler; - DragHandler *m_dragHandler; - - - void selectEntry(TrackEntry *te); - void addFilterType(const TrackEntryType &tet); - void unselect(); - void moveAll(int offset); - bool handleResize(int cursorTime, bool leftButton); - bool handleDrag(int cursorTime, bool leftButton); - void cancelResize(); - TrackEntry *getTrackEntryAt(int clickedTime); - void clearPreview(); - int dragPos() const { return m_dragPos; } - - - - int m_dragOffsetTime; - int m_dragPos; - QVector m_entryList; - QVector m_selected; - - TrackEntry *m_mainSelected; - QVector m_preview; - int m_width; - bool m_cursorSet; - int m_oldEntryLength; - bool m_unselectAllImmune; - int m_indicatorTime; - QString m_trackName; - QVector m_allowedTypes; - QVector m_disallowedTypes; - bool m_isFilter; - bool m_readonly; - TrackWidget *m_trackWidget; - ScriptTimeline *m_scriptTimeline; - Tools *m_tools; - QVector m_tcwCorresponding; - bool m_isMultiLineMultiSelectionMode; - bool m_multiLineMultiSelectionOkay; - bool m_dragPossible; - bool m_moveValid; - int m_mainTopicId; - -signals: - void signalUnselectAll(); - void signalTrackEntrySelectionChanged(TrackEntry *); - void signalChangesMade(); - void signalTrackEntryAdded(TrackEntry *te); - void signalTrackEntryDeleted(const int topicId); - void signalEditEntry(const int topicId); - void signalMultiLineMultiSelectionFailed(); - void signalMultiLineMultiSelectionOkay(); - void signalMultiLineMultiSelectionDone(int dropPos); - void signalMultiLineMultiSelectionStart(int dragPosTime); - void signalMultiLineMultiSelectionMove(int x); - void signalStartRubberband(QPoint pos); - void signalStopRubberband(); - void signalEscapeRubberband(); - void signalModifyRubberband(); - void signalSelectAllGlobal(); - void signalCopyTopics(); - void signalPasteTopics(); - void signalCutTopics(); - void signalDeleteSelectedTopics(); - void signalSave(); - void signalMoveAll(int offset); - void signalScroll(int incr); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp b/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp deleted file mode 100755 index 309364b3a..000000000 --- a/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "track_drag_handler.h" - -#include "track_content_widget.h" -#include "track_entry.h" -#include "tools.h" - -#include "track_widget.h" -#include "script_timeline.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsScriptTimeline -{ - -void DragHandler::dragEnterEvent(QDragEnterEvent *event) -{ - if (m_tcw->isMultiLineMultiSelectionMode()) - { - if (event->source() != m_tcw) - { - event->ignore(); - return; - } - else - { - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - tcw->dragEnter(tcw->selectedItemsToData(tcw->m_tools->getTime(m_dragStartPos.x()))); - } - } - - m_tcw->dragEnd(); - - if (!event->mimeData()->hasFormat(TrackContentWidget::mime)) - return; - - if (m_tcw->dragEnter(event->mimeData()->data(TrackContentWidget::mime))) - event->acceptProposedAction(); -} - - -void DragHandler::dragLeaveEvent(QDragLeaveEvent *event) -{ - if (m_tcw->isMultiLineMultiSelectionMode()) - { - event->ignore(); - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - tcw->dragEnd(); - } - - m_tcw->dragEnd(); -} - - -void DragHandler::dropEvent(QDropEvent *event) -{ - int dropPos = event->pos().x(); - int dropTime = m_tcw->m_tools->getTime(dropPos, m_tcw->m_isFilter) - m_tcw->m_dragOffsetTime; - - - - for (const auto &tcw : qAsConst(m_tcwCorresponding)) - { - bool hasParallelTopicsOfSameType = TrackContentWidget::checkForParallelTopics(tcw, m_tcw->m_preview, dropTime); - - if (hasParallelTopicsOfSameType) - { - new LtsWidgets::LtsMessageDialogNew(m_tcw, TrackContentWidget::parallelError(), QObject::tr("Error")); - break; - } - } - - if (m_tcw->dragDrop(dropPos)) - event->acceptProposedAction(); - else - emit m_tcw->signalMultiLineMultiSelectionDone(0); -} - - -void DragHandler::dragMoveEvent(QDragMoveEvent *event) -{ - int pos = event->pos().x(); - - if (m_tcw->dragMove(pos)) - event->acceptProposedAction(); - - - if (m_tcw->isMultiLineMultiSelectionMode()) - emit m_tcw->signalMultiLineMultiSelectionMove(pos); -} - - -void DragHandler::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() != Qt::LeftButton) - return; - m_dragStartPos = event->pos(); -} - - -void DragHandler::mouseMoveEvent(QMouseEvent *event) -{ - if (event->buttons() != Qt::LeftButton) - return; - - QPoint cursorPos = m_tcw->mapFromGlobal(event->globalPos()); - - - if (!m_tcw->m_mainSelected - || ((cursorPos - m_dragStartPos).manhattanLength() < QApplication::startDragDistance() - && (m_dragStartPos - cursorPos).manhattanLength() < QApplication::startDragDistance())) - return; - - QVector selected = m_tcw->getSelectedEntries(); - - if (selected.isEmpty()) - return; - - int cursorPosTime = m_tcw->m_tools->getTime(m_dragStartPos.x()); - - QMimeData *mimeData = new QMimeData(); - mimeData->setData(TrackContentWidget::mime, m_tcw->selectedItemsToData(cursorPosTime)); - - QDrag *drag = new QDrag(m_tcw); - drag->setMimeData(mimeData); - - if (m_tcw->isMultiLineMultiSelectionMode()) - emit m_tcw->signalMultiLineMultiSelectionStart(cursorPosTime); - - QVector selectedOld = m_tcw->m_selected; - - if (drag->exec(Qt::MoveAction) == Qt::MoveAction) - { - if (!m_tcw->isMultiLineMultiSelectionMode()) - { - - for (auto i = selectedOld.begin(); i != selectedOld.end(); ++i) - emit m_tcw->signalTrackEntryDeleted((*i)->m_topicId); - qDeleteAll(selectedOld); - } - } - else - { - emit m_tcw->signalMultiLineMultiSelectionDone(-1); - } -} - - -void DragHandler::keyPressEvent(QKeyEvent *event) -{ - switch (event->key()) - { - case Qt::Key_Left: - { - emit m_tcw->signalMoveAll(-1); - break; - } - - case Qt::Key_Right: - { - emit m_tcw->signalMoveAll(1); - break; - } - - default: - { - break; - } - } -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_drag_handler.h b/Lts/Common/LtsScriptTimeline/track_drag_handler.h deleted file mode 100755 index 185db4c25..000000000 --- a/Lts/Common/LtsScriptTimeline/track_drag_handler.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include - -namespace LtsScriptTimeline -{ -class TrackContentWidget; -class TrackEntry; - -class LTSSCRIPTTIMELINE_EXPORT DragHandler -{ -public: - explicit DragHandler(TrackContentWidget *_tcw) - : m_tcw(_tcw) - , m_dropPos(-1) - {} - - void dragEnterEvent(QDragEnterEvent *event); - void dragLeaveEvent(QDragLeaveEvent *event); - void dropEvent(QDropEvent *event); - void dragMoveEvent(QDragMoveEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void keyPressEvent(QKeyEvent *event); - - TrackContentWidget *m_tcw; - QVector m_tcwCorresponding; - - QPoint m_dragStartPos; - QVector m_vecCollisions, m_vecParallel; - int m_dropPos; -}; -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry.h b/Lts/Common/LtsScriptTimeline/track_entry.h deleted file mode 100755 index 98ca84186..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry.h +++ /dev/null @@ -1,187 +0,0 @@ -#pragma once - -#include -#include "track_entry_painters.h" - -#include -#include - -namespace LtsScriptTimeline -{ - -class LTSSCRIPTTIMELINE_EXPORT TrackEntry : public QObject -{ - Q_OBJECT - -public: - explicit TrackEntry(const TrackEntryType &tet = TrackEntryType::INVALID) - : QObject() - , m_topicId(-1) - , m_startTime(-1) - , m_length(-1) - , m_type(tet) - , isSet(false) - , index(-1) - , x(-1) - , y(-1) - , h(-1) - , w(-1) - {} - - explicit TrackEntry(int topicId, - int startTime, - int length, - LtsScriptTimeline::TrackEntryType type, - const QJsonObject &detailData, - const QString &channelName, - const QString &description) - : QObject() - , m_topicId(topicId) - , m_startTime(startTime) - , m_length(length) - , m_type(type) - , isSet(false) - , index(-1) - , x(-1) - , y(-1) - , h(-1) - , w(-1) - , m_detailData(detailData) - , m_channelName(channelName) - - , m_description(description) - {} - - - explicit TrackEntry(const TrackEntry &te) - : QObject() - , m_topicId(te.m_topicId) - , m_startTime(te.m_startTime) - , m_length(te.m_length) - , m_type(te.m_type) - , isSet(te.isSet) - , index(te.index) - , x(te.x) - , y(te.y) - , h(te.h) - , w(te.w) - , m_detailData(te.m_detailData) - , m_channelName(te.m_channelName) - , m_description(te.m_description) - , m_filterParentChannel(te.m_filterParentChannel) - {} - - explicit TrackEntry(TrackEntry *te) - : QObject() - , m_topicId(te->m_topicId) - , m_startTime(te->m_startTime) - , m_length(te->m_length) - , m_type(te->m_type) - , isSet(te->isSet) - , index(te->index) - , x(te->x) - , y(te->y) - , h(te->h) - , w(te->w) - , m_detailData(te->m_detailData) - , m_channelName(te->m_channelName) - , m_description(te->m_description) - , m_filterParentChannel(te->m_filterParentChannel) - {} - - TrackEntry(const TrackEntryType &tet, int length) - : QObject() - , m_topicId(-1) - , m_startTime(-1) - , m_length(length) - , m_type(tet) - , isSet(false) - , index(-1) - , x(-1) - , y(-1) - , h(-1) - , w(-1) - {} - - ~TrackEntry() - { - - } - - TrackEntry &operator=(const TrackEntry &other) - { - this->m_topicId = other.m_topicId; - this->m_startTime = other.m_startTime; - this->m_length = other.m_length; - this->m_type = other.m_type; - this->isSet = other.isSet; - this->index = other.index; - this->x = other.x; - this->y = other.y; - this->h = other.h; - this->w = other.w; - this->m_detailData = other.m_detailData; - this->m_channelName = other.m_channelName; - this->m_filterParentChannel = other.m_filterParentChannel; - this->m_description = other.m_description; - - return *this; - } - - int m_topicId; - int m_startTime; - int m_length; - TrackEntryType m_type; - bool isSet; - int index; - - - double x; - double y; - double h; - double w; - - QJsonObject m_detailData; - QString m_channelName; - QString m_description; - QString m_filterParentChannel; - - QJsonObject toJson() const - { - return QJsonObject({{QStringLiteral("topicId"), m_topicId}, - {QStringLiteral("startTime"), m_startTime}, - {QStringLiteral("length"), m_length}, - {QStringLiteral("type"), static_cast(m_type)}, - {QStringLiteral("isSet"), isSet}, - {QStringLiteral("index"), index}, - {QStringLiteral("x"), x}, - {QStringLiteral("y"), y}, - {QStringLiteral("w"), w}, - {QStringLiteral("h"), h}, - {QStringLiteral("detailData"), m_detailData}, - {QStringLiteral("channelName"), m_channelName}, - {QStringLiteral("description"), m_description}, - {QStringLiteral("filterParentChannel"), m_filterParentChannel} - }); - } - - void fromJson(const QJsonObject &obj) - { - m_topicId = obj[QStringLiteral("topicId")].toInt(); - m_startTime = obj[QStringLiteral("startTime")].toInt(-1); - m_length = obj[QStringLiteral("length")].toInt(-1); - m_type = static_cast(obj[QStringLiteral("type")].toInt()); - isSet = obj[QStringLiteral("isSet")].toBool(); - index = obj[QStringLiteral("index")].toInt(); - x = obj[QStringLiteral("x")].toDouble(); - y = obj[QStringLiteral("y")].toDouble(); - w = obj[QStringLiteral("w")].toDouble(); - h = obj[QStringLiteral("h")].toDouble(); - m_detailData = obj[QStringLiteral("detailData")].toObject(); - m_channelName = obj[QStringLiteral("channelName")].toString(); - m_description = obj[QStringLiteral("description")].toString(); - m_filterParentChannel = obj[QStringLiteral("filterParentChannel")].toString(); - } -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp b/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp deleted file mode 100755 index 4d01a6b47..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp +++ /dev/null @@ -1,321 +0,0 @@ - - - -#include "track_entry_painters.h" -#include "track_entry.h" - -#include -#include - - - - -namespace LtsScriptTimeline -{ - -QString translateTrackEntryType(const TrackEntryType &tet) -{ - switch (tet) - { - case TrackEntryType::INVALID: return QObject::tr(""); - case TrackEntryType::SituationMap: return QObject::tr("Map"); - case TrackEntryType::Situation: return QObject::tr("Situation"); - case TrackEntryType::SituationReplay: return QObject::tr("Replay"); - case TrackEntryType::Snapshot: return QObject::tr("Snapshot"); - case TrackEntryType::Image: return QObject::tr("Image"); - case TrackEntryType::Text: return QObject::tr("Text"); - case TrackEntryType::Pdf: return QObject::tr("Pdf"); - case TrackEntryType::Xml: return QObject::tr("Xml"); - case TrackEntryType::MSOffice: return QObject::tr("MS Office"); - case TrackEntryType::OpenOffice: return QObject::tr("Open Office"); - case TrackEntryType::Audio: return QObject::tr("Audio"); - case TrackEntryType::Video: return QObject::tr("Video"); - case TrackEntryType::Building: return QObject::tr("Building"); - case TrackEntryType::Evaluation: return QObject::tr("Evaluation"); - case TrackEntryType::Report: return QObject::tr("Report"); - - - case TrackEntryType::TacticalPlan: return QObject::tr("Tactical Plan Map Filter"); - case TrackEntryType::SituationMapFilter: return QObject::tr("Map Filter"); - } - - return QString(); -} - - -QString trackEntryTypeName(const TrackEntryType &tet) -{ - switch (tet) - { - case TrackEntryType::INVALID: return QStringLiteral(""); - case TrackEntryType::SituationMap: return QStringLiteral("SituationMap"); - case TrackEntryType::Situation: return QStringLiteral("SituationReplayAndMap"); - case TrackEntryType::SituationReplay: return QStringLiteral("SituationReplay"); - case TrackEntryType::Image: return QStringLiteral("Image"); - case TrackEntryType::Text: return QStringLiteral("Text"); - case TrackEntryType::Pdf: return QStringLiteral("Pdf"); - case TrackEntryType::Xml: return QStringLiteral("Xml"); - case TrackEntryType::TacticalPlan: return QStringLiteral("TacticalPlan"); - case TrackEntryType::MSOffice: return QStringLiteral("MSOffice"); - case TrackEntryType::OpenOffice: return QStringLiteral("OpenOffice"); - case TrackEntryType::Audio: return QStringLiteral("Audio"); - case TrackEntryType::Video: return QStringLiteral("Video"); - case TrackEntryType::Building: return QStringLiteral("Building"); - case TrackEntryType::SituationMapFilter: return QStringLiteral("SituationMapFilter"); - case TrackEntryType::Snapshot: return QStringLiteral("Snapshot"); - case TrackEntryType::Evaluation: return QStringLiteral("Evaluation"); - case TrackEntryType::Report: return QStringLiteral("Report"); - } - - return QString(); -} - - -QIcon appToIcon(const TrackEntryType type) -{ - switch (type) - { - case LtsScriptTimeline::TrackEntryType::INVALID: - return QIcon(); - case LtsScriptTimeline::TrackEntryType::SituationMap: - return QIcon(QLatin1String("icon:add_situation.svg")); - case LtsScriptTimeline::TrackEntryType::Situation: - return QIcon(QLatin1String("icon:add_situation_combined.svg")); - case LtsScriptTimeline::TrackEntryType::SituationReplay: - return QIcon(QLatin1String("icon:add_restart.svg")); - case LtsScriptTimeline::TrackEntryType::Image: - return QIcon(QLatin1String("icon:create_image_topic.svg")); - case LtsScriptTimeline::TrackEntryType::Text: - return QIcon(QLatin1String("icon:document-new-txt.svg")); - case LtsScriptTimeline::TrackEntryType::Pdf: - return QIcon(QLatin1String("icon:create_pdf_topic.svg")); - case LtsScriptTimeline::TrackEntryType::Xml: - return QIcon(QLatin1String("icon:document-new-xml.svg")); - case LtsScriptTimeline::TrackEntryType::TacticalPlan: - return QIcon(QLatin1String("icon:tactical_organization.svg")); - case LtsScriptTimeline::TrackEntryType::MSOffice: - return QIcon(QLatin1String("icon:ms_office.svg")); - case LtsScriptTimeline::TrackEntryType::OpenOffice: - return QIcon(QLatin1String("icon:documents.svg")); - case LtsScriptTimeline::TrackEntryType::Audio: - return QIcon(QLatin1String("icon:create_lecture_topic.svg")); - case LtsScriptTimeline::TrackEntryType::Video: - return QIcon(QLatin1String("icon:add_video_menu.svg")); - case LtsScriptTimeline::TrackEntryType::Building: - return QIcon(QLatin1String("icon:configure_buildings.svg")); - case LtsScriptTimeline::TrackEntryType::SituationMapFilter: - return QIcon(QLatin1String("icon:filter_player.svg")); - case LtsScriptTimeline::TrackEntryType::Snapshot: - return QIcon(QLatin1String("icon:create_snapshot.svg")); - case LtsScriptTimeline::TrackEntryType::Evaluation: - return QIcon(QLatin1String("icon:add_evaluation_elements.svg")); - case LtsScriptTimeline::TrackEntryType::Report: - return QIcon(QLatin1String("icon:add_reports_display.svg")); - } - - return QIcon(); -} - - -TrackEntryPainter *getTrackEntryPainter(const TrackEntryType &type) -{ - TrackEntryPainter *trackEntryPainter = nullptr; - static TrackEntryPainter painter; - - switch (type) - { - case TrackEntryType::SituationMap: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#b1cbbb")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Situation: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#b1c1cb")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::SituationReplay: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#c94c4c")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Audio: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#034f84")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Video: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#618685")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Image: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#fefbd8")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Evaluation: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#d6cbd3")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Pdf: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#80ced6")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Text: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#92a8d1")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Xml: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#eea29a")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::TacticalPlan: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#dac292")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::MSOffice: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#f7cac9")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::OpenOffice: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#b2b2b2")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Building: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#c6bcb6")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::SituationMapFilter: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#36486b")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Snapshot: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#f18973")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - case TrackEntryType::Report: - { - painter = TrackEntryPainter(QColor(QStringLiteral("#feb236")), translateTrackEntryType(type)); - trackEntryPainter = static_cast(&painter); - break; - } - - default: - { - qWarning() << "no painter found for type" << (int)type; - trackEntryPainter = nullptr; - break; - } - } - - return trackEntryPainter; -} - - - -void drawTrackEntry(TrackEntry *te, QPainter &painter, const QRect &trackRect, bool isSelected, bool isEnabled) -{ - TrackEntryPainter *trackEntryPainter = getTrackEntryPainter(te->m_type); - - if (!trackEntryPainter) - - return; - - QColor trackEntryBgColor = trackEntryPainter->bgColor(); - - if (isSelected) - { - int r = trackEntryBgColor.red(); - int g = trackEntryBgColor.green(); - int b = trackEntryBgColor.blue(); - - trackEntryBgColor = trackEntryBgColor.lighter(); - painter.setPen(QColor(255 - r, 255 - g, 255 - b)); - } - else if (!isEnabled) - { - trackEntryBgColor = QColor(200, 200, 200); - painter.setPen(QColor("white")); - } - else - painter.setPen(QColor(36, 36, 36)); - - painter.setBrush(trackEntryBgColor); - painter.drawRect(trackRect); - - if (trackRect.width() < 20) - return; - - QRect trackEntryPainterRect(trackRect.adjusted(1, 1, -1, -1)); - - painter.save(); - painter.setClipping(true); - painter.setClipRect(trackEntryPainterRect, Qt::ReplaceClip); - - - trackEntryPainter->paintEvent(painter, trackEntryPainterRect ); - painter.drawText(trackEntryPainterRect.adjusted(3, 16, 5000, 0), te->m_description); - painter.restore(); - - if (trackRect.width() < 12) - return; - - - int trackRectEnd = trackRect.right(); - int gradientLength = 12; - QRect gradientRect(trackRectEnd - gradientLength, 2, gradientLength, trackRect.height() - 2); - QLinearGradient gradient(gradientRect.bottomRight(), gradientRect.bottomLeft()); - - int r = trackEntryBgColor.red(); - int g = trackEntryBgColor.green(); - int b = trackEntryBgColor.blue(); - - gradient.setColorAt(0, QColor(r, g, b, 255)); - gradient.setColorAt(1, QColor(r, g, b, 0)); - painter.fillRect(gradientRect, gradient); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_painters.h b/Lts/Common/LtsScriptTimeline/track_entry_painters.h deleted file mode 100755 index 0a19597b4..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_painters.h +++ /dev/null @@ -1,90 +0,0 @@ - - - -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace LtsScriptTimeline -{ -class TrackEntry; - -enum class TrackEntryType -{ - INVALID, - SituationMap, - SituationReplay, - Image, - Text, - Pdf, - Xml, - TacticalPlan, - MSOffice, - OpenOffice, - Audio, - Video, - Building, - SituationMapFilter, - Snapshot, - Evaluation, - Report, - Situation -}; -LTSSCRIPTTIMELINE_EXPORT QString translateTrackEntryType(const TrackEntryType &tet); -LTSSCRIPTTIMELINE_EXPORT QString trackEntryTypeName(const TrackEntryType &tet); -LTSSCRIPTTIMELINE_EXPORT QIcon appToIcon(const TrackEntryType type); -inline std::ostream &operator<<(std::ostream &stream, const TrackEntryType tet) -{ - return stream << trackEntryTypeName(tet).toStdString(); -} - -class LTSSCRIPTTIMELINE_EXPORT TrackEntryPainter -{ -public: - explicit TrackEntryPainter(const QColor &bgColor = QColor(0, 0, 0), const QString &trackEntryType = QStringLiteral("INVALID")) - : m_bgColor(bgColor) - , m_trackEntryType(trackEntryType) - { - } - - void paintEvent(QPainter &painter, const QRect &rect) - { - painter.setPen(QPen(Qt::black)); - painter.drawText(rect.adjusted(3, 0, 5000 , 0), m_trackEntryType); - } - - QColor bgColor() const - { - return m_bgColor; - } - - void setBgColor(const QColor &bgColor) - { - m_bgColor = bgColor; - } - - const QString &trackEntryType() const - { - return m_trackEntryType; - } - - void setTrackEntryType(const QString &trackEntryType) - { - m_trackEntryType = trackEntryType; - } - -protected: - QColor m_bgColor; - QString m_trackEntryType; -}; - -void drawTrackEntry(TrackEntry *te, QPainter &painter, const QRect &trackRect, bool isSelected, bool isEnabled = true); -TrackEntryPainter *getTrackEntryPainter(const TrackEntryType &type); - -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp b/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp deleted file mode 100755 index 4643569a2..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "track_entry_preview_widget.h" -#include "ui_track_entry_preview_widget.h" -#include "track_entry.h" - -#include -#include - -#include -#include -#include -#include - - -namespace LtsScriptTimeline -{ -TrackEntryPreviewWidget::TrackEntryPreviewWidget(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::TrackEntryPreviewWidget) -{ - m_ui->setupUi(this); -#ifndef LTS_DEBUG - m_ui->lbTopicId->hide(); - m_ui->lblTopicId->hide(); - - m_ui->lbLen->hide(); - m_ui->lblLen->hide(); -#endif - connect(m_ui->widget, &TrackEntryPreviewer::signalDelete, - this, &TrackEntryPreviewWidget::signalDelete); - - - connect(m_ui->widget, &TrackEntryPreviewer::signalStartDrag, - this, &TrackEntryPreviewWidget::signalStartDrag); - connect(m_ui->widget, &TrackEntryPreviewer::signalStopDrag, - this, &TrackEntryPreviewWidget::signalStopDrag); -} - - -TrackEntryPreviewWidget::~TrackEntryPreviewWidget() -{ - delete m_ui; -} - - -void TrackEntryPreviewWidget::contextMenuEvent(QContextMenuEvent *event) -{ -#ifndef LTS_DEBUG - if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("TrackEntryPreviewWidget.DebugMenu")) == QVariant(true)) -#endif - { - QMenu *m = new QMenu(this); - m->setAttribute(Qt::WA_DeleteOnClose); - - QPoint p = mapToGlobal(m->pos()); - m->setGeometry(QRect(p, QPoint(event->x(), event->y()))); - - m->addAction(QStringLiteral("enable"), this, [this]() - { - setTopicEnabled(true); - }); - - m->addAction(QStringLiteral("disable"), this, [this]() - { - setTopicEnabled(false); - }); - - m->addAction(QStringLiteral("show detail data"), this, [this]() - { - TrackEntry *te = m_ui->widget->trackEntry(); - - if (te == nullptr) - { - LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("no te was set")); - return; - } - - QTextEdit *ted = new QTextEdit(nullptr); - ted->setAttribute(Qt::WA_DeleteOnClose); - ted->setWindowTitle(QStringLiteral("Track Entry #%1 - Detail Data string").arg(te->m_topicId)); - ted->setText(QString::fromUtf8(QJsonDocument(te->m_detailData).toJson())); - ted->setReadOnly(true); - ted->show(); - }); - - m->show(); - } -} - - -void TrackEntryPreviewWidget::setTopicEnabled(bool enabled) -{ - m_ui->widget->setEnabled(enabled); -} - - -void TrackEntryPreviewWidget::setTrackEntry(TrackEntry *te) -{ - m_ui->lblDnD->setVisible(!te->isSet); - m_ui->widget->setVisible(!te->isSet); - - m_ui->lbTopicId->setText(QString::number(te->m_topicId)); - m_ui->lbLen->setText(QString::number(te->m_length)); - m_ui->widget->setTrackEntry(te); -} -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h b/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h deleted file mode 100755 index 7862f5c82..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -#include - - -namespace Ui -{ -class TrackEntryPreviewWidget; -} - -namespace LtsScriptTimeline -{ -class TrackEntry; - - -class LTSSCRIPTTIMELINE_EXPORT TrackEntryPreviewWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TrackEntryPreviewWidget(QWidget *parent = nullptr); - ~TrackEntryPreviewWidget(); - - - - void setTrackEntry(TrackEntry *te); - - void setTopicEnabled(bool enabled); - -private: - Ui::TrackEntryPreviewWidget *m_ui; - - void contextMenuEvent(QContextMenuEvent *event); - -signals: - void signalStartDrag(); - void signalStopDrag(); - void signalDelete(); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp b/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp deleted file mode 100755 index c2c67489a..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "track_entry_previewer.h" -#include "track_entry.h" -#include "track_entry_painters.h" -#include "track_content_widget.h" - -#include -#include -#include -#include - - -namespace LtsScriptTimeline -{ - -QDataStream &operator<<(QDataStream &ds, const TrackEntry &te); -QDataStream &operator>>(QDataStream &ds, TrackEntry &te); - -TrackEntryPreviewer::TrackEntryPreviewer(QWidget *parent) - : QWidget(parent) - , m_te(nullptr) -{ - - - setVisible(false); -} - - -TrackEntryPreviewer::~TrackEntryPreviewer() -{} - - -void TrackEntryPreviewer::setTrackEntry(TrackEntry *te) -{ - m_te = te; - - if (m_te->isSet) - return; - - connect(m_te, &TrackEntry::destroyed, - this, [this]() - { - m_te = nullptr; - emit signalDelete(); - }); -} - - -void TrackEntryPreviewer::mousePressEvent(QMouseEvent *event) -{ - - if (!m_te || event->buttons() != Qt::LeftButton) - return; - - m_te->isSet = true; - QByteArray itemData(TrackContentWidget::itemsToData({ m_te }, 0)); - m_te->isSet = false; - - QMimeData *mimeData = new QMimeData; - mimeData->setData(TrackContentWidget::mime, itemData); - - QDrag *drag = new QDrag(this); - drag->setDragCursor(QPixmap(), Qt::CopyAction); - drag->setMimeData(mimeData); - - emit signalStartDrag(); - - if (drag->exec(Qt::CopyAction) == Qt::CopyAction) - emit signalDelete(); - - emit signalStopDrag(); -} - - -void TrackEntryPreviewer::paintEvent(QPaintEvent *event) -{ - QWidget::paintEvent(event); - - QPainter painter(this); - - if (!m_te) - return; - - drawTrackEntry(m_te, painter, rect().adjusted(0, 0, -1, -1), false, isEnabled()); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_entry_previewer.h b/Lts/Common/LtsScriptTimeline/track_entry_previewer.h deleted file mode 100755 index 79261e498..000000000 --- a/Lts/Common/LtsScriptTimeline/track_entry_previewer.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - - -namespace LtsScriptTimeline -{ -class TrackEntry; - - -class TrackEntryPreviewer : public QWidget -{ - Q_OBJECT - -public: - explicit TrackEntryPreviewer(QWidget *parent = nullptr); - ~TrackEntryPreviewer(); - - - - void setTrackEntry(TrackEntry *te); - TrackEntry *trackEntry() const { return m_te; } - -protected: - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - -private: - TrackEntry *m_te; - -signals: - void signalDelete(); - void signalStartDrag(); - void signalStopDrag(); - -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_header.cpp b/Lts/Common/LtsScriptTimeline/track_header.cpp deleted file mode 100755 index cf24ea392..000000000 --- a/Lts/Common/LtsScriptTimeline/track_header.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "track_header.h" -#include "tools.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsScriptTimeline -{ - -TrackHeader::TrackHeader(QWidget *parent) - : QWidget(parent) - , m_indicatorTime(-1) - , m_textMargin(12) - , m_textSpacing(10) - , m_indicatorLock(false) - , m_tools(nullptr) -{ - setCursor(Qt::IBeamCursor); - - m_timeFormat = QStringLiteral("HH:mm:ss"); -} - - -void TrackHeader::setTools(Tools *tools) -{ - m_tools = tools; -} - - -void TrackHeader::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() != Qt::LeftButton) - return; - if (!m_indicatorLock) - { - setIndicatorTime(m_tools->getTime(event->localPos().x())); - emit signalHeaderIndicatorTimeUserChanged(m_indicatorTime); - } -} - - -void TrackHeader::setIndicatorTime(int time) -{ - m_indicatorTime = time; - emit signalHeaderIndicatorTimeChanged(m_indicatorTime); - update(); -} - - -void TrackHeader::recreatePositionLabels() -{ - - QTime t; - t.setHMS(23, 59, 59); - - QFontMetrics fm(QApplication::font()); - -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - int currentTextWidth = fm.width(t.toString()); -#else - int currentTextWidth = fm.horizontalAdvance(t.toString()); -#endif - - if (currentTextWidth < 1 ) - { - qCritical() << "TrackHeader::recreatePositionLabels: invalid text size"; - return; - } - - - - int currentTextWidthTime = m_tools->getDuration(currentTextWidth); - - int time = m_tools->getDuration(m_textMargin); - int end = (m_tools->m_timelineLength * 2) + currentTextWidthTime; - - int paintStartTime = m_tools->getTime(0) - currentTextWidthTime; - int paintEndTime = m_tools->getTime(width() * 2); - int count = 0; - - m_positionMarkers.clear(); - - - - int textSpacingTime = m_tools->getDuration(m_textSpacing); - while (time < end && count < 5000) - { - - - if (time >= paintStartTime && time <= paintEndTime) - { - - m_positionMarkers.append(m_tools->getPos(time)); - } - - time += currentTextWidthTime + textSpacingTime; - count++; - } - - - - update(); -} - - -void TrackHeader::resizeEvent(QResizeEvent *event) -{ - recreatePositionLabels(); - - QWidget::resizeEvent(event); -} - - -void TrackHeader::updateMe() -{ - recreatePositionLabels(); - update(); -} - - -void TrackHeader::paintEvent(QPaintEvent *event) -{ - QPainter painter(this); - - QWidget::paintEvent(event); - - painter.save(); - - - - painter.setPen(QGuiApplication::palette().windowText().color()); - painter.setBrush(QGuiApplication::palette().window().color()); - - painter.drawRect(rect().adjusted(0, 0, -1, -1)); - - int h = height(); - - - if (m_indicatorTime > -1 && m_indicatorTime <= m_tools->getTime(width())) - { - painter.setPen(QColor("cyan")); - int indicatorPos = m_tools->getPos(m_indicatorTime); - painter.drawLine(indicatorPos, h - (h / 4), indicatorPos, h); - } - - - - painter.setPen(QGuiApplication::palette().windowText().color()); - - - - for (auto i = m_positionMarkers.constBegin(); i != m_positionMarkers.constEnd(); ++i) - { - int pos = *i; - - - int time = m_tools->getTime(pos); - - - QTime t = QTime::fromMSecsSinceStartOfDay(time * 1000); - - - QString x = t.toString(m_timeFormat); - - - painter.drawText(pos, h / 2 , x); - - painter.save(); - painter.setPen(QGuiApplication::palette().link().color()); - painter.drawLine(pos, (h / 2) + 2, pos, h); - painter.restore(); - - - } - - - painter.restore(); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_header.h b/Lts/Common/LtsScriptTimeline/track_header.h deleted file mode 100755 index 4d85bc7c2..000000000 --- a/Lts/Common/LtsScriptTimeline/track_header.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - - -namespace LtsScriptTimeline -{ -class Tools; - -class TrackHeader : public QWidget -{ - Q_OBJECT - -public: - explicit TrackHeader(QWidget *parent = nullptr); - - - void recreatePositionLabels(); - void updateMe(); - void setIndicatorTime(int time); - void lockIndicator(bool lock = true) { m_indicatorLock = lock; } - void setTools(Tools *tools); - int indicatorTime() const { return m_indicatorTime; } - -private: - void resizeEvent(QResizeEvent *event) override; - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - - int m_indicatorTime; - int m_textMargin; - int m_textSpacing; - QString m_timeFormat; - QVector m_positionMarkers; - bool m_indicatorLock; - Tools *m_tools; - -signals: - void signalHeaderIndicatorTimeChanged(int time); - void signalHeaderIndicatorTimeUserChanged(qint64 time); -}; - -} diff --git a/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp b/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp deleted file mode 100755 index a4d2b2c3e..000000000 --- a/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "track_resize_handler.h" -#include "track_entry.h" -#include "track_widget.h" -#include "track_content_widget.h" -#include "filter_track_widget.h" -#include "filter_background_widget.h" - -#include "tools.h" - -#include - -#include -#include - - -namespace LtsScriptTimeline -{ -void ResizeHandler::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() != Qt::LeftButton || !m_possible) - return; - - TrackEntry *te = m_trackContentWidget->getTrackEntryOnPos(event->globalPos()); - - if (te == nullptr) - return; - - m_oldLength = te->m_length; - m_resizing = true; -} - - -void ResizeHandler::mouseReleaseEvent(QMouseEvent *) -{ - m_resizing = false; - - if (!m_trackContentWidget->m_mainSelected) - return; - - QVector vecPreviewTrackEntries = {m_trackContentWidget->m_mainSelected}; - - bool msg = true; - for (TrackContentWidget *tcw : qAsConst(m_tcwCorresponding)) - { - if (TrackContentWidget::checkForParallelTopics(tcw, vecPreviewTrackEntries, m_trackContentWidget->m_mainSelected->m_startTime)) - { - if (msg) - { - LtsWidgets::LtsMessageDialog::showMessage(m_trackContentWidget, TrackContentWidget::parallelError(), QObject::tr("Error")); - msg = false; - } - m_trackContentWidget->m_mainSelected->m_length = m_oldLength; - } - } -} - - -bool ResizeHandler::isCollision(int newLength) const -{ - int startTime = m_trackContentWidget->m_mainSelected->m_startTime; - int endTime = startTime + newLength; - - - for (auto i = m_trackContentWidget->m_entryList.constBegin(); i != m_trackContentWidget->m_entryList.constEnd(); ++i) - { - const TrackEntry *trackEntry = *i; - - if (!trackEntry || trackEntry == m_trackContentWidget->m_mainSelected) - continue; - - if (LtsScriptTimeline::isCollision2(startTime, endTime, trackEntry)) - return true; - } - - - const QVector &filterTrackWidgets = m_trackContentWidget->trackWidget()->filterLines(); - for (const FilterTrackWidget *filterTrackWidget : qAsConst(filterTrackWidgets)) - { - for (auto iFilterContentWidget = filterTrackWidget->fbw()->filterContentWidgets().begin(); iFilterContentWidget != filterTrackWidget->fbw()->filterContentWidgets().end(); ++iFilterContentWidget) - { - int filterContentWidgetTopicId = iFilterContentWidget.key(); - if (m_trackContentWidget->m_mainSelected->m_topicId != filterContentWidgetTopicId) - continue; - - TrackContentWidget *filterContentWidget = iFilterContentWidget.value(); - for (auto iTrackEntry = filterContentWidget->m_entryList.constBegin(); iTrackEntry != filterContentWidget->m_entryList.end(); ++iTrackEntry) - { - const TrackEntry *trackEntry = *iTrackEntry; - - if (!trackEntry || trackEntry == m_trackContentWidget->m_mainSelected) - continue; - - if (endTime < startTime + trackEntry->m_startTime + trackEntry->m_length) - return true; - } - } - } - - return false; -} - - -bool ResizeHandler::mouseMoveEvent(QMouseEvent *event) -{ - QPoint pos = m_trackContentWidget->mapFromGlobal(event->globalPos()); - int posX = pos.x(); - int cursorTime = m_trackContentWidget->m_tools->getTime(posX, m_trackContentWidget->m_isFilter); - - if (m_resizing && m_trackContentWidget->m_mainSelected) - { - int newLength = cursorTime - m_trackContentWidget->m_mainSelected->m_startTime; - int cursorY = pos.y(); - - if (cursorY < 0 || cursorY > m_trackContentWidget->height() || newLength < 1 || (m_mainTeLength != -1 && newLength > m_mainTeLength) || isCollision(newLength)) - { - newLength = m_oldLength; - m_trackContentWidget->setCursor(Qt::ForbiddenCursor); - } - else - { - m_trackContentWidget->setCursor(m_cursor); - } - - m_trackContentWidget->m_mainSelected->m_length = newLength; - m_trackContentWidget->update(); - emit m_trackContentWidget->signalChangesMade(); - return true; - } - else - { - TrackEntry *te = m_trackContentWidget->getTrackEntryOnPos(event->globalPos()); - - if (m_trackContentWidget->m_isFilter) - { - TrackEntry *mainTe = m_trackContentWidget->getParentTopicOfFilter(te); - - if (mainTe != nullptr) - m_mainTeLength = mainTe->m_length; - else - m_mainTeLength = -1; - } - - - if (te && (te->m_type == LtsScriptTimeline::TrackEntryType::Audio || - te->m_type == LtsScriptTimeline::TrackEntryType::Video)) - { - m_possible = false; - m_trackContentWidget->setCursor(m_cursor = Qt::ArrowCursor); - m_trackContentWidget->update(); - } - else if (te && m_trackContentWidget->m_mainSelected && te == m_trackContentWidget->m_mainSelected && m_trackContentWidget->m_selected.count() == 1 - && (cursorTime >= (m_trackContentWidget->m_mainSelected->m_startTime + m_trackContentWidget->m_mainSelected->m_length - 4) - && cursorTime <= (m_trackContentWidget->m_mainSelected->m_startTime + m_trackContentWidget->m_mainSelected->m_length))) - { - m_possible = true; - m_trackContentWidget->setCursor(m_cursor = Qt::SizeHorCursor); - m_trackContentWidget->update(); - } - else if (!m_resizing) - { - m_possible = false; - m_trackContentWidget->setCursor(m_cursor = Qt::ArrowCursor); - m_trackContentWidget->update(); - } - } - - return false; -} - - -void ResizeHandler::keyPressEvent(QKeyEvent *event) -{ - if (m_trackContentWidget->m_mainSelected && event->key() == Qt::Key_Escape && m_resizing) - { - m_trackContentWidget->m_mainSelected->m_length = m_oldLength; - m_resizing = false; - m_possible = false; - m_trackContentWidget->setCursor(Qt::ArrowCursor); - m_trackContentWidget->update(); - } -} -} diff --git a/Lts/Common/LtsScriptTimeline/track_resize_handler.h b/Lts/Common/LtsScriptTimeline/track_resize_handler.h deleted file mode 100755 index c8133dcd2..000000000 --- a/Lts/Common/LtsScriptTimeline/track_resize_handler.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -class QMouseEvent; -class QKeyEvent; - -namespace LtsScriptTimeline -{ -class LTSSCRIPTTIMELINE_EXPORT ResizeHandler -{ -public: - explicit ResizeHandler(TrackContentWidget *_tcw) - : m_trackContentWidget(_tcw) - , m_resizing(false) - , m_possible(false) - , m_oldLength(-1) - , m_mainTeLength(-1) - {} - - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - bool mouseMoveEvent(QMouseEvent *event); - void keyPressEvent(QKeyEvent *event); - - TrackContentWidget *m_trackContentWidget; - QVector m_tcwCorresponding; - - bool m_resizing; - bool m_possible; - int m_oldLength; - int m_mainTeLength; - Qt::CursorShape m_cursor; - -private: - bool isCollision(int newLength) const; -}; -} diff --git a/Lts/Common/LtsScriptTimeline/track_select_handler.cpp b/Lts/Common/LtsScriptTimeline/track_select_handler.cpp deleted file mode 100755 index cfa5aaa2d..000000000 --- a/Lts/Common/LtsScriptTimeline/track_select_handler.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "track_select_handler.h" -#include "track_content_widget.h" -#include "track_entry.h" - -#include "tools.h" - -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsScriptTimeline -{ - -void SelectHandler::slotSetDescription() -{ - if (m_pressedEntry == nullptr) - return; - - bool ok; - QString iniDescription = m_pressedEntry->m_description; - QString text = QInputDialog::getText(tcw, - tr("Operator Application"), - tr("Topic Description:"), QLineEdit::Normal, - iniDescription, &ok, Qt::Popup | Qt::WindowTitleHint | Qt::CustomizeWindowHint); - if (ok) - { - m_pressedEntry->m_description = text; - emit tcw->signalChangesMade(); - } -} - - -void SelectHandler::mouseDoubleClick(QMouseEvent *event) -{ - if (m_pressedEntry != nullptr - && tcw->m_selected.count() == 1 - && !tcw->m_isMultiLineMultiSelectionMode - && event->buttons() == Qt::LeftButton) - emit tcw->signalEditEntry(m_pressedEntry->m_topicId); -} - - -void SelectHandler::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() == Qt::LeftButton) - { - m_pressedEntry = nullptr; - - QPoint clickedPos = tcw->mapFromGlobal(event->globalPos()); - int clickedPosY = clickedPos.y(); - int clickedPosX = clickedPos.x(); - - if (clickedPosY < 0 || clickedPosY > tcw->height()) - return; - - int clickedTime = tcw->m_tools->getTime(clickedPosX, tcw->m_isFilter); - m_pressedEntry = tcw->getTrackEntryAt(clickedTime); - } - else if (event->buttons() == Qt::RightButton) - { - QMenu *contextMenu = new QMenu(tcw); - contextMenu->setAttribute(Qt::WA_DeleteOnClose); - - - contextMenu->addSection(tr("All tracks")); - { - QAction *action = new QAction(tr("Set topic description"), tcw); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:document-edit.svg"))); - action->setEnabled(m_pressedEntry); - if (!m_pressedEntry) - action->setToolTip(tr("Right click directly on a single Topic to edit its description.")); - connect(action, &QAction::triggered, this, &SelectHandler::slotSetDescription); - contextMenu->addAction(action); - } - - { - QAction *action = new QAction(tr("Remove selected Topic(s)"), tcw); - action->setShortcut(QKeySequence(Qt::Key_Delete)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:delete.svg"))); - connect(action, &QAction::triggered, tcw, &TrackContentWidget::signalDeleteSelectedTopics); - contextMenu->addAction(action); - } - - - contextMenu->addSection(tr("Main track only")); - - { - QAction *action = new QAction(tr("Select all topics"), tcw); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:selection_rec.svg"))); - connect(action, &QAction::triggered, tcw, &TrackContentWidget::signalSelectAllGlobal); - contextMenu->addAction(action); - } - - { - - QAction *action = new QAction(tr("Cut selected topic(s)"), tcw); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:edit-cut.svg"))); - connect(action, &QAction::triggered, tcw, &TrackContentWidget::signalCutTopics); - contextMenu->addAction(action); - } - - { - QAction *action = new QAction(tr("Copy selected topic(s)"), tcw); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:edit-copy.svg"))); - connect(action, &QAction::triggered, tcw, &TrackContentWidget::signalCopyTopics); - contextMenu->addAction(action); - } - - { - QAction *action = new QAction(tr("Paste topic(s) at indicator position"), tcw); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - action->setShortcutVisibleInContextMenu(true); -#endif - action->setIcon(QIcon(QStringLiteral("icon:edit-paste.svg"))); - connect(action, &QAction::triggered, tcw, &TrackContentWidget::signalPasteTopics); - contextMenu->addAction(action); - } - - - contextMenu->popup(event->globalPos()); - } -} - - - -void SelectHandler::mouseReleaseEvent(QMouseEvent *event) -{ - QPoint clickedPos = tcw->mapFromGlobal(event->globalPos()); - int clickedPosX = clickedPos.x(); - int clickedTime = tcw->m_tools->getTime(clickedPosX, tcw->m_isFilter); - - TrackEntry *releasedEntry = tcw->getTrackEntryAt(clickedTime); - - if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && (!m_pressedEntry || !releasedEntry)) - { - emit tcw->signalUnselectAll(); - emit tcw->signalTrackEntrySelectionChanged(nullptr); - } - else - { - if (m_pressedEntry == releasedEntry) - tcw->selectEntry(releasedEntry); - } -} - - -void SelectHandler::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) - { - m_pressedEntry = nullptr; - emit tcw->signalUnselectAll(); - emit tcw->signalTrackEntrySelectionChanged(nullptr); - } -} -} diff --git a/Lts/Common/LtsScriptTimeline/track_select_handler.h b/Lts/Common/LtsScriptTimeline/track_select_handler.h deleted file mode 100755 index 044ee4cb7..000000000 --- a/Lts/Common/LtsScriptTimeline/track_select_handler.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include - - -class QMouseEvent; -class QKeyEvent; - - -namespace LtsScriptTimeline -{ -class TrackContentWidget; -class TrackEntry; - -class LTSSCRIPTTIMELINE_EXPORT SelectHandler : public QObject -{ - Q_OBJECT - -public: - explicit SelectHandler(TrackContentWidget *_tcw) - : QObject(nullptr) - , tcw(_tcw) - , m_pressedEntry(nullptr) - {} - - void mouseDoubleClick(QMouseEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void keyPressEvent(QKeyEvent *event); - - TrackContentWidget *tcw; - TrackEntry *m_pressedEntry; - -public slots: - void slotSetDescription(); -}; -} diff --git a/Lts/Common/LtsScriptTimeline/track_widget.cpp b/Lts/Common/LtsScriptTimeline/track_widget.cpp deleted file mode 100755 index 015709bf8..000000000 --- a/Lts/Common/LtsScriptTimeline/track_widget.cpp +++ /dev/null @@ -1,559 +0,0 @@ -#include "track_widget.h" -#include "ui_track_widget.h" -#include "track_entry.h" -#include "filter_track_widget.h" -#include "filter_background_widget.h" -#include "track_content_widget.h" -#include "tools.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsScriptTimeline -{ -TrackWidget::TrackWidget(int baseWidth, bool readonly, Tools *tools, bool filtersAllowed, QWidget *parent) - : QWidget(parent) - , ui(new Ui::TrackWidget) - , m_baseWidth(baseWidth) - , m_readonly(readonly) - , m_tools(tools) -{ - Q_ASSERT(m_tools != nullptr); - - ui->setupUi(this); - ui->mainTrack->setTrackWidget(this); - ui->mainTrack->setTools(tools); - - ui->chkSelect->hide(); - - ui->filtersWidget->hide(); - ui->leName->hide(); - ui->pbApply->hide(); - ui->pbCancel->hide(); - - ui->pbEditName->hide(); - - if (!filtersAllowed) - ui->pbExpand->hide(); - - ui->pbAddFilter->hide(); - - setBaseWidth(baseWidth); - setReadonly(readonly); - - connect(ui->mainTrack, &TrackContentWidget::signalUnselectAll, - this, &TrackWidget::signalUnselectAll); - connect(ui->mainTrack, &TrackContentWidget::signalTrackEntrySelectionChanged, - this, &TrackWidget::signalTrackEntrySelectionChanged); - connect(ui->mainTrack, &TrackContentWidget::signalChangesMade, - this, &TrackWidget::signalChangesMade); - connect(ui->mainTrack, &TrackContentWidget::signalChangesMade, - this, &TrackWidget::slotMainChangesMade); - connect(ui->mainTrack, &TrackContentWidget::signalTrackEntryAdded, - this, &TrackWidget::slotTrackEntryAdded); - connect(ui->mainTrack, &TrackContentWidget::signalTrackEntryDeleted, - this, &TrackWidget::slotTrackEntryDeleted); - connect(ui->mainTrack, &TrackContentWidget::signalEditEntry, - this, &TrackWidget::signalEditEntry); - - connect(ui->chkSelect, &QCheckBox::stateChanged, - this, &TrackWidget::signalSelectTrack); - connect(ui->pbExpand, &QPushButton::clicked, - this, &TrackWidget::slotExpand); - connect(ui->pbAddFilter, &QPushButton::clicked, - this, &TrackWidget::slotAddFilter); -} - - -TrackWidget::~TrackWidget() -{ - m_filterLines.clear(); - delete ui; -} - - -void TrackWidget::setReadonly(bool readonly) -{ - m_readonly = readonly; - ui->mainTrack->setReadonly(readonly); - - if (readonly) - ui->pbAddFilter->hide(); - else if (ui->filtersWidget->isVisible()) - ui->pbAddFilter->show(); - - for (auto i3 = m_filterLines.begin(); i3 != m_filterLines.end(); ++i3) - { - FilterTrackWidget *ftw = *i3; - ftw->setReadonly(readonly); - } -} - - -void TrackWidget::setScriptTimeline(ScriptTimeline *scriptTimeline) -{ - m_scriptTimeline = scriptTimeline; - ui->mainTrack->setScriptTimeline(m_scriptTimeline); -} - - -void TrackWidget::keyPressEvent(QKeyEvent *event) -{ - getTrackContentWidget()->keyPressEvent(event); -} - - -QVector TrackWidget::getSelectedFilterEntries() const -{ - QVector result; - const QVector &ftws = filterLines(); - - for (const FilterTrackWidget *ftw : qAsConst(ftws)) - { - const FilterBackgroundWidget *fbw = ftw->fbw(); - const QMap &fcws = fbw->filterContentWidgets(); - - for (auto i = fcws.constBegin(); i != fcws.constEnd(); ++i) - { - TrackContentWidget *tcw = i.value(); - result += tcw->getSelectedEntries(); - } - } - - return result; -} - - -const QVector &TrackWidget::getSelectedEntries() const -{ - return ui->mainTrack->getSelectedEntries(); -} - - -void TrackWidget::slotAddFilter() -{ - FilterTrackWidget *ftw = new FilterTrackWidget(m_baseWidth, m_readonly); - ftw->setTrackWidget(this); - ftw->setTools(m_tools); - ftw->setScriptTimeline(m_scriptTimeline); - ui->filtersWidget->layout()->addWidget(ftw); - m_filterLines.append(ftw); - - connect(ftw, &FilterTrackWidget::signalChangesMade, - this, &TrackWidget::signalChangesMade); - - connect(ftw, &FilterTrackWidget::signalUnselectAll, - this, &TrackWidget::signalUnselectAll); - - connect(ftw, &FilterTrackWidget::signalTrackEntrySelectionChanged, - this, &TrackWidget::signalTrackEntrySelectionChanged); - - connect(ftw, &FilterTrackWidget::signalEditEntry, - this, &TrackWidget::signalEditEntry); - - connect(ftw, &FilterTrackWidget::signalTrackEntryDeleted, - this, &TrackWidget::signalTrackEntryDeleted); - - connect(ftw, &FilterTrackWidget::signalDeleteMe, - this, [this]() - { - m_filterLines.removeAll(qobject_cast(sender())); - sender()->deleteLater(); - emit signalChangesMade(); - }); - - emit signalChangesMade(); - slotMainChangesMade(); -} - - -void TrackWidget::slotMainChangesMade() -{ - const QVector trackEntries = ui->mainTrack->getTrackEntries(); - - for (auto i1 = trackEntries.constBegin(); i1 != trackEntries.constEnd(); ++i1) - { - const TrackEntry *te = *i1; - - for (auto i3 = m_filterLines.begin(); i3 != m_filterLines.end(); ++i3) - { - FilterTrackWidget *ftw = *i3; - ftw->fbw()->adjustFilterContentWidgets(te); - } - } - - -} - - -void TrackWidget::slotTrackEntryAdded(TrackEntry *te) -{ - - - for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) - ftw->fbw()->trackEntryAdded(te); - - emit signalTrackEntryAdded(te); -} - - -void TrackWidget::slotTrackEntryDeleted(const int topicId) -{ - - - for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) - ftw->fbw()->trackEntryDeleted(topicId); - - emit signalTrackEntryDeleted(topicId); -} - - - - - - - - -void TrackWidget::slotExpand() -{ - if (ui->filtersWidget->isVisible()) - { - ui->pbExpand->setText(tr("More")); - ui->pbAddFilter->hide(); - ui->filtersWidget->hide(); - } - else - { - ui->pbExpand->setText(tr("Less")); - - if (!m_readonly) - ui->pbAddFilter->show(); - - ui->filtersWidget->show(); - slotMainChangesMade(); - } -} - - -void TrackWidget::selectEntry(int topicId) -{ - ui->mainTrack->selectEntry(topicId); -} - - -int TrackWidget::getParentTopicIdOfFilter(const TrackEntry *te) const -{ - - for (const FilterTrackWidget *ftw : m_filterLines) - { - const FilterBackgroundWidget *fbw = ftw->fbw(); - const QMap &filterContentWidgets = fbw->filterContentWidgets(); - - - for (auto i = filterContentWidgets.constBegin(); i != filterContentWidgets.constEnd(); ++i) - { - int parentTopicId = i.key(); - const TrackContentWidget *datWidget = i.value(); - const QVector &filterEntries = datWidget->getTrackEntries(); - - for (const TrackEntry *filterTE : filterEntries) - { - if (filterTE == te) - return parentTopicId; - } - } - } - - return -1; -} - - -QVector TrackWidget::getTrackEntriesWithFilters(bool selectedEntriesOnly) const -{ - QVector result; - const QVector trackEntryList = ui->mainTrack->getTrackEntries(); - - for (TrackEntry *te : trackEntryList) - { - if (selectedEntriesOnly && !ui->mainTrack->getSelectedEntries().contains(te)) - continue; - - result += te; - - - QJsonArray jsonFilterLines; - - - for (const FilterTrackWidget *ftw : qAsConst(m_filterLines)) - { - const FilterBackgroundWidget *fbw = ftw->fbw(); - const QMap &filterContentWidgets = fbw->filterContentWidgets(); - - - const TrackContentWidget *datWidget = filterContentWidgets.value(te->m_topicId); - - if (datWidget == nullptr) - continue; - - const QVector &filterEntries = datWidget->getTrackEntries(); - - if (filterEntries.isEmpty()) - continue; - - QJsonArray jsonFilterLine; - for (const TrackEntry *fte : filterEntries) - jsonFilterLine.append(fte->toJson()); - - jsonFilterLines.append(jsonFilterLine); - } - - te->m_detailData[QStringLiteral("Filters")] = jsonFilterLines; - } - - return result; -} - - -QVector TrackWidget::getTrackEntriesFiltersAsEntries(bool selectedEntriesOnly) const -{ - QVector result; - const QVector trackEntryList = ui->mainTrack->getTrackEntries(); - - for (TrackEntry *te : trackEntryList) - { - if (selectedEntriesOnly && !ui->mainTrack->getSelectedEntries().contains(te)) - continue; - - result += te; - - - for (FilterTrackWidget *ftw : m_filterLines) - { - const QMap &filterContentWidgets = ftw->fbw()->filterContentWidgets(); - - for (auto i1 = filterContentWidgets.constBegin(); i1 != filterContentWidgets.constEnd(); ++i1) - { - TrackContentWidget *tcw = i1.value(); - result += tcw->getTrackEntries(); - } - } - } - - return result; -} - - -QVector TrackWidget::getTrackEntries(bool selectedEntriesOnly) const -{ - QVector result; - const QVector trackEntryList = ui->mainTrack->getTrackEntries(); - - if (selectedEntriesOnly) - { - for (TrackEntry *te : trackEntryList) - { - if (!ui->mainTrack->getSelectedEntries().contains(te)) - continue; - - result += te; - } - } - else - { - return trackEntryList; - } - - return result; -} - - -void TrackWidget::setBaseWidth(int width) -{ - m_baseWidth = width; - - ui->mainLeft->setMinimumWidth(width); - ui->mainLeft->setMaximumWidth(width); - - for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) - ftw->setBaseWidth(m_baseWidth); -} - - -void TrackWidget::addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget) -{ - ui->mainTrack->addCorrespondingTrackWidget(pCorrespondingTrackWidget); -} - - -TrackContentWidget *TrackWidget::getTrackContentWidget() const -{ - return ui->mainTrack; -} - - -void TrackWidget::setInternalName(const QString &name) -{ - m_internalName = name; - ui->mainTrack->setTrackName(name); -} - - -void TrackWidget::setTrackName(const QString &name) -{ - ui->lblTrackName->setText(name); -} - - -bool TrackWidget::addTrackEntry(TrackEntry *te, bool selectTrackEntry) -{ - if (!ui->mainTrack->addTrackEntry(te, selectTrackEntry)) - return false; - - const QJsonObject detailData = te->m_detailData; - - - - - - - - if (detailData.isEmpty()) - { - - return true; - } - - - - - const QJsonArray filterLines = detailData[QStringLiteral("Filters")].toArray(); - - - if (filterLines.isEmpty()) - return true; - - - - - - - int missingFilterLines = filterLines.count() - m_filterLines.count(); - - while (missingFilterLines > 0) - { - slotAddFilter(); - missingFilterLines--; - } - - - - - for (int i1 = 0; i1 != filterLines.count(); i1++) - { - const QJsonArray filterLine = filterLines.at(i1).toArray(); - - for (const QJsonValue &filter : filterLine) - { - TrackEntry *filterTe = new TrackEntry; - filterTe->fromJson(filter.toObject()); - - - m_filterLines.at(i1)->addFilterEntry(te , filterTe); - - - } - } - - emit signalChangesMade(); - - return true; -} - - -QString TrackWidget::trackName() const -{ - return m_internalName; -} - - -bool TrackWidget::isSelected() const -{ - return (ui->chkSelect->checkState() == Qt::CheckState::Checked); -} - - -void TrackWidget::unselectAll() -{ - ui->mainTrack->unselectAll(); - - for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) - { - const auto fcw = ftw->fbw()->filterContentWidgets(); - for (TrackContentWidget *tcw : fcw) - tcw->unselectAll(); - } -} - - -void TrackWidget::updateMe() -{ - update(); - ui->mainTrack->update(); - slotMainChangesMade(); -} - - -void TrackWidget::setIndicatorTime(int time) -{ - ui->mainTrack->setIndicatorTime(time); - - for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) - { - const auto filterContentWidgets = ftw->fbw()->filterContentWidgets(); - - - for (auto i1 = filterContentWidgets.begin(); i1 != filterContentWidgets.end(); ++i1) - { - int mainTeId = i1.key(); - int mainTeStart = 0; - - const QVector &tes = ui->mainTrack->getTrackEntries(); - for (const TrackEntry *te : tes) - { - if (te->m_topicId == mainTeId) - { - mainTeStart = te->m_startTime; - break; - } - } - - TrackContentWidget *filterTcw = i1.value(); - filterTcw->setIndicatorTime(time - mainTeStart); - } - } -} - - -void TrackWidget::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - - painter.save(); - - painter.setPen(QGuiApplication::palette().windowText().color()); - painter.setBrush(QGuiApplication::palette().window().color()); - painter.drawRect(rect()); - - painter.restore(); -} - -} diff --git a/Lts/Common/LtsScriptTimeline/track_widget.h b/Lts/Common/LtsScriptTimeline/track_widget.h deleted file mode 100755 index 780725f2d..000000000 --- a/Lts/Common/LtsScriptTimeline/track_widget.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include "track_entry_painters.h" - -#include -#include -#include - - -namespace Ui -{ -class TrackWidget; -} - - -namespace LtsScriptTimeline -{ -class TrackEntry; -class TrackContentWidget; -class FilterTrackWidget; -class FilterBackgroundWidget; -class ScriptTimeline; -class Tools; - - -class LTSSCRIPTTIMELINE_EXPORT TrackWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TrackWidget(int baseWidth, bool readonly, Tools *tools, bool filtersAllowed, QWidget *parent = nullptr); - ~TrackWidget(); - - void setReadonly(bool readonly); - void updateMe(); - void unselectAll(); - bool isSelected() const; - QString trackName() const; - bool addTrackEntry(TrackEntry *te, bool selectTrackEntry); - void setTrackName(const QString &name); - void addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget); - TrackContentWidget *getTrackContentWidget() const; - void setBaseWidth(int width); - void setIndicatorTime(int time); - void selectEntry(int topicId); - - void setInternalName(const QString &name); - QVector getTrackEntriesFiltersAsEntries(bool selectedEntriesOnly = false) const; - QVector getTrackEntriesWithFilters(bool selectedEntriesOnly = false) const; - QVector getTrackEntries(bool selectedEntriesOnly = false) const; - QVector getSelectedFilterEntries() const; - const QVector &getSelectedEntries() const; - int getParentTopicIdOfFilter(const TrackEntry *te) const; - void setScriptTimeline(ScriptTimeline *scriptTimeline); - const QVector &filterLines() const { return m_filterLines; } - - void keyPressEvent(QKeyEvent *event) override; - -private: - QVector m_filterLines; - Ui::TrackWidget *ui; - int m_baseWidth; - QString m_internalName; - bool m_readonly; - ScriptTimeline *m_scriptTimeline; - Tools *m_tools; - -protected: - void paintEvent(QPaintEvent *event) override; - -private slots: - void slotExpand(); - void slotTrackEntryAdded(TrackEntry *te); - void slotTrackEntryDeleted(const int topicId); - void slotMainChangesMade(); - void slotAddFilter(); - -signals: - void signalUnselectAll(); - void signalSelectTrack(); - void signalTrackEntrySelectionChanged(TrackEntry *te); - void signalChangesMade(); - void signalEditEntry(const int topicId); - void signalTrackEntryDeleted(const int topicId); - void signalTrackEntryAdded(TrackEntry *te); -}; - -} diff --git a/Lts/Common/LtsSerialPortQuerry/CMakeLists.txt b/Lts/Common/LtsSerialPortQuerry/CMakeLists.txt deleted file mode 100755 index 05114df6a..000000000 --- a/Lts/Common/LtsSerialPortQuerry/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# -# Lts SerialPortQuerry library -# - -SET(ltsspq_SRCS - lts_serial_port_querry.h - lts_serial_port_querry.cpp - logger.h - logger.cpp -) - - -NGL_ADD_LIBRARY2(AAR SV LtsSerialPortQuerry SHARED ${ltsspq_SRCS} ) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSerialPortQuerry - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsSerialPortQuerry - PRIVATE - PUBLIC - NGLLogger - Qt5::SerialPort -) diff --git a/Lts/Common/LtsSerialPortQuerry/logger.cpp b/Lts/Common/LtsSerialPortQuerry/logger.cpp deleted file mode 100755 index c75d4f318..000000000 --- a/Lts/Common/LtsSerialPortQuerry/logger.cpp +++ /dev/null @@ -1,32 +0,0 @@ - - - -#include "logger.h" - -#include - -namespace LtsSerialPortQuerry -{ - -LOGGER_T getLogger(const char *name) -{ - static const char ns[] = "SPQ."; - std::string loggerName; - const size_t len = strlen(name); - loggerName.reserve(sizeof(ns) + len); - loggerName.append(ns, sizeof(ns) - 1); - loggerName.append(name, len); - - try - { - return NGL::NGLLoggerBootstrap::getNGLRootLogger(). - getNGLLogger(loggerName); - } - catch (const std::exception &ex) - { - fprintf(stderr, "Error retrieving logger: %s", ex.what()); - abort(); - } -} - -} diff --git a/Lts/Common/LtsSerialPortQuerry/logger.h b/Lts/Common/LtsSerialPortQuerry/logger.h deleted file mode 100755 index 75fc2cdc4..000000000 --- a/Lts/Common/LtsSerialPortQuerry/logger.h +++ /dev/null @@ -1,18 +0,0 @@ - - - -#pragma once - -#include - -namespace LtsSerialPortQuerry -{ -typedef NGL::INGLLogger &LOGGER_T; - -LOGGER_T getLogger(const char *name); -inline LOGGER_T getLogger(const QString &loggerName) -{ - return getLogger(loggerName.toUtf8().constData()); -} -} - diff --git a/Lts/Common/LtsSerialPortQuerry/lts_comport_querry.h b/Lts/Common/LtsSerialPortQuerry/lts_comport_querry.h deleted file mode 100755 index e6ecd3a50..000000000 --- a/Lts/Common/LtsSerialPortQuerry/lts_comport_querry.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "lts_serial_port_querry_export.h" -#include "lts_seri" -#include -#include -#include - - - - -namespace LtsSerialPortQuerry -{ -class LTSSERIALPORTQUERRY_EXPORT LtsSerialPortQuerry : public QObject -{ - Q_OBJECT -public: - explicit LtsSerialPortQuerry(QObject *parent = nullptr); - - void setSerialPortSettings(const QSerialPort::BaudRate &baudRate, - const QSerialPort::OpenModeFlag &openModeFlag, - const QSerialPort::DataBits &dataBits, - const QSerialPort::FlowControl &flowControll, - const QSerialPort::Parity &parity, - const QSerialPort::StopBits &stopBits); - void continueQuerry(); - void restartQuerry(); - QSerialPort *currentSerialPort(); - -signals: - void connectionEstablished(); - -private: - void querrySerialPort(const QSerialPortInfo &info); - void closeAndDeleteCurrentPort(); - -private: - void onSerialPortError(QSerialPort::SerialPortError serialPortError); - -private: - QList m_serialPortInfos; - int m_currentQuerry; - QSerialPort::BaudRate m_baudRate; - QSerialPort::OpenModeFlag m_openModeFlag; - QSerialPort::DataBits m_dataBits; - QSerialPort::FlowControl m_flowControll; - QSerialPort::Parity m_parity; - QSerialPort::StopBits m_stopBits; - QSerialPort *m_currentSerialPort; -}; - -} diff --git a/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.cpp b/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.cpp deleted file mode 100755 index daa9b81b4..000000000 --- a/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "lts_serial_port_querry.h" - -#include - -namespace LtsSerialPortQuerry -{ - -LtsSerialPortQuerry::LtsSerialPortQuerry(QObject *parent) - : QObject(parent) - , m_currentQuerry(0) - , m_baudRate(QSerialPort::Baud115200) - , m_openModeFlag(QSerialPort::ReadWrite) - , m_dataBits(QSerialPort::Data8) - , m_flowControll(QSerialPort::NoFlowControl) - , m_parity(QSerialPort::NoParity) - , m_stopBits(QSerialPort::OneStop) - , m_currentSerialPort(nullptr) - , m_logger(getLogger("LtsSerialPortQuerry")) - -{ - -} - -void LtsSerialPortQuerry::setSerialPortSettings(const QSerialPort::BaudRate &baudRate, - const QIODevice::OpenModeFlag &openModeFlag, - const QSerialPort::DataBits &dataBits, - const QSerialPort::FlowControl &flowControll, - const QSerialPort::Parity &parity, - const QSerialPort::StopBits &stopBits) -{ - m_baudRate = baudRate; - m_openModeFlag = openModeFlag; - m_dataBits = dataBits; - m_flowControll = flowControll; - m_parity = parity; - m_stopBits = stopBits; -} - -void LtsSerialPortQuerry::continueQuerry() -{ - m_currentQuerry++; - if (m_currentQuerry>= m_serialPortInfos.size()) - { - - restartQuerry(); - return; - } - querrySerialPort(m_serialPortInfos.at(m_currentQuerry)); -} - -void LtsSerialPortQuerry::restartQuerry() -{ - DEBUG_LOG(m_logger) << "restart querry "; - m_serialPortInfos = QSerialPortInfo::availablePorts(); - m_currentQuerry = 0; - if (m_serialPortInfos.size()) - querrySerialPort(m_serialPortInfos.at(m_currentQuerry)); - else - QTimer::singleShot(1000, this, &LtsSerialPortQuerry::restartQuerry); -} - -QSerialPort *LtsSerialPortQuerry::currentSerialPort() -{ - return m_currentSerialPort; -} - -void LtsSerialPortQuerry::querrySerialPort(const QSerialPortInfo &info) -{ - closeAndDeleteCurrentPort(); - m_currentSerialPort = new QSerialPort(info, this); - m_currentSerialPort->setBaudRate(m_baudRate); - m_currentSerialPort->setDataBits(m_dataBits); - m_currentSerialPort->setParity(m_parity); - m_currentSerialPort->setStopBits(m_stopBits); - m_currentSerialPort->setFlowControl(m_flowControll); - - DEBUG_LOG(m_logger) << "Try opening Comport:" << m_currentSerialPort->portName() << " index is: " << m_currentQuerry; - - - if (!m_currentSerialPort->open(QSerialPort::ReadWrite)) - { - - DEBUG_LOG(m_logger) << "Oppening failed query next"; - - continueQuerry(); - }else - { - connect(m_currentSerialPort, &QSerialPort::errorOccurred, - this, &LtsSerialPortQuerry::onSerialPortError, Qt::UniqueConnection); - DEBUG_LOG(m_logger) << "Oppened comport" << m_currentSerialPort->portName(); - emit connectionEstablished(); - } -} - -void LtsSerialPortQuerry::closeAndDeleteCurrentPort() -{ - if (m_currentSerialPort) - { - m_currentSerialPort->disconnect(); - if (m_currentSerialPort->isOpen()) - m_currentSerialPort->close(); - m_currentSerialPort->deleteLater(); - - m_currentSerialPort = nullptr; - } -} - - -void LtsSerialPortQuerry::onSerialPortError(QSerialPort::SerialPortError serialPortError) -{ - - if (!m_currentSerialPort) - return; - DEBUG_LOG(m_logger) << "Spontaneous error " << serialPortError << " doesn't have to be an error ..."; - - const QString errStr(QStringLiteral("QSerialPort::SerialPortError ") + QString::number(serialPortError) + - QStringLiteral(" from serial port ") + m_currentSerialPort->portName() + - QStringLiteral(" error: ") + m_currentSerialPort->errorString()); - - switch (serialPortError) - { - case QSerialPort::SerialPortError::NoError: - { - - break; - } - case QSerialPort::SerialPortError::DeviceNotFoundError: - case QSerialPort::SerialPortError::PermissionError: - case QSerialPort::SerialPortError::OpenError: - case QSerialPort::SerialPortError::NotOpenError: - case QSerialPort::SerialPortError::UnknownError: - { - ERROR_LOG(m_logger) << "Closing port and try to reopen it. " << m_currentSerialPort->portName(); - continueQuerry(); - - Q_FALLTHROUGH(); - } - case QSerialPort::SerialPortError::UnsupportedOperationError: - { - ERROR_LOG(m_logger) << errStr; - break; - } - default: - { - ERROR_LOG(m_logger) << "Got an Error> " << errStr << "restarting"; - continueQuerry(); - - break; - } - } - -} -} diff --git a/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.h b/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.h deleted file mode 100755 index c8e4965d5..000000000 --- a/Lts/Common/LtsSerialPortQuerry/lts_serial_port_querry.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include "logger.h" - -#include -#include -#include - -namespace LtsSerialPortQuerry -{ -class LTSSERIALPORTQUERRY_EXPORT LtsSerialPortQuerry : public QObject -{ - Q_OBJECT -public: - explicit LtsSerialPortQuerry(QObject *parent = nullptr); - - void setSerialPortSettings(const QSerialPort::BaudRate &baudRate, - const QSerialPort::OpenModeFlag &openModeFlag, - const QSerialPort::DataBits &dataBits, - const QSerialPort::FlowControl &flowControll, - const QSerialPort::Parity &parity, - const QSerialPort::StopBits &stopBits); - - void restartQuerry(); - QSerialPort *currentSerialPort(); - -public slots: - void continueQuerry(); - -signals: - void connectionEstablished(); - -private: - void querrySerialPort(const QSerialPortInfo &info); - void closeAndDeleteCurrentPort(); - -private: - void onSerialPortError(QSerialPort::SerialPortError serialPortError); - -private: - QList m_serialPortInfos; - int m_currentQuerry; - QSerialPort::BaudRate m_baudRate; - QSerialPort::OpenModeFlag m_openModeFlag; - QSerialPort::DataBits m_dataBits; - QSerialPort::FlowControl m_flowControll; - QSerialPort::Parity m_parity; - QSerialPort::StopBits m_stopBits; - QSerialPort *m_currentSerialPort; - - LOGGER_T m_logger; -}; - -} diff --git a/Lts/Common/LtsSession/CMakeLists.txt b/Lts/Common/LtsSession/CMakeLists.txt deleted file mode 100755 index 0282087b2..000000000 --- a/Lts/Common/LtsSession/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-05-15 -# - - -# LtsSessionDataDds -SET(LtsSessionDataDds_SRCS - lts_session_data_dds.cpp - lts_session_data_dds.h -) -NGL_ADD_LIBRARY2(WS LtsSessionDataDds SHARED ${LtsSessionDataDds_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSessionDataDds - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsSessionDataDds - PRIVATE - ddsconnector - PUBLIC - ddscore - NGLLogger - Qt5::Core -) - - - -# LtsSession -SET(ltssession_SRCS - lts_session_manager.h - lts_session_manager.cpp - lts_session.h - lts_session.cpp - lts_session_lock_handler.h - lts_session_lock_handler.cpp -) - -NGL_ADD_LIBRARY2(WS LtsSession SHARED ${ltssession_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSession - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsSession - PRIVATE - Qt5::Network - LtsMultiMonitor - LtsSessionData - ddsconnector - LtsUtilsGuiDds # ReqHelpr & for Debug Menu ! TODO: rm on release! - LtsNotification - svcCoreClient # Core Ftc - for TLH - OhpOnlineHelpViewerGuiPluginInterface - Qt5::Svg - PUBLIC - LtsWidgets - LtsSessionDataDds - Qt5::Core - GuiPluginInterface - DataPluginInterface - NGLLogger - ProjectConfig -) - - -# LtsSessionData (avoid cross-deps between AppManager, Session and PluginManager) -SET(LtsSessionData_SRCS - lts_session_data.cpp - lts_session_data.h -) - -NGL_ADD_LIBRARY2(WS TOOLSSV TOOLSWS LtsSessionData SHARED ${LtsSessionData_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSessionData - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsSessionData - PUBLIC - Qt5::Core - Qt5::Gui # QPixmap as Storage for Profile Picture - ddscore -) diff --git a/Lts/Common/LtsSession/lts_session.cpp b/Lts/Common/LtsSession/lts_session.cpp deleted file mode 100755 index 023b20a85..000000000 --- a/Lts/Common/LtsSession/lts_session.cpp +++ /dev/null @@ -1,1649 +0,0 @@ - - - -#include "lts_session.h" -#include "lts_session_manager.h" -#include "lts_session_data_dds.h" -#include "lts_session_lock_handler.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 -#include - - - -#include - - - - -using namespace LtsWidgets; - -namespace LtsSession -{ -bool LtsSession::s_valgrind = false; -int LtsSession::s_nextSessionId = 0; - - -LtsSession::LtsSession(LtsSessionManager *sessionManager) - : m_sessionManager(sessionManager) - , m_mainBackground(nullptr) - , m_hasBaseLayout(false) - , m_baseLayout(nullptr) - , m_markedForDeletion(false) - , m_payloads(false) - , m_initDone(false) - , m_menuVisible(true) - , m_firstActionsFetch(true) - , m_exerciseData(new LtsWidgets::ExerciseData) - , m_currentInitIfc(nullptr) - , m_slotPluginInitDoneMutex(false) - , m_pluginInitDone(false) - , m_hidden(false) - , m_loadedFromSavegame(false) - , m_foreverInvisible(false) - , m_id(s_nextSessionId++) -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); - - connect(this, &LtsSession::signalDoneInitializingSession, - this, &LtsSession::slotStopWaitDialog); - connect(this, &LtsSession::signalInitializationAborted, - this, &LtsSession::slotStopWaitDialog); - - - - connect(this, &LtsSession::_signalDoneInitializingSession, - this, [this]() - { - - - disconnect(this, &LtsSession::_signalDoneInitializingSession, - this, nullptr); - - m_initDone = true; - emit signalDoneInitializingSession(); - }); - - - QTimer *t = new QTimer(this); - t->setInterval(1000); - connect(t, &QTimer::timeout, - this, &LtsSession::signalStillAlive); - t->start(); - m_scriptEditorTimer = new QTimer(this); - - if ( strcmp(PROJECTBUILD, "qatarmout") == 0 - || strcmp(PROJECTBUILD, "qatar") == 0) - { - DEBUG_LOG(m_logger) << "qatarmout Timer = 4000"; - m_scriptEditorTimer->setInterval(4000); - } - else - { - DEBUG_LOG(m_logger) << "qatarmout Timer = 0"; - m_scriptEditorTimer->setInterval(0); - } - - m_scriptEditorTimer->setSingleShot(true); - connect(m_scriptEditorTimer, &QTimer::timeout, this, &LtsSession::delayScriptEditorStart); -} - - -LtsSession::~LtsSession() -{ - - - - delete m_baseLayout; - - - - - - clearGuiPluginInterfaces(); - clearDataPluginInterfaces(); - - delete m_exerciseData; -} - - -QString LtsSession::toString() const -{ - std::ostringstream stream; - stream << "LtsSession:" - << " this=" << this - << ", domainId=" << this->domainId() - << ", partitionId=" << this->partitionId() - << ", partitionType=" << this->partitionType() - << ", exerciseId='" << this->exerciseId() << "'" - << ", workspaceId='" << this->workspaceId() << "'" - << ", appToString='" << this->appToString(this->app()) << "'" - << ", LtsSessionDataDds=" << LtsSessionDataDds::toString(); - return QString::fromStdString(stream.str()); - - - - - - - - - -} - - -QWidget *LtsSession::baseLayoutWidget() -{ - - - return static_cast(m_baseLayout); -} - - -void LtsSession::aboutToCloseTab(LayoutArea area) -{ - m_baseLayout->getTabFromLayoutArea(area)->aboutToClose(); -} - - -void LtsSession::setDependency(LtsSession *session) -{ - if (session == this) - { - Q_ASSERT(session != this); - ERROR_LOG(m_logger) << "Tried to set session dependency on self"; - return; - } - - m_dependency = session; -} - - -void LtsSession::aboutToClose() -{ - emit sessionAboutToBeDestroyed(this); - - if (m_baseLayout == nullptr) - return; - - aboutToCloseTab(LayoutArea::eDialogTab); - aboutToCloseTab(LayoutArea::eInfoTab); - aboutToCloseTab(LayoutArea::eSpecialTab); - aboutToCloseTab(LayoutArea::eMainTab); -} - - -void LtsSession::slotStopWaitDialog() -{ - disconnect(this, &LtsSession::signalDoneInitializingSession, - this, &LtsSession::slotStopWaitDialog); - disconnect(this, &LtsSession::signalInitializationAborted, - this, &LtsSession::slotStopWaitDialog); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); -} - - -QString LtsSession::appToString(LtsAppDescriptor::App app) -{ - using namespace LtsAppDescriptor; - switch (app) - { - case App::Situation: - return tr("Situation"); - case App::ResourceManager: - return tr("Resource Manager"); - case App::PlanEditor: - return tr("Plan Editor"); - case App::BuildingViewer: - return tr("Building Viewer"); - case App::OC: - return tr("O/C"); - case App::OrgChart: - return tr("Organisation Chart"); - case App::Scripts: - return tr("Scripts"); - case App::Snapshot: - return tr("Snapshot"); - case App::SystemMonitor: - return tr("System Monitor"); - case App::Timeline: - return tr("Timeline"); - case App::SymbolEditor: - return tr("Symbol Editor"); - case App::ScriptEditorEssentialsView: - return tr("Scripts Essentials View"); - case App::BaseDownloader: - return tr("Base Downloader"); - case App::OnlineHelp: - return tr("Online Help"); - case App::ThpSituationReplay: - return tr("THP Situation Replay"); - case App::SituationTablet: - return tr("Situation (Tablet)"); - case App::SilhouetteEditor: - return tr("Silhouette Editor"); - case App::Shootback: - return tr("Shootback"); - case App::Login: - return tr("Login"); - break; - case App::SAAs: - return tr("SAAs"); - break; - } - - return tr("Unknown"); -} - -QString LtsSession::appIconFilename(LtsAppDescriptor::App app) -{ - using namespace LtsAppDescriptor; - switch (app) - { - case App::Situation: - return QStringLiteral("icon:situation.svg"); - case App::ResourceManager: - return QStringLiteral("icon:player_equipment.svg"); - case App::PlanEditor: - return QStringLiteral("icon:plan_editor.svg"); - case App::BuildingViewer: - return QStringLiteral("icon:3d_viewer.svg"); - case App::OC: - return QStringLiteral("icon:display_oc_evaluations.svg"); - case App::OrgChart: - return QStringLiteral("icon:org_chart.svg"); - case App::Scripts: - return QStringLiteral("icon:insert_script.svg"); - case App::Snapshot: - return QStringLiteral("icon:snapshots.svg"); - case App::SystemMonitor: - return QStringLiteral("icon:system_status.svg"); - case App::Timeline: - return QStringLiteral("icon:timeline.svg"); - case App::SymbolEditor: - return QStringLiteral("icon:define_tactical_symbols.svg"); - case App::ScriptEditorEssentialsView: - return QStringLiteral("icon:evaluation_elements.svg"); - case App::BaseDownloader: - return QStringLiteral("icon:download.svg"); - case App::OnlineHelp: - return QStringLiteral("icon:help.svg"); - case App::ThpSituationReplay: - return QStringLiteral("icon:restart.svg"); - case App::SituationTablet: - return QStringLiteral("icon:situation.svg"); - case App::SilhouetteEditor: - return QStringLiteral("icon:situation.svg"); - case App::Shootback: - return QStringLiteral("icon:target.svg"); - case App::SAAs: - return QStringLiteral("icon:saa_remote_desktop.svg"); - case App::Login: - break; - } - - return {}; -} - -QIcon LtsSession::appToIcon(LtsAppDescriptor::App app) -{ - return LtsUtils::iconFromSvg(appIconFilename(app)); -} - - -bool LtsSession::removeMenuAction(LtsWidgets::LtsDragIcon *icon) -{ - if (!m_baseLayout) - return false; - return m_baseLayout->removeMenuAction(icon); -} - - -void LtsSession::setMenuVisible(bool visible) -{ - m_menuVisible = visible; - - if (m_baseLayout == nullptr) - return; - - m_baseLayout->getMenu()->setVisible(visible); -} - -LtsDragIcon *LtsSession::addAction(const LtsWidgets::PluginData &plData, bool isFirstRun) -{ - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "No BaseLayout available atm, cannot add action!"; - return nullptr; - } - - return m_baseLayout->addMenuAction(plData, isFirstRun); -} - - -LtsNotification::LtsNotification *LtsSession::createNotification(const QString &identifier) -{ - LtsNotification::LtsNotificationController *c = new LtsNotification::LtsNotificationController(identifier); - LtsNotification::LtsNotification *n = new LtsNotification::LtsNotification(c); - - if (m_baseLayout) - m_baseLayout->addNotification(c->createWidget()); - - m_notiCtlList.append(c); - - connect(c, &LtsNotification::LtsNotificationController::destroyed, - this, [this, c]() { m_notiCtlList.removeAll(c); }); - - return n; -} - - -bool LtsSession::init(const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList, - bool hidden) -{ - m_foreverInvisible = hidden; - if (!LtsSessionDataDds::init(connParams)) - return false; - initializePlugins(dataPluginList, guiPluginList); - return true; -} - - -bool LtsSession::init(const dds::domain::DomainParticipant &dp, - const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList) -{ - if (!LtsSessionDataDds::init(dp, connParams)) - return false; - initializePlugins(dataPluginList, guiPluginList); - return true; -} - - -void LtsSession::initializePlugins(const QVector &dataPluginList, const QVector &guiPluginList) -{ - initializeDataPlugins(dataPluginList); - initializeGuiPlugins(guiPluginList); -} - - -void LtsSession::initializeDataPlugins(const QVector &dataPluginList) -{ - QMap> newDataPluginInterfaces; - - for (const LtsPluginManager::AppPluginInstance &api : dataPluginList) - { - DataPluginBase::Plugin *plugin = qobject_cast(api.pluginInstance.plugin); - - if (!plugin) - { - WARNING_LOG(m_logger) << "Session: invalid data plugin (" << api.pluginInstance.plugin->metaObject()->className() << ")" << api.pluginInstance.fileName; - continue; - } - - - if (app() == LtsAppDescriptor::App::Login - && (!plugin->pluginName().contains(QStringLiteral("AssetManagement")) - && !plugin->pluginName().contains(QStringLiteral("OperatorManagement")) - && !plugin->pluginName().contains(QStringLiteral("ExerciseManagement"))) - ) - continue; - - - if (!plugin->availableInPartition(partitionType())) - { - DEBUG_LOG(m_logger) << "Data plugin " << api.pluginInstance.pluginName << " is not available in this partition type " << partitionType(); - continue; - } - - - if ((!plugin->pluginName().contains(QStringLiteral("AssetManagement")) - && !plugin->pluginName().contains(QStringLiteral("OperatorManagement")) - && !plugin->pluginName().contains(QStringLiteral("ExerciseManagement")) - ) && app() == LtsAppDescriptor::App::Login - ) - continue; - std::shared_ptr ifc = plugin->createDataInterface(this); - if (!ifc) - { - WARNING_LOG(m_logger) << "Create data interface failed for '" << api.pluginInstance.pluginName << "' !"; - continue; - } - - m_dataPluginInterfaces[plugin->pluginName()] = ifc; - newDataPluginInterfaces[plugin->pluginName()] = ifc; - DEBUG_LOG(m_logger) << "Adding data plugin " << plugin->pluginName() << " for " << partitionType() << " - " << this; - } - - addAvailablaeDataPluginInterfaces(newDataPluginInterfaces); -} - - -void LtsSession::initializeGuiPlugins(const QVector &guiPluginList) -{ - m_failedFetches.clear(); - - - - - for (auto i = guiPluginList.constBegin(); i != guiPluginList.constEnd(); ++i) - { - const LtsPluginManager::AppPluginInstance &api = *i; - - GuiPluginBase::Plugin *plugin = qobject_cast(api.pluginInstance.plugin); - - if (!plugin) - { - WARNING_LOG(m_logger) << "Session: invalid gui plugin (" << api.pluginInstance.plugin->metaObject()->className() << ")" << api.pluginInstance.fileName; - continue; - } - - - - if (!plugin->availableInPartition(partitionType())) - { - DEBUG_LOG(m_logger) << "Gui plugin " << api.pluginInstance.pluginName << " is not available in this partition type " << partitionType(); - continue; - } - - - std::shared_ptr ifc = plugin->createGuiInterface(this); - - if (!ifc) - { - WARNING_LOG(m_logger) << "Create gui interface failed for '" << api.pluginInstance.pluginName << "' (" << api.pluginInstance.plugin->metaObject()->className() << ") !"; - continue; - } - - - QFlags flags = api.m_flags; - - if (api.pluginInstance.pluginName == QStringLiteral("dev")) - flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS; - - ifc->setFlags(flags); - ifc->setOptions(api.m_options); - ifc->setAppId(api.m_appId); - m_guiPluginInterfaces[plugin->pluginName()] = ifc; - DEBUG_LOG(m_logger) << "Adding gui plugin " << plugin->pluginName() << " for " << partitionType() << " - " << this; - } - - - - - QMutableMapIterator > it(m_guiPluginInterfaces); - while (it.hasNext()) - { - const auto val = it.next(); - const QString &guiPluginKey = val.key(); - std::shared_ptr guiPlugin = val.value(); - - if (!guiPlugin->fetchInterfaces()) - { - DEBUG_LOG(m_logger) << "Fetching interfaces failed for Gui Plugin '" << guiPluginKey << "' - removing and refetching !"; - m_failedFetches.append(guiPluginKey); - it.remove(); - it.toFront(); - - - for (auto i = m_payloadPriorityMap.begin(); i != m_payloadPriorityMap.end(); ++i) - { - std::shared_ptr payloadIfc = *i; - - if (payloadIfc == guiPlugin) - break; - } - - m_payloadList.removeAll(guiPlugin); - } - else - { - - - int executionPriority = guiPlugin->executionPriority(); - - if (executionPriority > -1) - { - if (m_payloads && guiPlugin->getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::PAYLOAD)) - { - if (executionPriority == 9999) - { - if (!m_payloadList.contains(guiPlugin)) - m_payloadList.append(guiPlugin); - } - else - m_payloadPriorityMap.insert(executionPriority, guiPlugin); - - } - } - - - } - } - - - QTimer::singleShot(100, this, &LtsSession::slotStartPluginInit); - -} - - -LtsTabWidget *LtsSession::locateWidget(QWidget *widgetToLocate, LayoutArea area) -{ - if (!m_baseLayout) - return nullptr; - - LtsTabWidget *tabWidget = m_baseLayout->getTabFromLayoutArea(area); - - if (tabWidget->indexOf(widgetToLocate) >= 0) - return tabWidget; - - return nullptr; -} - - -void LtsSession::enableBaseLayoutMenu(bool on) -{ - if (m_baseLayout) - { - QWidget *ltsMenu = m_baseLayout->getMenu(); - - if (ltsMenu) - ltsMenu->setEnabled(on); - } -} - - -void LtsSession::enableBaseLayoutArea(LtsWidgets::LayoutArea area, bool on) -{ - if (m_baseLayout) - { - QWidget *ltsWidget = m_baseLayout->getTabFromLayoutArea(area); - if (ltsWidget) - ltsWidget->setEnabled(on); - } -} - - -int LtsSession::workstationIp() -{ - static int wsId = -1; - if (wsId == -1) - { -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - const QString hostname = workstationName(); - const auto addreses = QHostInfo::fromName(hostname).addresses(); - for (const QHostAddress &ha : addreses) - { - if (ha.isLoopback()) - continue; - if (ha.protocol() != QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) - continue; - wsId = static_cast(ha.toIPv4Address()); - break; - } -#else - const QList interfaces = QNetworkInterface::allInterfaces(); - for (const QNetworkInterface &ninterface : interfaces) - { - if (ninterface.type() == QNetworkInterface::InterfaceType::Wifi) - continue; - const auto addreseEntry = ninterface.addressEntries(); - for (const QNetworkAddressEntry &ent : addreseEntry) - { - const QHostAddress ha = ent.ip(); - if (ha.isLoopback()) - continue; - if (ha.protocol() != QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) - continue; - wsId = static_cast(ha.toIPv4Address()); - break; - } - if (wsId != -1) - break; - } -#endif - } - return wsId; -} - - - -QString LtsSession::workstationName() -{ - return QHostInfo::localHostName().toUpper(); -} - - -const QString LtsSession::workspaceId() const -{ - if (isReplayPartition()) - return QStringLiteral("replay_%1").arg(QString::number(partitionId())); - return m_workspaceId; -} - - -void LtsSession::setWorkspaceId(const QString &newWorkspaceId ) -{ - DEBUG_LOG(m_logger) << LOGFUNCTION << " change workspaceId: cur = '" << m_workspaceId << "' for app '" << appToString(app()) << "'"; - DEBUG_LOG(m_logger) << LOGFUNCTION << " change workspaceId: new = '" << newWorkspaceId << "' for app '" << appToString(app()) << "'"; - m_workspaceId = newWorkspaceId; - - if (m_baseLayout) - m_baseLayout->setWorkspaceId(workspaceId() ); - - emit workspaceIdChanged(newWorkspaceId); -} - - -ExcState LtsSession::exerciseState() const -{ - return m_exerciseData ? m_exerciseData->exState : ExerciseState::eUnknown; -} - - -void LtsSession::connectOnlineHelpButton(LtsWidgets::LtsDialog *widget) -{ - - connect(widget, &LtsDialog::signalShowOnlineHelp, - this, &LtsSession::slotShowOnlineHelp, - Qt::UniqueConnection); -} - - -void LtsSession::slotStapleTopicLockHandler(LtsWidgets::LtsDialog *widget) -{ - if (widget->property("topicLockStapled").isValid()) - { - Q_ASSERT(false); - return; - } - widget->setProperty("topicLockStapled", true); - - connectOnlineHelpButton(widget); - if (m_coreFtc == nullptr) - { - DEBUG_LOG(m_logger) << "no core ftc, no TopicLockHandler for " << toString(); - return; - } - - auto *topicLockHandler = new TopicLockHandler(); - - - - if (!topicLockHandler->init(m_coreFtc, m_exerciseId)) - { - WARNING_LOG(m_logger) << "LtsSession::addWidget: init of TopicLockHandler failed!"; - delete topicLockHandler; - return; - } - - connect(topicLockHandler, &TopicLockHandler::signalLockDialog, - widget, &LtsDialog::slotLock, Qt::QueuedConnection); - connect(topicLockHandler, &TopicLockHandler::signalUnlockDialog, - widget, &LtsDialog::slotUnlock, Qt::QueuedConnection); - connect(widget, &LtsDialog::signalLockTopic, - topicLockHandler, &TopicLockHandler::slotTryLock, Qt::DirectConnection); - - - - connect(widget, &LtsDialog::destroyed, - topicLockHandler, &TopicLockHandler::slotFinalize, Qt::QueuedConnection); - - - - - connectOnlineHelpButton(widget); -} - - -bool LtsSession::focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area) -{ - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "no base layout to focus a tab on! menuGroup: " << menuGroup << "ident: " << identifier; - return false; - } - - return m_baseLayout->focusTab(menuGroup, identifier, area); -} - - -LtsWidgets::LtsDialog *LtsSession::getWidget(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area) -{ - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "no base layout to get a widget from! menuGroup: " << menuGroup << "ident: " << identifier; - return nullptr; - } - - return m_baseLayout->getWidget(menuGroup, identifier, area); -} - - -bool LtsSession::widgetExists(const QString &menuGroup, const QString &identifier, LayoutArea area) const -{ - if (!m_baseLayout) - return false; - LtsTabWidget *tab = m_baseLayout->getTabFromLayoutArea(area); - if (!tab) - return false; - QWidget *existingWidget = tab->getWidget(menuGroup, identifier); - return existingWidget != nullptr; -} - - -bool LtsSession::addWidget(LtsDialog *widget, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable) -{ - - - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "no base layout to put a widget on! menuGroup: " << menuGroup << "ident: " << identifier; - return false; - } - - - - - - - return m_baseLayout->addWidget(widget, menuGroup, identifier, area, setClosable); -} - - -void LtsSession::setExerciseData(const LtsWidgets::ExerciseData &exerciseData) -{ - - if ((*m_exerciseData) == exerciseData) - return; - - *m_exerciseData = exerciseData; - - if (!m_baseLayout) - return; - m_baseLayout->setExerciseData(exerciseData); - emit signalExerciseDataChanged(); -} - - -const LtsWidgets::ExerciseData *LtsSession::exerciseData() const -{ - return m_exerciseData; -} - - -void LtsSession::setProfilePicture(const QPixmap &pixmap) -{ - - if (!m_baseLayout) - return; - m_baseLayout->setProfilePicture(pixmap); -} - - -void LtsSession::makeVisible() -{ - Q_ASSERT(m_baseLayout == nullptr); - if (m_baseLayout) - { - qCritical() << "base layout already visible"; - return; - } - - createBaseLayout(); -} - - -void LtsSession::refetchActions() -{ - - for (auto i = m_guiPluginInterfaces.cbegin(); i != m_guiPluginInterfaces.cend(); ++i) - { - std::shared_ptr ifc = *i; - - if (!ifc->availableInPartition(partitionType())) - { - DEBUG_LOG(m_logger) << i.key() << " is not available in this partition type " << partitionType(); - continue; - } - - if (m_firstActionsFetch && ifc->getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN) && !ifc->getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS)) - { - ERROR_LOG(m_logger) << "Plugin '" << i.key() << "' has flag 'MAIN' but is missing the 'ACTIONS' flag - ingoring actions!"; - continue; - } - - if (ifc->getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS)) - { - - const QVector plData = ifc->getActions(); - - if (plData.isEmpty()) - DEBUG_LOG(m_logger) << i.key() << " (" << ifc->menuGroup() << ") does not provide actions"; - - - - for (const LtsWidgets::PluginData &pd : plData) - { - if (!pd.menuGroup.isEmpty() && !pd.menuText.isEmpty() && pd.identifier.isEmpty()) - ERROR_LOG(m_logger) << "Identifier for Action " << pd.menuGroup << " - " << pd.menuText << " missing - not added!"; - else - { - if (!LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(pd.operatorRight)) - m_missingRightActions += pd; - - m_baseLayout->addMenuAction(pd, m_firstActionsFetch); - } - } - } - else - { - - } - } - -#ifdef LTS_DEBUG - if (m_firstActionsFetch) - { - const QString &executeAction(qEnvironmentVariable("EXCON_EXECUTE_ACTION", {})); - if (!executeAction.isEmpty()) - { - WARNING_LOG(m_logger) << "EXCON_EXECUTE_ACTION was set to: " << executeAction; - m_baseLayout->executeAction(executeAction); - } - } -#endif - - m_firstActionsFetch = false; -} - -void LtsSession::slotSwap() -{ - LtsMultiMonitor::LtsMultiMonitor::getInstance()->selectAndSwap(m_baseLayout); -} - - -int LtsSession::screenId() const -{ - if (m_baseLayout == nullptr) - return -1; - - LtsMultiMonitor::LtsScreenBase *screen = LtsMultiMonitor::LtsMultiMonitor::getInstance()->widgetsScreen(m_baseLayout); - - if (screen == nullptr) - return -2; - - return screen->screenId(); -} - - -int LtsSession::screenContainerId() const -{ - if (m_baseLayout == nullptr) - return -1; - - LtsMultiMonitor::LtsScreenBase *screen = LtsMultiMonitor::LtsMultiMonitor::getInstance()->widgetsScreen(m_baseLayout); - - if (screen == nullptr) - return -2; - - try - { - return screen->containerIdOfWidget(m_baseLayout); - } - catch(...){} - - return -3; -} - - -int LtsSession::screenTypeId() const -{ - if (m_baseLayout == nullptr) - return -1; - - LtsMultiMonitor::LtsScreenBase *screen = LtsMultiMonitor::LtsMultiMonitor::getInstance()->widgetsScreen(m_baseLayout); - - if (screen == nullptr) - return -2; - - return screen->screenType(); -} - - -LtsAppDescriptor::App LtsSession::app() const -{ - return m_sessionManager ? m_sessionManager->appId() : LtsAppDescriptor::App::Login; -} - - -bool LtsSession::event(QEvent *e) -{ - if (e->type() == QEvent::LanguageChange) - m_baseLayout->setAppName(appToString(app())); - - return LtsSessionDataDds::event(e); -} - - -void LtsSession::addDeveloperActions() -{ - - { - LtsWidgets::PluginData p; - p.menuGroup = QStringLiteral("[Dev]"); - p.menuText = QStringLiteral("Open Admin Session"); - p.identifier = QStringLiteral("OpenAdminSession"); - - p.directFunc = [&](const LtsWidgets::PluginData &) - { - LtsSession *adminSession = m_sessionManager->getSession(IDL::PartitionType::Global, 0); - if (!adminSession) - { - - DdsConnectionParams connParams; - connParams.domainId = domainId(); - connParams.partitionId = 0; - connParams.exerciseId = 0; - connParams.partitionType = IDL::PartitionType::Global; - connParams.tcpConnectionUrl = tcpConnectionUrl(); - connParams.qos_file = m_qosFileList; - adminSession = m_sessionManager->createSession(connParams, - true, - false); - return; - } - else - adminSession->makeVisible(); - }; - - m_baseLayout->addMenuAction(p); - } - - { - LtsWidgets::PluginData p; - p.menuGroup = QStringLiteral("[Dev]"); - p.menuText = QStringLiteral("Slow down RequestHelper"); - p.identifier = QStringLiteral("SlowDownRequestHelper"); - - p.directFunc = [&](const LtsWidgets::PluginData &) - { - LtsUtils::RequestHelper::s_debug = true; - }; - - m_baseLayout->addMenuAction(p); - } - - { - LtsWidgets::PluginData p; - p.menuGroup = QStringLiteral("[Dev]"); - p.menuText = QStringLiteral("Reset RequestHelper"); - p.identifier = QStringLiteral("ResetRequestHelper"); - - p.directFunc = [&](const LtsWidgets::PluginData &) - { - LtsUtils::RequestHelper::s_debug = false; - }; - - m_baseLayout->addMenuAction(p); - } - - { - LtsWidgets::PluginData p; - p.menuGroup = QStringLiteral("[Dev]"); - p.menuText = QStringLiteral("fake DoneInitializingSession"); - p.identifier = QStringLiteral("fakeDoneInitializingSession"); - - p.directFunc = [&](const LtsWidgets::PluginData &) - { - m_initDone = true; - emit _signalDoneInitializingSession(); - }; - - m_baseLayout->addMenuAction(p); - } - -} - - -bool LtsSession::notifyUnsavedChanges() -{ - if (m_baseLayout == nullptr) - return true; - - return m_baseLayout->notifyUnsavedChanges(); -} - - -void LtsSession::createBaseLayout() -{ - - - using namespace LtsAppDescriptor; - - TRACE_LOG(m_logger) << LOGFUNCTION << " START, Creating Base Layout for Session " << partitionId(); - - Q_ASSERT(m_baseLayout == nullptr); - if (m_baseLayout != nullptr) - { - WARNING_LOG(m_logger) << "Base Layout already exists for Session" << this; - return; - } - - m_baseLayout = new LtsBaseLayoutWidget; - - connect(m_baseLayout, &LtsBaseLayoutWidget::signalSuccessfullyPlaced, - this, [this]() - { - DEBUG_LOG(m_logger) << "current APP "<app()); - - if ( this->app() == App::Scripts) - { - m_scriptEditorTimer->start(); - } - else - { - if (m_needsToBeSuccessfullyPlaced) - { - - emit _signalDoneInitializingSession(); - } - } - }); - - connect(m_baseLayout, &LtsBaseLayoutWidget::destroyed, - this, [this]() - { - m_baseLayout = nullptr; - - }); - - m_baseLayout->hide(); - m_baseLayout->setSessionInfo(domainId(), partitionType(), partitionId(), workspaceId()); - m_baseLayout->setWindowIcon(appToIcon(app())); - - - - - - - if (app() == App::ScriptEditorEssentialsView || - m_presetBaseLayoutAsSecondaryWidget) - m_baseLayout->setSecondaryWidget(); - - connect(m_baseLayout, &LtsBaseLayoutWidget::signalRetranslateActions, - this, &LtsSession::refetchActions); - connect(m_baseLayout, &LtsBaseLayoutWidget::signalClose, - this, &LtsSession::signalClose); - connect(m_baseLayout, &LtsBaseLayoutWidget::signalLogout, - this, &LtsSession::signalLogout); - connect(m_baseLayout, &LtsBaseLayoutWidget::signalShowOperatorSettings, - this, &LtsSession::slotShowOperatorSettings); - connect(m_baseLayout, &LtsBaseLayoutWidget::signalSwap, - this, &LtsSession::slotSwap); - connect(this, &LtsSession::partitionTimeChanged, - m_baseLayout, &LtsBaseLayoutWidget::setExerciseTime); - connect(m_baseLayout, &LtsBaseLayoutWidget::signalStapleTopicLockHandler, - this, &LtsSession::slotStapleTopicLockHandler, Qt::DirectConnection); - - const auto *sessionData = LtsSessionData::LtsSessionData::getInstance(); - - const QString &operatorName = sessionData->getOperatorName(); - bool showTerminalWindow = sessionData->getShowTerminalWindow(); - m_baseLayout->setOperatorName(operatorName, showTerminalWindow); - m_baseLayout->setProfilePicture(sessionData->getProfilePicture()); - m_baseLayout->setAppName(appToString(app())); - - refetchActions(); - -#ifdef LTS_DEBUG - bool showDeveloperActions = true; -#else - bool showDeveloperActions = false; -#endif - - if (sessionData->hasDebugFlag(QStringLiteral("DeveloperTools.Active"))) - showDeveloperActions = true; - - if (showDeveloperActions) - addDeveloperActions(); - - - for (LtsNotification::LtsNotificationController *ctl : qAsConst(m_notiCtlList)) - { - if (!m_baseLayout->notificationExists(ctl->identifier())) - m_baseLayout->addNotification(ctl->createWidget()); - } - - m_hasBaseLayout = true; - - m_baseLayout->getMenu()->setVisible(m_menuVisible); - m_baseLayout->initilizationFinished(); - - - - if (!m_foreverInvisible) - { - - QVector irreplacableWidgets {baseLayoutWidget()}; - LtsSession *dependencySession = dependency(); - int runaway = 100; - while (dependencySession && runaway > 0) - { - irreplacableWidgets += dependencySession->baseLayoutWidget(); - dependencySession = dependencySession->dependency(); - runaway--; - } - - - - - - - - - LtsMultiMonitor::LtsMultiMonitor *multiMonitor = LtsMultiMonitor::LtsMultiMonitor::getInstance(); - - WARNING_LOG(m_logger) << "Presets: ScreenId: " << m_screenIdPreset << ", screenContainerId: " << m_screenContainerIdPreset; - if (m_screenIdPreset >= 0) - multiMonitor->putScreen(m_baseLayout, m_screenIdPreset, m_screenContainerIdPreset); - else - multiMonitor->selectScreen(m_baseLayout, nullptr, true, false, irreplacableWidgets); - - slotMonitorSelected(); - connect(multiMonitor, &LtsMultiMonitor::LtsMultiMonitor::signalSelected, - this, &LtsSession::slotMonitorSelected, Qt::UniqueConnection); - - connect(multiMonitor, &LtsMultiMonitor::LtsMultiMonitor::signalScreenCountChanged, - m_baseLayout, &LtsBaseLayoutWidget::slotScreenCountChanged); - - m_baseLayout->slotScreenCountChanged(multiMonitor->screenCount()); - } - - - if (!sessionData->isTablet()) - return; - - - LtsNotification::LtsNotification *noti = createNotification(QStringLiteral("TabletWindowSwitcher")); - - noti->setIcon(QIcon(QStringLiteral("icon:switch_display.svg"))); - noti->setAction([this]() - { - emit taskSwitch(this ); - }); - - - emit hideAllOtherBLWs(this); -} - - -void LtsSession::slotMonitorSelected() -{ - auto mmm = LtsMultiMonitor::LtsMultiMonitor::getInstance(); - LtsMultiMonitor::LtsScreenBase *screen = mmm->widgetsScreen(m_baseLayout); - - if (!screen) - return; - - int newScreenContainerId; - try - { - newScreenContainerId = screen->containerIdOfWidget(m_baseLayout); - } - catch (...) - { - WARNING_LOG(m_logger) << "cannot find container for base layout"; - return; - } - - const int newScreenId = screen->screenId(); - if (m_screenId != newScreenId) - { - m_screenId = newScreenId; - m_screenContainerId = newScreenContainerId; - emit signalScreenIdChanged(m_screenId, m_screenContainerId, screen->screenType()); - } -} - -void LtsSession::delayScriptEditorStart() -{ - DEBUG_LOG(m_logger)<<"LtsSession::delayScriptEditorStart "; - if (m_needsToBeSuccessfullyPlaced) - { - - - emit _signalDoneInitializingSession(); - } -} - -bool LtsSession::isChildOfSession(QWidget *child) -{ - if (!child) - { - return false; - } - return baseLayout()->isAncestorOf(child); -} - -void LtsSession::doneInitializingSession() -{ - LtsGlobalWaitDialog::getInstance()->done(); - - - - if (m_markedForDeletion) - { - emit signalInitializationAborted(); - emit signalClose(); - return; - } - - if (!m_sessionManager->isHeadless()) - { - int sessionCount = m_sessionManager->sessionCount(); - - if (!m_hidden && (m_hasBaseLayout || sessionCount < 1)) - makeVisible(); - - - - - - - } - - - - if (!createTopicClient(m_coreFtc)) - WARNING_LOG(m_logger) << "could not create core ftc - no tlh"; - m_coreFtc->enableAll(); - - if (!getForeignGuiInterface(m_ohvIfc)) - WARNING_LOG(m_logger) << "no ovh ifc, no Help-Button !"; - - INFO_LOG(m_logger) << "emit signalDoneInitializingSession, session=" << this; - - - if (!m_needsToBeSuccessfullyPlaced || m_sessionManager->isHeadless() || !m_hasBaseLayout) - { - - - emit _signalDoneInitializingSession(); - } - - -} - - -void LtsSession::slotShowOnlineHelp(const QString &fileName) -{ - if (m_ohvIfc == nullptr) - { - LtsWidgets::LtsMessageDialog::showMessage(baseLayout() , tr("Online Help not available.")); - return; - } - - m_ohvIfc->showHelp(fileName); -} - - -void LtsSession::slotShowOperatorSettings() -{ - const QMap &mapButtons = m_baseLayout->mapButtons(); - - for (auto i = mapButtons.constBegin(); i != mapButtons.constEnd(); ++i) - { - const PluginData &pd = *i; - - if (pd.identifier == QStringLiteral("_OpmShowOperatorSettings")) - { - LtsDialog *showOperatorSettingsDlg = pd.createWidgetFunc(nullptr); - - if (showOperatorSettingsDlg != nullptr) - { - addWidget(showOperatorSettingsDlg, pd.menuGroup, pd.identifier, pd.layoutArea, pd.closable); - - } - else - WARNING_LOG(m_logger) << "could not create operator settings dialog from action"; - - return; - } - } - - WARNING_LOG(m_logger) << "action for showing operator settings not found !!"; -} - - -void LtsSession::slotStartPluginInit() -{ - m_initializeThesePlugins = m_guiPluginInterfaces; - slotPluginInitDone(); -} - - -void LtsSession::slotPluginInitDone() -{ - - - if (m_pluginInitDone) - return; - - if (m_slotPluginInitDoneMutex) - { - - - return; - } - - m_slotPluginInitDoneMutex = true; - - GuiPluginBase::Interface *theSender = dynamic_cast(sender()); - - if (theSender != m_currentInitIfc) - { - WARNING_LOG(m_logger) << this << " called slotPluginInitDone from plugin which was not currently active for initialization - ignoring"; -#ifdef LTS_DEBUG - WARNING_LOG(m_logger) << "current init ifc: " << m_currentInitIfc << ", sender: " << sender(); -#endif - m_slotPluginInitDoneMutex = false; - return; - } - - - - m_currentInitIfc = nullptr; - - - auto i = m_initializeThesePlugins.begin(); - - if (i == m_initializeThesePlugins.end()) - { - m_pluginInitDone = true; - - - executePayloads(); - - return; - } - - std::shared_ptr ifc = i.value(); - - if (ifc->availableInPartition(partitionType())) - { - m_currentInitIfc = ifc.get(); - - - - - ifc->init(); - if (!ifc->m_hasInit) - { - - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - } - else - { - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - - m_initializeThesePlugins.erase(i); - - m_slotPluginInitDoneMutex = false; -} - - -void LtsSession::slotExecuteNextPayload() -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); - - - - - - - - - auto it1 = m_payloadPriorityMap.begin(); - - if (it1 != m_payloadPriorityMap.end()) - { - std::shared_ptr ifc = (*it1); - m_payloadPriorityMap.erase(it1); - - DEBUG_LOG(m_logger) << "Executing priorized Payload in Partition " << partitionId() << ", Type " << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); - - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, - this, [name]() - { - - }); - connect(m_blub, &QTimer::timeout, - this, [this, name]() - { - m_blub2++; - - }); - m_blub->start(1000); - - - ifc->executePayload(m_mainBackground); - } - else - { - auto it2 = m_payloadList.begin(); - - if (it2 != m_payloadList.end()) - { - std::shared_ptr ifc = (*it2); - m_payloadList.erase(it2); - - DEBUG_LOG(m_logger) << "Executing unpriorized Payload in Partition " << partitionId() << ", Type " << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); - - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, - this, [name]() - { - - }); - connect(m_blub, &QTimer::timeout, - this, [this, name]() - { - m_blub2++; - - }); - m_blub->start(1000); - - - ifc->executePayload(m_mainBackground); - } - else - { - Q_ASSERT(m_payloadsDone == false); - m_payloadsDone = true; - - m_mainBackground = nullptr; - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - - m_blub = nullptr; - } - - - - doneInitializingSession(); - } - } - - -} - - -void LtsSession::executePayloads() -{ - - if (!m_payloadPriorityMap.isEmpty() || !m_payloadList.isEmpty()) - { - - - - - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); - - slotExecuteNextPayload(); - } - else - { - - DEBUG_LOG(m_logger) << "no payloads"; - - if (partitionType() == IDL::PartitionType::Global) - { - m_hasBaseLayout = true; - } - - doneInitializingSession(); - } -} - - -void LtsSession::clearDataPluginInterfaces() -{ - QMap> weakDataPluginInterfaces; - for (auto i = m_dataPluginInterfaces.cbegin(); i != m_dataPluginInterfaces.cend(); ++i) - { - - weakDataPluginInterfaces.insert(i.key(), i.value()); - } - - DEBUG_LOG(m_logger) << "Clearing data plugin interfaces for " << partitionType() << " - " << this; - m_dataPluginInterfaces.clear(); - -#ifdef LTS_DEBUG - - if (partitionType() != IDL::PartitionType::Global) - { - for (auto i = weakDataPluginInterfaces.cbegin(); i != weakDataPluginInterfaces.cend(); ++i) - { - std::shared_ptr sp = i.value().lock(); - if (!sp) - continue; - WARNING_LOG(m_logger) << "still referenced Data Plugin '" << i.key() - << "' in partition type " << partitionType() - << " (use_count=" << sp.use_count() << ")" - << " - application may crash on exit! " << this; - } - } -#endif -} - - -void LtsSession::clearGuiPluginInterfaces() -{ - bool bInClearPluginInterfaces = true; - auto cbCheckEventLoop = [this, &bInClearPluginInterfaces]() - { - if (bInClearPluginInterfaces) - { - ERROR_LOG(m_logger) << "QEventLoop was called within clearGuiPluginInterfaces which is not allowed!"; - Q_ASSERT(false); - } - }; - QTimer::singleShot(0, this, cbCheckEventLoop); - m_ohvIfc = nullptr; - QMap> weakGuiPluginInterfaces; - for (auto i = m_guiPluginInterfaces.cbegin(); i != m_guiPluginInterfaces.cend(); ++i) - { - i.value()->clearInterfaces(); - weakGuiPluginInterfaces.insert(i.key(), i.value()); - } - bInClearPluginInterfaces = false; - - DEBUG_LOG(m_logger) << "Clearing gui plugin interfaces for " << partitionType() << " - " << this; - m_guiPluginInterfaces.clear(); - -#ifdef LTS_DEBUG - - if (partitionType() != IDL::PartitionType::Global) - { - for (auto i = weakGuiPluginInterfaces.cbegin(); i != weakGuiPluginInterfaces.cend(); ++i) - { - std::shared_ptr sp = i.value().lock(); - if (!sp) - continue; - WARNING_LOG(m_logger) << "still referenced Gui Plugin '" << i.key() - << "'in partition type " << partitionType() - << " (use_count=" << sp.use_count() << ")" - << " Gui Plugin '" << i.key() << "'" - << " - application may crash on exit! " << this; - } - } -#endif -} - -LtsSessionManager *LtsSession::getSessionManager() -{ - return m_sessionManager; -} - -} diff --git a/Lts/Common/LtsSession/lts_session.h b/Lts/Common/LtsSession/lts_session.h deleted file mode 100755 index 0a739bab4..000000000 --- a/Lts/Common/LtsSession/lts_session.h +++ /dev/null @@ -1,285 +0,0 @@ - - - -#pragma once - -#include "lts_session_data_dds.h" -#include "lts_session_manager.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - - -#include - - -namespace LtsWidgets -{ -class LtsBackgroundWidget; -class LtsTabWidget; -class LtsBaseLayoutWidget; -struct ExerciseData; -class LtsDialog; -class LtsDragIcon; -} -namespace LtsNotification -{ -class LtsNotification; -class LtsNotificationController; -} -namespace LtsPluginManager -{ -struct PluginInstance; -struct AppPluginInstance; -} -namespace DeveloperTools -{ -class DevDialog; -class AppWidget; -} -namespace Core -{ -namespace Interface -{ -class Ftc; -} -} -namespace OnlineHelp -{ -class ForeignOnlineHelpViewerGuiInterface; -} - - - -namespace LtsSession -{ -class LTSSESSION_EXPORT LtsSession : public LtsSessionDataDds -{ - Q_OBJECT -public: - static bool s_valgrind; - static int s_nextSessionId; - - enum class PluginInstanceStatus - { - UNKNOWN = 0, - LOADED, - FAILED, - }; - - static QString appToString(LtsAppDescriptor::App app); - static QString appIconFilename(LtsAppDescriptor::App app); - static QIcon appToIcon(LtsAppDescriptor::App app); - - explicit LtsSession(LtsSessionManager *sessionManager); - ~LtsSession(); - - QString toString() const; - - LtsSessionManager *getSessionManager(); - bool init(const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList, bool hidden); - bool init(const dds::domain::DomainParticipant &dp, - const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList); - void makeVisible(); - void setPayloadsEnabled() { m_payloads = true; } - void aboutToClose(); - bool isInitializationDone() const { return m_initDone; } - void presetBaseLayoutAsSecondaryWidget() { m_presetBaseLayoutAsSecondaryWidget = true; } - - - - LtsWidgets::LtsDragIcon *addAction(const LtsWidgets::PluginData &plData, bool isFirstRun = false); - void setMenuVisible(bool visible = true); - bool removeMenuAction(LtsWidgets::LtsDragIcon *icon); - int screenId() const; - int screenContainerId() const; - int screenTypeId() const; - LtsAppDescriptor::App app() const; - void setScreenIdPreset(int screenId) { m_screenIdPreset = screenId; } - void setScreenContainerIdPreset(int screenPart) { m_screenContainerIdPreset = screenPart; } - bool isForeverInvisible() const { return m_foreverInvisible; } - - LtsNotification::LtsNotification *createNotification(const QString &identifier); - - - - void setExerciseData(const LtsWidgets::ExerciseData &exerciseData); - const LtsWidgets::ExerciseData *exerciseData() const; - void setProfilePicture(const QPixmap &pixmap); - void setBaseLayoutVisible() { m_hasBaseLayout = true; } - bool isVisible() const { return (m_baseLayout != nullptr); } - const QString &name() const { return m_name; } - void setName(const QString &name) { m_name = name; } - - - bool isChildOfSession(QWidget* child); - LtsWidgets::LtsBaseLayoutWidget* baseLayout() { return m_baseLayout; } - QWidget *baseLayoutWidget(); - void setMarkForDeletion() { m_markedForDeletion = true; } - void doneInitializingSession(); - bool notifyUnsavedChanges(); - const QVector &bgWidgets() const { return m_bgWidgets; } - - - bool widgetExists(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area) const; - - - bool addWidget(LtsWidgets::LtsDialog *widget, - const QString &menuGroup, - const QString &identifier, - LtsWidgets::LayoutArea area = LtsWidgets::LayoutArea::eUnknown, - bool setClosable = true); - - LtsWidgets::LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area); - bool focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area); - - - - LtsWidgets::LtsTabWidget *locateWidget(QWidget *widgetToLocate, LtsWidgets::LayoutArea area); - - - - - void enableBaseLayoutMenu(bool on = true); - - - - void enableBaseLayoutArea(LtsWidgets::LayoutArea area, bool on = true); - - - - static int workstationIp(); - - - static QString workstationName(); - - - - QVector &missingRightActions() { return m_missingRightActions; } - - void setSavegame(const QJsonObject &sg) { m_savegame = sg; } - const QJsonObject &getSavegame() const { return m_savegame; } - - void setHidden() { m_hidden = true; } - - const QString workspaceId() const; - void setWorkspaceId(const QString &newWorkspaceId); - - LtsWidgets::ExcState exerciseState() const; - - void setLoadedFromSavegame() { m_loadedFromSavegame = true; } - bool isLoadedFromSavegame() const { return m_loadedFromSavegame; } - void addDeveloperActions(); - void refetchActions(); - void setDependency(LtsSession *session); - bool hasDependency(LtsSession *session) { return (m_dependency.data() == session); } - LtsSession *dependency() const { return m_dependency; } - - - void setNeedsToBeSuccessfullyPlaced(bool needed = true) { m_needsToBeSuccessfullyPlaced = needed; } - - int id() const { return m_id; } - -public slots: - void slotExecuteNextPayload(); - void slotPluginInitDone(); - void slotShowOnlineHelp(const QString &fileName); - - -private slots: - void slotShowOperatorSettings(); - void slotSwap(); - void slotStapleTopicLockHandler(LtsWidgets::LtsDialog *widget); - void slotStopWaitDialog(); - void slotStartPluginInit(); - void slotMonitorSelected(); - void delayScriptEditorStart(); - -signals: - void signalInitializationAborted(); - void _signalDoneInitializingSession(); - void signalDoneInitializingSession(); - void signalClose(); - void signalLogout(); - void signalQuit(); - void signalStillAlive(); - void signalExerciseDataChanged(); - void signalScreenIdChanged(int screenId, int screenContainerId, int screenTypeId); - void sessionAboutToBeDestroyed(LtsSession *session); - void workspaceIdChanged(const QString &newWorkspaceId); - void signalTakeOverBdv(); - void hideAllOtherBLWs(QObject *caller); - void taskSwitch(QObject *caller); - -private: - friend class DeveloperTools::DevDialog; - friend class DeveloperTools::AppWidget; - friend class LtsSessionManager; - - bool event(QEvent *e) override; - void createBaseLayout(); - void initializePlugins(const QVector &dataPluginList, const QVector &guiPluginList); - void initializeDataPlugins(const QVector &dataPluginList); - void initializeGuiPlugins(const QVector &guiPluginList); - bool tryInitGuiPlugin(const QString &guiPluginKey, std::shared_ptr &guiPlugin); - void clearDataPluginInterfaces(); - void clearGuiPluginInterfaces(); - void executePayloads(); - void connectOnlineHelpButton(LtsWidgets::LtsDialog *widget); - - void aboutToCloseTab(LtsWidgets::LayoutArea area); - - QPointer m_sessionManager; - QMap > m_payloadPriorityMap; - QList > m_payloadList; - LtsWidgets::LtsBackgroundWidget *m_mainBackground; - bool m_hasBaseLayout; - LtsWidgets::LtsBaseLayoutWidget *m_baseLayout; - bool m_markedForDeletion; - bool m_payloads; - QVector m_notiCtlList; - bool m_initDone; - bool m_payloadsDone { false }; - bool m_menuVisible; - - bool m_firstActionsFetch; - LtsWidgets::ExerciseData *m_exerciseData; - QVector m_missingRightActions; - std::shared_ptr m_coreFtc; - GuiPluginBase::Interface *m_currentInitIfc; - bool m_slotPluginInitDoneMutex; - bool m_pluginInitDone; - QJsonObject m_savegame; - bool m_hidden; - int m_screenId { -1 }; - int m_screenContainerId { -1 }; - int m_screenIdPreset { -1 }; - int m_screenContainerIdPreset { -1 }; - bool m_loadedFromSavegame; - std::shared_ptr m_ohvIfc; - bool m_foreverInvisible; - QStringList m_failedFetches; - QVector m_bgWidgets; - QString m_workspaceId; - bool m_needsToBeSuccessfullyPlaced { true }; - QString m_name; - QTimer *m_blub { nullptr }; - QTimer *m_scriptEditorTimer { nullptr }; - int m_blub2; - QPointer m_dependency; - bool m_presetBaseLayoutAsSecondaryWidget { false }; - int m_id; -}; - -} diff --git a/Lts/Common/LtsSession/lts_session_data.cpp b/Lts/Common/LtsSession/lts_session_data.cpp deleted file mode 100755 index 1c8621467..000000000 --- a/Lts/Common/LtsSession/lts_session_data.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "lts_session_data.h" - - -namespace LtsSessionData -{ - -struct LtsSessionDataHandler -{ - LtsSessionData ltsSessionData; -}; - -Q_GLOBAL_STATIC(LtsSessionDataHandler, s_ltsSessionData) - - -LtsSessionData::LtsSessionData() - : m_domainIdPreset(-1) - , m_partitionIdPreset(-1) - , m_partitionTypePreset(IDL::PartitionType::Any) - , m_operatorId(-1) - , m_scriptId(0) - , m_showTerminalWindow(false) - , m_requestedAppId(LtsAppDescriptor::App::Login) - , m_workstationId(-1) - , m_isTablet(false) - , m_placeScriptScreens(false) -{ - - setAsTablet(false); - - clear(); -} - - -LtsSessionData *LtsSessionData::getInstance() -{ - return &s_ltsSessionData->ltsSessionData; -} - - -void LtsSessionData::loggedOut() -{ - m_operatorId = -1; - m_operatorRights.clear(); - m_operatorName.clear(); - m_showTerminalWindow = false; - m_profilePicture = QPixmap(); - m_visibleParties.clear(); -} - - -void LtsSessionData::removePresets() -{ - - m_partitionIdPreset = -1; - m_partitionTypePreset = IDL::PartitionType::Any; - - m_operatorNamePreset.clear(); - m_passwordPreset.clear(); -} - - -void LtsSessionData::clear() -{ - removePresets(); - loggedOut(); -} - - -bool LtsSessionData::hasOperatorRight(int operatorRight) const -{ - if (operatorRight == 0) - return true; - if (m_operatorRights.contains(operatorRight)) - return true; - return m_operatorRights.contains(-1); -} - - -bool LtsSessionData::isVisibleParty(int exerciseId, int partyId) const -{ - for (const auto &visibleParties : m_visibleParties) - { - if (visibleParties.exerciseId != exerciseId) - continue; - return visibleParties.visibleParties.contains(partyId); - } - return false; -} - -const QVector LtsSessionData::getVisibleParties(int exerciseId) const -{ - for(auto visibleParties : m_visibleParties) - { - if (visibleParties.exerciseId == exerciseId) - return visibleParties.visibleParties; - } - return {}; -} - - -void LtsSessionData::setAsTablet(bool isTablet) -{ - m_isTablet = isTablet; - -} - -} diff --git a/Lts/Common/LtsSession/lts_session_data.h b/Lts/Common/LtsSession/lts_session_data.h deleted file mode 100755 index a79f5e812..000000000 --- a/Lts/Common/LtsSession/lts_session_data.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsSessionData -{ - -struct PartiesByExercise -{ - int exerciseId = 0; - QVector visibleParties; -}; - -class LTSSESSIONDATA_EXPORT LtsSessionData -{ -public: - static LtsSessionData *getInstance(); - - - const QString &getOperatorNamePreset() const { return m_operatorNamePreset; } - const QString &getPasswordPreset() const { return m_passwordPreset; } - void setOperatorPreset(const QString &operatorName, const QString &password) { m_operatorNamePreset = operatorName; m_passwordPreset = password; } - - int getDomainIdPreset() const { return m_domainIdPreset; } - void setDomainIdPreset(int domainIdPreset) { m_domainIdPreset = domainIdPreset; } - - QStringList getQosFile() const {return m_qosFile; } - void setQosFile(const QStringList &qosFile) { m_qosFile = qosFile; } - - int getPartitionIdPreset() const { return m_partitionIdPreset; } - IDL::PartitionType getPartitionTypePreset() const { return m_partitionTypePreset; } - void setPartitionPreset(const int partitionId, IDL::PartitionType partitionType) { m_partitionIdPreset = partitionId; m_partitionTypePreset = partitionType; } - - - - void removePresets(); - - - - int getScriptId() const { return m_scriptId; } - void setScriptId(int scriptId) { m_scriptId = scriptId; } - - int getOperatorId() const { return m_operatorId; } - void setOperatorId(int operatorId) { m_operatorId = operatorId; } - - const QString &getOperatorName() const { return m_operatorName; } - void setOperatorName(const QString &operatorName) { m_operatorName = operatorName; } - - const QSet &getOperatorRights() const { return m_operatorRights; } - void setOperatorRights(const QSet &operatorRights) { m_operatorRights = operatorRights; } - bool hasOperatorRight(int operatorRight) const; - - const QPixmap &getProfilePicture() const { return m_profilePicture; } - void setProfilePicture(const QPixmap &profilePicture) { m_profilePicture = profilePicture; } - - bool isVisibleParty(int exerciseId, int partyId) const; - const QVector &getVisibleParties() const { return m_visibleParties; } - const QVector getVisibleParties(int exerciseId) const; - void setVisibleParties(const QVector &visibleParties) { m_visibleParties = visibleParties; } - - QUrl getTcpConnectionUrl() const { return m_tcpConnectionUrl; } - void setTcpConnectionUrl(const QUrl &tcpConnectionUrl) { m_tcpConnectionUrl = tcpConnectionUrl; } - - bool getShowTerminalWindow() const { return m_showTerminalWindow; } - void setShowTerminalWindow(bool showTerminalWindow) { m_showTerminalWindow = showTerminalWindow; } - - LtsAppDescriptor::App getRequestedAppId() const { return m_requestedAppId; } - void setRequestedAppId(LtsAppDescriptor::App requestedAppId) { m_requestedAppId = requestedAppId; } - - int getWorkstationId() const { return m_workstationId; } - void setWorkstationId(int workstationId) { m_workstationId = workstationId; } - - bool isTablet() const { return m_isTablet; } - void setAsTablet(bool isTablet = true); - - bool getPlaceScriptScreens() const { return m_placeScriptScreens; } - void setPlaceScriptScreens(bool placeScriptScreens) { m_placeScriptScreens = placeScriptScreens; } - - bool hasDebugFlag(const QString &debugFlag) const { return m_debugFlags.constEnd() != m_debugFlags.constFind(debugFlag); } - QVariant debugFlagValue(const QString &debugFlag) const { QVariant result; auto it = m_debugFlags.constFind(debugFlag); if (it != m_debugFlags.constEnd()) result = *it; return result; } - void setDebugFlagValue(const QString &debugFlag, const QVariant &value) { m_debugFlags.insert(debugFlag, value); } - const QMap &debugFlags() const { return m_debugFlags; } - void removeDebugFlag(const QString &debugFlag) { m_debugFlags.remove(debugFlag); } - - void clear(); - void loggedOut(); - -private: - LtsSessionData(); - ~LtsSessionData() = default; - friend struct LtsSessionDataHandler; - - - QString m_operatorNamePreset; - QString m_passwordPreset; - QStringList m_qosFile; - QUrl m_tcpConnectionUrl; - int m_domainIdPreset; - int m_partitionIdPreset; - IDL::PartitionType m_partitionTypePreset; - - int m_operatorId; - int m_scriptId; - QString m_operatorName; - bool m_showTerminalWindow; - QSet m_operatorRights; - QPixmap m_profilePicture; - QVector m_visibleParties; - QMap m_debugFlags; - LtsAppDescriptor::App m_requestedAppId; - int m_workstationId; - bool m_isTablet; - bool m_placeScriptScreens; -}; - -static inline bool hasOperatorRight(int operatorRight) -{ - auto instance = LtsSessionData::getInstance(); - return instance->hasOperatorRight(operatorRight); -} - -} diff --git a/Lts/Common/LtsSession/lts_session_data_dds.cpp b/Lts/Common/LtsSession/lts_session_data_dds.cpp deleted file mode 100755 index 71e29f4bf..000000000 --- a/Lts/Common/LtsSession/lts_session_data_dds.cpp +++ /dev/null @@ -1,389 +0,0 @@ - - - -#include "lts_session_data_dds.h" -#include -#include -#include -#include -#include -#include -#include - -namespace LtsSession -{ - - - - - - -class DdsConnectionContainer -{ - struct DdsConnection : public DdsConnectionParams - { - std::weak_ptr ddsConnector = std::weak_ptr(); - - DdsConnection() = default; - explicit DdsConnection(const DdsConnectionParams &p) - : DdsConnectionParams(p) - {} - }; - QVector ddsConnections; -public: - - std::shared_ptr findGlobalConnection() - { - for (int i = ddsConnections.size() - 1; i >= 0; --i) - { - const DdsConnection &conn = ddsConnections.at(i); - if (conn.partitionType != IDL::PartitionType::Global) - continue; - auto ret = conn.ddsConnector.lock(); - if (ret != nullptr) - return ret; - - ddsConnections.removeAt(i); - } - Q_ASSERT(false); - return std::shared_ptr(); - } - - std::shared_ptr findConnection(const DdsConnectionParams &connParams) - { - for (int i = ddsConnections.size() - 1; i >= 0; --i) - { - const DdsConnection &conn = ddsConnections.at(i); - if (conn != connParams) - continue; - auto ret = conn.ddsConnector.lock(); - if (ret != nullptr) - return ret; - - ddsConnections.removeAt(i); - } - return std::shared_ptr(); - } - - dds::domain::DomainParticipant findMatchingDomainParticipant(const DdsConnectionParams &connParams) - { - for (int i = ddsConnections.size() - 1; i >= 0; --i) - { - const DdsConnection &conn = ddsConnections.at(i); - if (!connParams.hasSameDomainParams(conn)) - continue; - auto ret = conn.ddsConnector.lock(); - if (ret != nullptr) - return ret->getDomainParticipant(); - - ddsConnections.removeAt(i); - } - return nullptr; - } - - void insertConnection(const DdsConnectionParams &connParams, - const std::shared_ptr &ddsConnector) - { - DdsConnection val(connParams); - val.ddsConnector = ddsConnector; - - ddsConnections += val; - } -}; -Q_GLOBAL_STATIC(DdsConnectionContainer, s_ddsConnections) - - - -class DataPluginContainer -{ -public: - struct DataPluginValue - { - QString identifier; - std::weak_ptr weakIfc; - - bool operator==(const DataPluginValue &o) const - { - return identifier == o.identifier && weakIfc.lock().get() == o.weakIfc.lock().get(); - } - }; - - QMap> m_dataPlugins; -public: - QMap> findDataPlugins(const DdsConnectionParams &connParams); - void insertDataPlugins(const DdsConnectionParams &connParams, - const QMap> &dataPlugins); -}; - -uint qHash(const DataPluginContainer::DataPluginValue &val) -{ - qint64 ptr = reinterpret_cast(val.weakIfc.lock().get()); - return ::qHash(val.identifier) ^ ::qHash(ptr); -} - -QMap> DataPluginContainer::findDataPlugins(const DdsConnectionParams &connParams) -{ - QMap> ret; - DdsConnectionParams key(connParams); - key.exerciseId = 0; - - auto it = m_dataPlugins.find(key); - if (it != m_dataPlugins.end()) - { - QSet &cont = it.value(); - auto itCont = cont.begin(); - while (itCont != cont.end()) - { - auto spPlugin = itCont->weakIfc.lock(); - if (spPlugin) - { - ret[itCont->identifier] = spPlugin; - ++itCont; - } - else - { - itCont = cont.erase(itCont); - } - } - } - return ret; -} - -void DataPluginContainer::insertDataPlugins(const DdsConnectionParams &connParams, - const QMap> &dataPlugins) -{ - DdsConnectionParams key(connParams); - key.exerciseId = 0; - - QSet cont; - cont.reserve(dataPlugins.size()); - for (auto it = dataPlugins.begin(); it != dataPlugins.end(); ++it) - { - DataPluginValue val; - val.identifier = it.key(); - val.weakIfc = it.value(); - cont += val; - } - - m_dataPlugins[key].unite(cont); -} - -Q_GLOBAL_STATIC(DataPluginContainer, s_dataPlugins) - - - - - - - -LtsSessionDataDds::LtsSessionDataDds(QObject *parent) - : QObject(parent) - , m_exerciseId(-1) - , m_emittedTime(0) - , m_curMSecSinceEpoch(0) - , m_active(true) - , m_fetcher(nullptr) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.Session")) -{} - -LtsSessionDataDds::~LtsSessionDataDds() -{ - m_dtcPartitionTime = nullptr; - m_ddsConnector = nullptr; - delete m_fetcher; -} - -bool LtsSessionDataDds::init(const DdsConnectionParams &connParams) -{ - - m_ddsConnector = s_ddsConnections->findConnection(connParams); - m_exerciseId = connParams.exerciseId; - m_qosFileList = QStringList(connParams.qos_file); - if (m_ddsConnector == nullptr) - { - - dds::domain::DomainParticipant dp = s_ddsConnections->findMatchingDomainParticipant(connParams); - if (dp != nullptr) - return init(dp, connParams); - #ifdef WIN_32 - DDS::QosProfile::ConnectionType type = DDS::QosProfile::ConnectionType::Tablet; - #else - DDS::QosProfile::ConnectionType type = DDS::QosProfile::ConnectionType::Client; - #endif - - m_ddsConnector = std::make_shared(connParams.domainId, - connParams.partitionType, - connParams.partitionId, - connParams.exerciseId, - type); - s_ddsConnections()->insertConnection(connParams, m_ddsConnector); - } - return initInternal(); -} - -bool LtsSessionDataDds::init(const dds::domain::DomainParticipant &dp, const DdsConnectionParams &connParams) -{ - Q_ASSERT(dp.domain_id() == connParams.domainId); - - m_ddsConnector = s_ddsConnections->findConnection(connParams); - m_exerciseId = connParams.exerciseId; - - - - QStringList qosList; - if (!connParams.qos_file.isEmpty()) - { - qosList.append(connParams.qos_file); - } - - if (!qosList.isEmpty()) - m_qosFileList = qosList; - else - m_qosFileList.clear(); - -#ifdef WIN_32 - DDS::QosProfile::ConnectionType type = DDS::QosProfile::ConnectionType::Tablet; -#else - DDS::QosProfile::ConnectionType type = DDS::QosProfile::ConnectionType::Client; -#endif - - if (m_ddsConnector == nullptr) - { - m_ddsConnector = std::make_shared(dp, - connParams.partitionType, - type, - connParams.partitionId - ); - - - - s_ddsConnections()->insertConnection(connParams, m_ddsConnector); - } - return initInternal(); -} - -Fetcher *LtsSessionDataDds::fetcher(const QString &yourPluginsName) -{ - if (m_fetcher == nullptr) - m_fetcher = new Fetcher(this, yourPluginsName); - - return m_fetcher; -} - -QString LtsSessionDataDds::partitionTypeString(IDL::PartitionType partitionType) -{ - switch (partitionType) - { - case IDL::PartitionType::AllExercises: - return QStringLiteral("AllExercises"); - case IDL::PartitionType::Any: - return QStringLiteral("Any"); - case IDL::PartitionType::Exercise: - return QStringLiteral("Exercise"); - case IDL::PartitionType::Global: - return QStringLiteral("Global"); - case IDL::PartitionType::Replay: - return QStringLiteral("Replay"); - } - - return QStringLiteral("Invalid (%1)").arg(int(partitionType)); -} - -QString LtsSessionDataDds::toString() const -{ - return QStringLiteral("(LtsSessionDataDds: id=%1, partitionId=%2, partitionType=%3(%4), exerciseId=%5)").arg((qulonglong)this).arg(partitionId()).arg(static_cast(partitionType())).arg(partitionTypeString(partitionType())).arg(exerciseId()); -} - -int LtsSessionDataDds::domainId() const -{ - return m_ddsConnector ? m_ddsConnector->getDomainId() : -1; -} - -int LtsSessionDataDds::exerciseId() const -{ - return m_exerciseId; -} - -int LtsSessionDataDds::partitionId() const -{ - return m_ddsConnector ? m_ddsConnector->getPartitionId() : -1; -} - -IDL::PartitionType LtsSessionDataDds::partitionType() const -{ - return m_ddsConnector ? m_ddsConnector->getPartitionType() : IDL::PartitionType::Global; -} - -QStringList LtsSessionDataDds::qosFile() const -{ - return m_ddsConnector ? m_qosFileList : QStringList(); -} - -QUrl LtsSessionDataDds::tcpConnectionUrl() const -{ - return m_ddsConnector ? m_ddsConnector->getTcpConnectionUrl() : QUrl(); -} - -DdsConnectionParams LtsSessionDataDds::connParams() const -{ - DdsConnectionParams connParams; - connParams.domainId = domainId(); - connParams.exerciseId = exerciseId(); - connParams.partitionId = partitionId(); - connParams.partitionType = partitionType(); - connParams.qos_file = qosFile(); - connParams.tcpConnectionUrl = tcpConnectionUrl(); - return connParams; -} - - -bool LtsSessionDataDds::initInternal() -{ - if (!m_ddsConnector || !m_ddsConnector->init(m_qosFileList)) - { - ERROR_LOG(m_logger) << "DDS connector could not be initialized - session aborted!"; - m_ddsConnector = nullptr; - return false; - } - - m_ddsConnector->initPublisherSubscriber(); - m_ddsConnectorGlobal = s_ddsConnections->findGlobalConnection(); - if (!m_ddsConnectorGlobal) - m_ddsConnectorGlobal = m_ddsConnector; - - m_dtcPartitionTime.reset(m_ddsConnector->createDTC()); - assert(m_dtcPartitionTime.get()); - - m_dtcPartitionTime->setSubscriberCallback([this](const Core::PartitionTime & data, DataModification) - { - m_curMSecSinceEpoch = data.timeStamp(); - emit partitionTimeChangedInternal(); - }); - connect(this, &LtsSessionDataDds::partitionTimeChangedInternal, - this, &LtsSessionDataDds::onPartitionTimeChangedInternal, Qt::QueuedConnection); - return true; -} - -void LtsSessionDataDds::onPartitionTimeChangedInternal() -{ - if (!m_active) - return; - if (m_emittedTime == m_curMSecSinceEpoch) - return; - m_emittedTime = m_curMSecSinceEpoch; - emit partitionTimeChanged(m_curMSecSinceEpoch); -} - -QMap> LtsSessionDataDds::availableDataPluginInterfaces() const -{ - return s_dataPlugins->findDataPlugins(connParams()); - -} - -void LtsSessionDataDds::addAvailablaeDataPluginInterfaces(const QMap> &dataPlugins) -{ - s_dataPlugins->insertDataPlugins(connParams(), - dataPlugins); -} - -} diff --git a/Lts/Common/LtsSession/lts_session_data_dds.h b/Lts/Common/LtsSession/lts_session_data_dds.h deleted file mode 100755 index e9e3fbbb5..000000000 --- a/Lts/Common/LtsSession/lts_session_data_dds.h +++ /dev/null @@ -1,349 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include - - -namespace DataPluginBase { class Interface; } -namespace DDSConnector { class DdsConnector; } -namespace GuiPluginBase { class Interface; } - -namespace LtsSession -{ -struct DdsConnectionParams -{ - int domainId = -1; - int partitionId = 0; - int exerciseId = 0; - IDL::PartitionType partitionType = IDL::PartitionType::Any; - QUrl tcpConnectionUrl; - QStringList qos_file; - - DdsConnectionParams() = default; - explicit DdsConnectionParams(const Core::ReplayPartition &rp) - : domainId(rp.domainId()) - , partitionId(rp.id()) - , exerciseId(rp.exerciseId()) - , partitionType(IDL::PartitionType::Replay) - { - setTcpConnectionUrl(rp.tcpConnectionUrl()); - } - - bool isValid() const { return domainId >= 0 && partitionType != IDL::PartitionType::Any; } - bool hasSameDomainParams(const DdsConnectionParams &o) const - { - return domainId == o.domainId && - tcpConnectionUrl == o.tcpConnectionUrl; - } - bool operator==(const DdsConnectionParams &o) const - { - return domainId == o.domainId && - partitionId == o.partitionId && - exerciseId == o.exerciseId && - qos_file == o.qos_file && - partitionType == o.partitionType && - tcpConnectionUrl == o.tcpConnectionUrl; - } - bool operator !=(const DdsConnectionParams &o) const - { - return !operator==(o); - } - bool operator <(const DdsConnectionParams &o) const - { - if (domainId != o.domainId) - return domainId < o.domainId; - if (partitionId != o.partitionId) - return partitionId < o.partitionId; - if (exerciseId != o.exerciseId) - return exerciseId < o.exerciseId; - if (qos_file != o.qos_file) - return qos_file < o.qos_file; - if (partitionType != o.partitionType) - return partitionType < o.partitionType; - return tcpConnectionUrl.toString() < o.tcpConnectionUrl.toString(); - } - - void setTcpConnectionUrl(const std::string &url) - { - tcpConnectionUrl = QUrl::fromUserInput(QString::fromStdString(url)); - } -}; - - -class Fetcher; - - -class LTSSESSIONDATADDS_EXPORT LtsSessionDataDds - : public QObject -{ - Q_OBJECT -protected: - - explicit LtsSessionDataDds(QObject *parent = nullptr); -public: - ~LtsSessionDataDds(); - - bool init(const DdsConnectionParams &connParams); - bool init(const dds::domain::DomainParticipant &dp, - const DdsConnectionParams &connParams); - - std::shared_ptr ddsConnector() { return m_ddsConnector; } - std::shared_ptr ddsConnectorGlobal() { return m_ddsConnectorGlobal; } - int domainId() const; - int exerciseId() const; - int partitionId() const; - QStringList qosFile() const; - IDL::PartitionType partitionType() const; - static QString partitionTypeString(IDL::PartitionType partitionType); - bool isExercisePartition() const { return (partitionType() == IDL::PartitionType::Exercise); } - bool isReplayPartition() const { return (partitionType() == IDL::PartitionType::Replay); } - QString toString() const; - Fetcher *fetcher(const QString &yourPluginsName); - - QUrl tcpConnectionUrl() const; - DdsConnectionParams connParams() const; - - qint64 partitionTime() const { return m_curMSecSinceEpoch; } - - - template - bool createTopicClient(std::shared_ptr &member, bool bEnableAll = false, bool bGlobal = false) - { - member = T::create(bGlobal ? m_ddsConnectorGlobal : m_ddsConnector); - if ( bEnableAll && !member->enableAll()) - { - member = nullptr; - ERROR_LOG(m_logger) << "Error initializing dtc/ftc for service " << T::identifier(); - return false; - } - return true; - } - - template - bool createTopicClient(std::shared_ptr &dtc, std::shared_ptr &ftc, bool bEnableAll = false, bool bGlobal = false) - { - dtc = T::create(bGlobal ? m_ddsConnectorGlobal : m_ddsConnector); - ftc = S::create(bGlobal ? m_ddsConnectorGlobal : m_ddsConnector); - if ( (bEnableAll && !dtc->enableAll()) || !ftc->enableAll()) - { - dtc = nullptr; - ftc = nullptr; - ERROR_LOG(m_logger) << "Error initializing dtc/ftc for service " << T::identifier(); - return false; - } - return true; - } - - template - bool getDtc(std::shared_ptr &val) const - { - for (const auto &i : m_dataPluginInterfaces) - { - auto ifc = std::dynamic_pointer_cast(i); - if (ifc) - { - val = ifc->getDtc(); - return val != nullptr; - } - } - val.reset(); - DEBUG_LOG(m_logger) << "Trying to load dtc " << typeid(T).name() << " - " << typeid(S).name() << " failed!" - << ", m_dataPluginInterfaces.size() = " << m_dataPluginInterfaces.size() << " - " << this; - return false; - } - - - template - bool getFtc(std::shared_ptr &val) const - { - for (const auto &i : m_dataPluginInterfaces) - { - auto ifc = std::dynamic_pointer_cast(i); - if (ifc) - { - val = ifc->getFtc(); - return val != nullptr; - } - } - val.reset(); - DEBUG_LOG(m_logger) << "Trying to load ftc " << typeid(T).name() << " - " << typeid(S).name() << " failed!" - << ", m_dataPluginInterfaces.size() = " << m_dataPluginInterfaces.size() << " - " << this; - return false; - } - - - - template - bool getForeignDataInterface(std::shared_ptr &val) const - { - - - for (const auto &i : m_dataPluginInterfaces) - { - auto ifc = std::dynamic_pointer_cast(i); - if (ifc) - { - val = ifc->getForeignDataInterface(); - return val != nullptr; - } - } - val.reset(); - DEBUG_LOG(m_logger) << "Trying to load foreign data ifc " << typeid(T).name() << " - " << typeid(S).name() << " failed!" - << ", m_dataPluginInterfaces.size() = " << m_dataPluginInterfaces.size() << " - " << this; - return false; - } - - - - - - - template - bool getForeignDataInterface_NEW(std::shared_ptr &val) const - { - - - for (const auto &i : m_dataPluginInterfaces) - { - val = std::dynamic_pointer_cast(i); - if (val) - return true; - } - val.reset(); - DEBUG_LOG(m_logger) << "Trying to load foreign data ifc " << typeid(S).name() << " failed!" - << ", m_guiPluginInterfaces.size() = " << m_guiPluginInterfaces.size() << " - " << this; - return false; - } - - - - - template - bool getForeignGuiInterface(std::shared_ptr &val) const - { - for (const auto &i : m_guiPluginInterfaces) - { - val = std::dynamic_pointer_cast(i); - if (val) - return true; - } - val.reset(); - DEBUG_LOG(m_logger) << "Cannot find foreign gui ifc " << typeid(S).name() << " in " << toString(); - - return false; - } - - - template - bool interfaceFromGui(std::shared_ptr &val) const - { - for (const auto &i : m_guiPluginInterfaces) - { - val = std::dynamic_pointer_cast(i); - if (val) - return true; - } - val.reset(); - DEBUG_LOG(m_logger) << "Trying to load foreign gui ifc #2 " << typeid(S).name() << " failed!" - << ", m_guiPluginInterfaces.size() = " << m_guiPluginInterfaces.size() << " - " << this; - return false; - } - -signals: - void partitionTimeChanged(qint64 newPartitionTime); - void partitionTimeChangedInternal(); - -private: - void onPartitionTimeChangedInternal(); - bool initInternal(); - -protected: - std::shared_ptr m_ddsConnector; - std::shared_ptr m_ddsConnectorGlobal; - int m_exerciseId; - std::shared_ptr m_dtcPartitionTime; - qint64 m_emittedTime; - qint64 m_curMSecSinceEpoch; - bool m_active; - void setInactive() { m_active = false; } - - - QMap> availableDataPluginInterfaces() const; - void addAvailablaeDataPluginInterfaces(const QMap> &dataPlugins); - QMap> m_dataPluginInterfaces; - QMap> m_guiPluginInterfaces; - QMap> m_initializeThesePlugins; - Fetcher *m_fetcher; - QStringList m_qosFileList; - NGL::INGLLogger &m_logger; -}; - - -class Fetcher -{ -public: - Fetcher(LtsSessionDataDds *sessionDataDds, const QString &caller) - : m_sessionDataDds(sessionDataDds) - , m_errors(false) - , m_caller(caller) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.Fetcher")) - , m_optional(false) - { - - } - - LtsSessionDataDds *m_sessionDataDds; - bool m_errors; - QString m_caller; - QString m_whatWillNotWork; - NGL::INGLLogger &m_logger; - bool m_optional; - QString m_resultType; - QString m_getterFunctionType; - - template - void getForeignGuiInterface(std::shared_ptr &foreignGuiInterface, const QString &whatWillNotWork , bool optional) - { - if (!m_sessionDataDds->getForeignGuiInterface(foreignGuiInterface)) - { - m_whatWillNotWork = whatWillNotWork; - m_optional = optional; - m_resultType = QString::fromUtf8(typeid(ForeignGuiInterface).name()); - m_getterFunctionType = QStringLiteral("gui interface"); - print(); - } - } - - bool status() const - { - return !m_errors; - } - - void print() - { - m_errors = true; - - QString msg(QStringLiteral("%1 cannot find '%2' in %3 %4")); - - if (m_optional) - { - INFO_LOG(m_logger) << msg.arg(m_caller, m_resultType, m_sessionDataDds->toString(), QStringLiteral("' - optional but %1 will not work").arg(m_whatWillNotWork)); - } - else - { - ERROR_LOG(m_logger) << msg.arg(m_caller, m_resultType, m_sessionDataDds->toString(), QStringLiteral(" which is required")); - } - } -}; - -} diff --git a/Lts/Common/LtsSession/lts_session_lock_handler.cpp b/Lts/Common/LtsSession/lts_session_lock_handler.cpp deleted file mode 100755 index 7099cf1c3..000000000 --- a/Lts/Common/LtsSession/lts_session_lock_handler.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#include "lts_session_lock_handler.h" - -#include -#include - -#include - -#include - - -namespace LtsSession -{ - -TopicLockHandler::TopicLockHandler() - : QObject() - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.TopicLockHandler")) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -TopicLockHandler::~TopicLockHandler() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -bool TopicLockHandler::init(const std::shared_ptr &coreFtc, - int exerciseId) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - if (coreFtc == nullptr) - return false; - coreFtc->enableAll(); - m_exerciseId = exerciseId; - m_coreFtc = coreFtc; - - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; - return true; -} - - -bool TopicLockHandler::lock() -{ - INFO_LOG(m_logger) << "LOCK " << m_topicInfo.topicName(); - TRACE_LOG(m_logger) << LOGFUNCTION << objStateAsString(); - - if (m_coreFtc == nullptr) - return false; - - if (m_locked) - { - ERROR_LOG(m_logger) << LOGFUNCTION << ": called lock while still locked! " << m_lockEntityId; - Q_ASSERT(false); - return false; - } - - const auto *sessionData = LtsSessionData::LtsSessionData::getInstance(); - - Core::TryLockEntityRequest req; - req.id(m_lockEntityId); - - req.entityTypeName(m_topicInfo.topicName().toStdString()); - req.entityId(m_topicInfo.entityId()); - req.exerciseId(m_exerciseId); - - req.operatorId(sessionData->getOperatorId()); - req.workstationId(sessionData->getWorkstationId()); - - try - { - const Core::TryLockEntityReply reply(m_coreFtc->tryLockEntityRequest(req)); - - TRACE_LOG(m_logger) << LOGFUNCTION << ": Reply received - state=" << objStateAsString() - << " - reply=" << reply; - - if (reply.alreadyLocked().is_set()) - { - const Core::LockEntityError &alreadyLocked = reply.alreadyLocked().get(); - INFO_LOG(m_logger) << LOGFUNCTION << ": Topic already locked by ws " << alreadyLocked.workstationName() << " and op " << alreadyLocked.operatorName(); - - - - - const auto lockedBy(QStringLiteral("%2/%1").arg(QString::fromStdString(alreadyLocked.operatorName()), QString::fromStdString(alreadyLocked.workstationName()))); - - emit signalLockDialog(lockedBy); - } - else - { - DEBUG_LOG(m_logger) << LOGFUNCTION << ": Topic NOT already locked"; - - Q_ASSERT(reply.id() > 0); - m_locked = true; - m_lockEntityId = reply.id(); - } - } - catch (const std::runtime_error &err) - { - ERROR_LOG(m_logger) << LOGFUNCTION << ": Failed to lock topic! " << err.what(); - Q_ASSERT(false); - return false; - } - - return true; -} - -bool TopicLockHandler::unlock() -{ - INFO_LOG(m_logger) << "UNLOCK " << m_topicInfo.topicName() << " - " << m_lockEntityId; - TRACE_LOG(m_logger) << LOGFUNCTION << objStateAsString(); - - if (m_coreFtc == nullptr) - return false; - - if (!m_locked) - { - ERROR_LOG(m_logger) << LOGFUNCTION << ": Tried to unlock while unlocked.. " << objStateAsString(); - Q_ASSERT(false); - return false; - } - - Q_ASSERT(m_lockEntityId > 0); - - const Core::TryUnlockEntityRequest req(m_lockEntityId); - - - m_locked = false; - m_lockEntityId = 0; - - try - { - const auto reply(m_coreFtc->tryUnlockEntityRequest(req)); - - TRACE_LOG(m_logger) << LOGFUNCTION << ": Reply received - state=" << objStateAsString() - << " - reply=" << reply; - - DEBUG_LOG(m_logger) << LOGFUNCTION << ": Topic unlocked!"; - } - catch (const std::runtime_error &err) - { - ERROR_LOG(m_logger) << LOGFUNCTION << ": Failed to unlock topic! " << err.what(); - return false; - } - - - - emit signalUnlockDialog(); - - - - return true; -} - - -std::string TopicLockHandler::objStateAsString() const -{ - std::ostringstream o; - o << " m_topicInfoId=" << m_lockEntityId - << ", entityId=" << m_topicInfo.entityId() - << ", exId=" << m_exerciseId - << ", lckd=" << m_locked - << ", asked=" << m_askedForLock - << ", delftrAnwr=" << m_deleteAfterAnswer; - return o.str(); -} - - -bool TopicLockHandler::slotUnlock() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " slotUnlock" << objStateAsString(); - return unlock(); -} - - -void TopicLockHandler::slotTryLock(const LtsWidgets::TopicInfo &topicInfo) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START " << objStateAsString(); - - m_topicInfo = topicInfo; - - if (m_topicInfo.topicName().isEmpty() || !topicInfo.isValid()) - { - DEBUG_LOG(m_logger) << LOGFUNCTION << " no lockable topic, that's okay"; - return; - } - - lock(); - TRACE_LOG(m_logger) << LOGFUNCTION << " END " << objStateAsString(); -} - - -void TopicLockHandler::slotFinalize() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START " << objStateAsString(); - - if (m_locked) - { - slotUnlock(); - } - - deleteLater(); - - TRACE_LOG(m_logger) << LOGFUNCTION << " END " << objStateAsString(); -} - - - - -TopicLockHanderData::TopicLockHanderData() - : QObject() -{ -} - -void TopicLockHanderData::init(const std::shared_ptr &coreFtc, int exerciseId) -{ - m_topicLockHandler = new TopicLockHandler(); - m_topicLockHandler->init(coreFtc, exerciseId); - - connect(m_topicLockHandler.data(), &TopicLockHandler::signalLockDialog, - this, &TopicLockHanderData::slotLock, Qt::UniqueConnection); - connect(m_topicLockHandler.data(), &TopicLockHandler::signalUnlockDialog, - this, &TopicLockHanderData::slotUnlock, Qt::UniqueConnection); -} - -TopicLockHanderData::~TopicLockHanderData() -{ - - if (m_topicLockHandler) - m_topicLockHandler->slotFinalize(); -} - -void TopicLockHanderData::unlock() -{ - qDebug() << "Debugname=" << m_debugName << ": " - << " slotUnlock"; - if (m_topicLockHandler) - m_topicLockHandler->slotUnlock(); -} - -bool TopicLockHanderData::isLocked() const -{ - if (!m_topicLockHandler) - { - qDebug() << "Debugname=" << m_debugName << ": " - << " isLocked -> no handler"; - return false; - } - qDebug() << "Debugname=" << m_debugName << ": " - << " isLocked = " << m_topicLockHandler->isLocked(); - return m_topicLockHandler->isLocked(); -} - -LtsWidgets::TopicInfo TopicLockHanderData::getTopicInfo() const -{ - if (m_topicLockHandler) - return m_topicLockHandler->getTopicInfo(); - return {}; -} - -void TopicLockHanderData::slotTryLock(const LtsWidgets::TopicInfo &topicInfo) -{ - qDebug() << "Debugname=" << m_debugName << ": " - << "slotTryLock " << m_topicLockHandler; - if (!m_topicLockHandler) - return; - m_topicLockHandler->slotTryLock(topicInfo); -} - - -void TopicLockHanderData::slotLock(const QString &lockedBy) -{ - - - qDebug() << "Debugname=" << m_debugName << ": " - << "slotLock"; - m_lockedBy = lockedBy; - emit lockDataChanged(); -} - -void TopicLockHanderData::slotUnlock() -{ - qDebug() << "Debugname=" << m_debugName << ": " - << "slotUnlock"; - m_lockedBy.clear(); - emit lockDataChanged(); -} - - -} diff --git a/Lts/Common/LtsSession/lts_session_lock_handler.h b/Lts/Common/LtsSession/lts_session_lock_handler.h deleted file mode 100755 index 947da5433..000000000 --- a/Lts/Common/LtsSession/lts_session_lock_handler.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include - - - - - -namespace LtsSession -{ -class LTSSESSION_EXPORT TopicLockHandler : public QObject -{ - Q_OBJECT -public: - TopicLockHandler(); - ~TopicLockHandler(); - - bool init(const std::shared_ptr &coreFtc, int exerciseId); - - bool isLocked() const { return m_locked; } - LtsWidgets::TopicInfo getTopicInfo() const { return m_topicInfo; } - -public slots: - void slotTryLock(const LtsWidgets::TopicInfo &topicInfo); - bool slotUnlock(); - void slotFinalize(); - -signals: - void signalLockDialog(const QString &lockedBy); - void signalUnlockDialog(); - -private: - bool lock(); - bool unlock(); - std::string objStateAsString() const; - - std::shared_ptr m_coreFtc; - - LtsWidgets::TopicInfo m_topicInfo; - int m_lockEntityId{0}; - - int m_exerciseId{0}; - bool m_locked{false}; - bool m_askedForLock{false}; - bool m_deleteAfterAnswer{false}; - - NGL::INGLLogger &m_logger; -}; - - -class LTSSESSION_EXPORT TopicLockHanderData : public QObject -{ - Q_OBJECT - -public: - TopicLockHanderData(); - ~TopicLockHanderData(); - void init(const std::shared_ptr &coreFtc, int exerciseId); - - void unlock(); - void slotTryLock(const LtsWidgets::TopicInfo &topicInfo); - bool isLocked() const; - LtsWidgets::TopicInfo getTopicInfo() const; - const QString &getLockedBy() const { return m_lockedBy; } - -Q_SIGNALS: - void lockDataChanged(); - -private Q_SLOTS: - void slotLock(const QString &lockedBy); - void slotUnlock(); -public: - QString m_debugName; -private: - QPointer m_topicLockHandler; - QString m_lockedBy; -}; - - -} diff --git a/Lts/Common/LtsSession/lts_session_manager.cpp b/Lts/Common/LtsSession/lts_session_manager.cpp deleted file mode 100755 index 6b3563617..000000000 --- a/Lts/Common/LtsSession/lts_session_manager.cpp +++ /dev/null @@ -1,240 +0,0 @@ - - - - -#include "lts_session_manager.h" -#include "lts_session.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsSession -{ - -LtsSessionManager::LtsSessionManager() - : QObject() - , m_appId(LtsAppDescriptor::App::Login) - , m_headless(false) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.Manager")) -{} - - -LtsSessionManager::~LtsSessionManager() -{ - - - for (auto i = m_sessionMap.begin(); i != m_sessionMap.end(); ++i) - { - LtsSession *session = i.value(); - session->setInactive(); - session->deleteLater(); - } -} - -bool LtsSessionManager::init(const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList, - LtsAppDescriptor::App appId, - const QString &workspaceId, - bool headless, - bool hidden) -{ - - m_dataPluginList = dataPluginList; - m_guiPluginList = guiPluginList; - m_appId = appId; - m_headless = headless; - - - - - - - - - bool bHasBaseLayout = connParams.exerciseId > 0; - - LtsSession *session = createSession(connParams, bHasBaseLayout, true, hidden); - session->setWorkspaceId(workspaceId); - return session; -} - - -QVector LtsSessionManager::sessions() -{ - return m_sessionMap.values().toVector(); - - - - - - -} - - -void LtsSessionManager::softQuit() -{ - for (auto i = m_sessionMap.begin(); i != m_sessionMap.end(); ++i) - { - LtsSession *session = *i; - session->aboutToClose(); - session->setInactive(); - session->deleteLater(); - } - - m_sessionMap.clear(); - - emit hideAllOtherBLWs(nullptr); - - QMetaObject::invokeMethod(this, &LtsSessionManager::signalQuit, Qt::QueuedConnection); -} - - -LtsSession *LtsSessionManager::createSession(const DdsConnectionParams &connParams, - bool hasBaseLayout, - bool payloads, - bool hidden) -{ - LtsSession *session = getSession(connParams.partitionType, connParams.partitionId); - - if (session != nullptr) - { - WARNING_LOG(m_logger) << "Tried to create a Session that already exists"; - return nullptr; - } - - session = new LtsSession(this); - - connect(session, &LtsSession::signalQuit, - this, &LtsSessionManager::signalQuit); - - - connect(session, &LtsSession::signalClose, - this, &LtsSessionManager::slotCloseSession, Qt::QueuedConnection); - connect(session, &LtsSession::signalLogout, - this, &LtsSessionManager::signalLogout); - connect(session, &LtsSession::signalInitializationAborted, - this, &LtsSessionManager::signalInitializationAborted); - connect(session, &LtsSession::hideAllOtherBLWs, - this, &LtsSessionManager::hideAllOtherBLWs); - connect(session, &LtsSession::taskSwitch, - this, &LtsSessionManager::taskSwitch); -#ifdef LTS_DEBUG - connect(session, &LtsSession::signalStillAlive, - this, [this]() - { - LtsSession *session = qobject_cast(sender()); - - for (auto i = m_sessionMap.constBegin(); i != m_sessionMap.constEnd(); ++i) - { - if (i.value() == session) - return; - } - - WARNING_LOG(m_logger) << "Previously deleted Session " << session->partitionId() << "/" - << session->partitionType() << " is still alive!"; - }); -#endif - - if (hasBaseLayout) - session->setBaseLayoutVisible(); - - if (payloads) - session->setPayloadsEnabled(); - - m_sessionMap[SessionKey(connParams.partitionType, connParams.partitionId)] = session; - - - LtsSession *globSession = getSession(IDL::PartitionType::Global, 0); - if (globSession && globSession->ddsConnector() && globSession->domainId() == connParams.domainId) - { - - dds::domain::DomainParticipant dp = globSession->ddsConnector()->getDomainParticipant(); - if (!session->init(dp, connParams, m_dataPluginList, m_guiPluginList)) - { - session->deleteLater(); - return nullptr; - } - } - else - { - - - - if (!session->init(connParams, m_dataPluginList, m_guiPluginList, hidden)) - { - session->deleteLater(); - return nullptr; - } - } - - - if (session->isInitializationDone()) - QMetaObject::invokeMethod(this, &LtsSessionManager::signalInitializationFinished, - Qt::QueuedConnection); - else - connect(session, &LtsSession::signalDoneInitializingSession, - this, &LtsSessionManager::signalInitializationFinished, - Qt::QueuedConnection); - - - return session; -} - - -void LtsSessionManager::slotCloseSession() -{ - LtsSession *session = qobject_cast(QObject::sender()); - - if (!session) - { - FATAL_LOG(m_logger) << "LtsSessionManager: slot called without object - so it cannot be deleted from the session list!"; - return; - } - - bool found = false; - for (auto i = m_sessionMap.begin(); i != m_sessionMap.end(); ++i) - { - if (i.value() == session) - { - const auto key = i.key(); - session->aboutToClose(); - session->setInactive(); - session->deleteLater(); - m_sessionMap.remove(key); - - found = true; - break; - } - } - - if (!found) - FATAL_LOG(m_logger) << "LtsSessionManager: Cannot find Session in map!"; - - emit hideAllOtherBLWs(nullptr); - - if (m_sessionMap.isEmpty()) - emit signalQuit(); -} - - -LtsSession *LtsSessionManager::getSession(IDL::PartitionType partitionType, int partitionId) -{ - auto it = m_sessionMap.find(SessionKey(partitionType, partitionId)); - - if (it != m_sessionMap.end()) - return it.value(); - - return nullptr; -} - -} diff --git a/Lts/Common/LtsSession/lts_session_manager.h b/Lts/Common/LtsSession/lts_session_manager.h deleted file mode 100755 index 74477c3c7..000000000 --- a/Lts/Common/LtsSession/lts_session_manager.h +++ /dev/null @@ -1,106 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -namespace NGL -{ -class INGLLogger; -} -namespace DeveloperTools -{ -class AppWidget; -} - - -namespace LtsSession -{ -class LtsSession; -struct DdsConnectionParams; - -class LTSSESSION_EXPORT LtsSessionManager : public QObject -{ - Q_OBJECT - -public: - LtsSessionManager(); - ~LtsSessionManager(); - - - LtsSession *createSession(const DdsConnectionParams &connParams, - bool hasBaseLayout = true, - bool payloads = true, - bool hidden = false); - - - LtsSession *getSession(IDL::PartitionType partitionType, int partitionId); - bool init(const DdsConnectionParams &connParams, - const QVector &dataPluginList, - const QVector &guiPluginList, - LtsAppDescriptor::App appId, const QString &workspaceId, - bool headless, bool hidden); - int sessionCount() const { return m_sessionMap.count(); } - LtsAppDescriptor::App appId() const { return m_appId; } - bool isHeadless() const { return m_headless; } - QVector sessions(); - void softQuit(); - -private: - friend class DeveloperTools::DevDialog; - friend class DeveloperTools::AppWidget; - - struct SessionKey - { - SessionKey(IDL::PartitionType pt = IDL::PartitionType::Any, int pId = 0) - : partitionType(pt) - , partitionId(pId) - {} - IDL::PartitionType partitionType; - int partitionId; - - bool operator<(const SessionKey &o) const - { - if (partitionType != o.partitionType) - return partitionType < o.partitionType; - return partitionId < o.partitionId; - } - }; - - void logout(); - void addPayloads(LtsSession *session); - - QMap m_sessionMap; - QVector m_dataPluginList; - QVector m_guiPluginList; - LtsAppDescriptor::App m_appId; - bool m_headless; - NGL::INGLLogger &m_logger; - -private slots: - void slotCloseSession(); - -signals: - - - void signalStopBlock(); - void signalLogout(); - void signalQuit(); - void signalInitializationFinished(); - void signalInitializationAborted(); - void hideAllOtherBLWs(QObject *caller); - void taskSwitch(QObject *caller); -}; - -} diff --git a/Lts/Common/LtsTimeline/CMakeLists.txt b/Lts/Common/LtsTimeline/CMakeLists.txt deleted file mode 100755 index 2c6d092b7..000000000 --- a/Lts/Common/LtsTimeline/CMakeLists.txt +++ /dev/null @@ -1,74 +0,0 @@ -# -# CMakeLists.txt for LtsTimeline, the base for EventTimeline and ScriptTimeline aka. Script Editor -# -# $Id: CMakeLists.txt 39615 2018-03-16 13:36:41Z wille_m $ -# $HeadURL: svn://192.168.133.21/TDS/branches/product/features/hotline/Lts/Common/LtsEventTimeline/CMakeLists.txt $ -# $Author: wille_m $ -# -# Created by Mike Wille, SLTE, 2021-05-18 -# - -SET(src - timeline_model.h - timeline_model.cpp - - item.h - item.cpp - item_delegate.h - item_delegate.cpp - - timeline_header_view.cpp - timeline_header_view.h - timeline_header_view_mouse_handler.cpp - timeline_header_view_mouse_handler.h - timeline_utils.cpp - timeline_utils.h - timeline_view.h - timeline_view.cpp - timeline_view_drag_handler.cpp - timeline_view_drag_handler.h - timeline_view_resize_handler.cpp - timeline_view_resize_handler.h - timeline_view_selection_handler.cpp - timeline_view_selection_handler.h - timeline_view_track_handler.cpp - timeline_view_track_handler.h - timeline_widget.h - timeline_widget.cpp - - track.h - track.cpp -) - -SET(uisrc - timeline_widget.ui -) - -NGL_WRAP_UI(src ${uisrc}) -NGL_ADD_LIBRARY2(WS LtsTimeline SHARED ${src}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsTimeline - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsTimeline - PRIVATE - NGLLogger - ngltools # DTG - LtsWidgets # LtsMessageDialogNew for remove filter track - Qt5::Core - Qt5::Gui - Qt5::Widgets -) -NGL_INSTALL_TARGET(LtsTimeline) - - -# LtsTimelineTest -NGL_ADD_EXECUTABLE(WS LtsTimelineTest main.cpp) -NGL_TARGET_LINK_LIBRARIES(LtsTimelineTest - LtsTimeline - nglapplication - NGLLogger - Qt5::Core - Qt5::Gui - Qt5::Widgets -) diff --git a/Lts/Common/LtsTimeline/item.cpp b/Lts/Common/LtsTimeline/item.cpp deleted file mode 100755 index 2eec74152..000000000 --- a/Lts/Common/LtsTimeline/item.cpp +++ /dev/null @@ -1,45 +0,0 @@ - - - -#include "item.h" - -#include -#include -#include -#include -#include - -#include - - -namespace LtsTimeline -{ - -QString Item::mimetype = QStringLiteral("application/rheinmetall.timeline.item"); - -QColor Item::s_highlightColor = QRgb(0x43b0e0); - - -Item::Item(qint64 startTime, int duration, const QColor &color, bool isEditable) - : m_color(color) - , m_startTime(startTime) - , m_duration(duration) - , m_isEditable(isEditable) -{} - - - - -void Item::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &, bool highlight) const -{ - painter->save(); - if (highlight) - painter->setPen(QPen(QBrush(s_highlightColor), 5)); - else - painter->setPen(QPen(QBrush(Qt::black), 1)); - painter->setBrush(QBrush(m_color)); - painter->drawRect(option.rect); - painter->restore(); -} - -} diff --git a/Lts/Common/LtsTimeline/item.h b/Lts/Common/LtsTimeline/item.h deleted file mode 100755 index 955c56e20..000000000 --- a/Lts/Common/LtsTimeline/item.h +++ /dev/null @@ -1,87 +0,0 @@ - - - -#pragma once - -#include - -#include - -#include -#include - - -class QPainter; -class QStyleOptionViewItem; -class QModelIndex; - -namespace PresentationManagement -{ -class ScriptModel; -} - - -namespace LtsTimeline -{ - -class ItemDelegate; - - -class LTSTIMELINE_EXPORT Item -{ - friend class ItemDelegate; - -public: - static QString mimetype; - static QColor s_highlightColor; - - Item() = default; - Item(qint64 startTime, int duration, const QColor &color, bool isEditable = true); - Item(const Item &) = delete; - Item &operator=(const Item &) = delete; - Item(Item &&) noexcept = default; - virtual ~Item() = default; - - int id() const { return m_id; } - int parentTrackId() const { return m_parentTrackId; } - - QColor color() const { return m_color; } - void setColor(const QColor &color) { m_color = color; } - - qint64 startTime() const { return m_startTime; } - [[deprecated("use model's setItemStartTime instead, needed to notify the model that it has to re-sort items")]] - void setStartTime(qint64 startTime) { m_startTime = startTime; } - - int duration() const { return m_duration; } - void setDuration(int duration) { m_duration = duration; } - - bool isEditable() const { return m_isEditable; } - void setEditable(bool editable) { m_isEditable = editable; } - - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, bool highlight = false) const; - - void _setStartTime(qint64 startTime) { m_startTime = startTime; } - void _setDuration(int duration) { m_duration = duration; } - -protected: - QColor m_color; - qint64 m_startTime { -1 }; - int m_duration { -1 }; - bool m_isEditable { false }; - -private: - friend class TimelineModel; - friend class PresentationManagement::ScriptModel; - - int m_id { -1 }; - int m_parentTrackId { -1 }; -}; - -typedef std::unique_ptr ItemPtr; -typedef std::vector Items; -typedef std::reference_wrapper ItemPtrRef; -typedef std::reference_wrapper ItemPtrNCVRef; -typedef std::vector ItemPtrNCVRefs; -typedef std::vector ItemPtrRefs; - -} diff --git a/Lts/Common/LtsTimeline/item_delegate.cpp b/Lts/Common/LtsTimeline/item_delegate.cpp deleted file mode 100755 index 51aeb4b31..000000000 --- a/Lts/Common/LtsTimeline/item_delegate.cpp +++ /dev/null @@ -1,185 +0,0 @@ - - - -#include "item_delegate.h" -#include "item.h" -#include "track.h" -#include "timeline_view.h" - -#include -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -ItemDelegate::ItemDelegate(TimelineView *parent) - : QStyledItemDelegate(parent) - , m_view(parent) - , m_pushButton(new QPushButton) - , m_pushButtonPressed(new QPushButton) -{ - m_pushButtonPressed->setDown(true); -} - -ItemDelegate::~ItemDelegate() -{ - m_pushButton->deleteLater(); - m_pushButton = nullptr; - m_pushButtonPressed->deleteLater(); - m_pushButtonPressed = nullptr; -} - - - - -QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - Q_UNUSED(option); - Q_UNUSED(index); - return QSize(100, 50); -} - -void ItemDelegate::paintDragItems(QPainter *painter) const -{ - painter->save(); - - const QRect rightSectionRect = QRect(QPoint(m_view->m_header->sectionPosition(Section::eItems), 0), - QPoint(m_view->geometry().right(), m_view->geometry().bottom())); - painter->setClipRect(rightSectionRect); - - int dx = m_view->m_dragHandler->deltaX(); - int dy = m_view->m_dragHandler->deltaY(); - try - { - for (const int &itemId : m_view->m_dragHandler->m_dragItemIds) - { - Item &item = m_view->timelineModel()->item(itemId); - if (item.startTime() + item.duration() < m_view->currentTime()) - continue; - - if (item.startTime() > m_view->endTime()) - break; - - - - const QModelIndex rowIndex(m_view->timelineModel()->indexOfTrack(item.parentTrackId())); - const QModelIndex columnIndex(m_view->timelineModel()->index(rowIndex.row(), 1, rowIndex.parent())); - const QRect visualRect(m_view->visualRect(columnIndex)); - int left = m_view->toPixel(item.startTime()) + dx; - int right = m_view->toPixel(item.startTime() + item.duration()) + dx; - int top = visualRect.top() + dy; - int bottom = visualRect.bottom() + dy; - QStyleOptionViewItem option; - option.rect = QRect(left, top, right - left - 1, bottom - top); - item.paint(painter, option, columnIndex, m_view->selectedItemIds().contains(itemId)); - } - } - catch (...) {} - - painter->restore(); -} - - -void ItemDelegate::paintTrackButtons(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - try - { - - const Track &track = m_view->timelineModel()->track(index.data(Qt::UserRole).toInt()); - - int buttonLength = option.rect.height() / 2; - if (track.hasInsertButton()) - { - QPushButton *button = m_view->m_trackHandler->isInsertButtonPressed(index) ? m_pushButtonPressed : m_pushButton; - QRect rect = QRect(0, 0, buttonLength, buttonLength); - button->setGeometry(rect); - button->setText(QStringLiteral("+")); - button->setEnabled(m_view->timelineModel()->isEditable()); - QPixmap map = button->grab(); - painter->drawPixmap(option.rect.right() - buttonLength, option.rect.top(), map); - } - if (track.hasRemoveButton()) - { - QPushButton *button = m_view->m_trackHandler->isRemoveButtonPressed(index) ? m_pushButtonPressed : m_pushButton; - QRect rect = QRect(0, 0, buttonLength, buttonLength); - button->setGeometry(rect); - button->setText(QStringLiteral("-")); - button->setEnabled(m_view->timelineModel()->isEditable()); - QPixmap map = button->grab(); - painter->drawPixmap(option.rect.right() - buttonLength, option.rect.center().y(), map); - } - } - catch (...) {} -} - - - - -void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (index.column() == Section::eActuator) - { - painter->save(); - QStyledItemDelegate::paint(painter, option, index); - paintTrackButtons(painter, option, index); - painter->restore(); - return; - } - - auto drawItem = [this, &option, &painter, &index](const Item &item) - { - - - - - - - if (m_view->timelineModel()->isEditable()) - if (m_view->m_dragHandler->m_dragItemIds.contains(item.id())) - return; - - QStyleOptionViewItem opt = option; - opt.rect.setLeft(std::max(m_view->toPixel(item.startTime()), option.rect.left())); - opt.rect.setRight(std::min(m_view->toPixel(item.startTime() + item.duration()), option.rect.right()) - 1); - item.paint(painter, opt, index, m_view->selectedItemIds().contains(item.id())); - }; - - try - { - const Track &track = m_view->timelineModel()->track(index.data().toInt()); - painter->save(); - - - painter->setClipping(true); - painter->setClipRect(option.rect); - - const Items &items = track.items(); - for (const ItemPtr &item : items) - drawItem(static_cast(*item.get())); - const QSet copyItemIds = track.copyItemIds(); - for (const int &itemId : copyItemIds) - { - try - { - const Item &item = m_view->timelineModel()->item(itemId); - drawItem(item); - } - catch(...) - { - fprintf(stderr, "ItemDelegate::paint: cannot find item copy of %d\n", itemId); - } - } - - painter->restore(); - } - catch (...) - { - ERROR_LOG(m_view->m_logger) << "ItemDelegate::paint -> CATCH"; - } -} - -} diff --git a/Lts/Common/LtsTimeline/item_delegate.h b/Lts/Common/LtsTimeline/item_delegate.h deleted file mode 100755 index b7aa26af8..000000000 --- a/Lts/Common/LtsTimeline/item_delegate.h +++ /dev/null @@ -1,47 +0,0 @@ - - - -#pragma once - -#include - -#include -#include - - -namespace LtsTimeline -{ - -class TimelineView; -class TimelineModel; - - -class LTSTIMELINE_EXPORT ItemDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - ItemDelegate(TimelineView *parent = nullptr); - ~ItemDelegate(); - - - - QSize sizeHint(const QStyleOptionViewItem &option = {}, const QModelIndex &index = {}) const override; - - - - virtual void paintDragItems(QPainter *painter) const; - virtual void paintTrackButtons(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - -protected: - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - -protected: - TimelineView *m_view { nullptr }; - - QPushButton *m_pushButton { nullptr }; - QPushButton *m_pushButtonPressed { nullptr }; - -}; - -} diff --git a/Lts/Common/LtsTimeline/log4cxx_timeline.props b/Lts/Common/LtsTimeline/log4cxx_timeline.props deleted file mode 100755 index 499ebc0d3..000000000 --- a/Lts/Common/LtsTimeline/log4cxx_timeline.props +++ /dev/null @@ -1,66 +0,0 @@ -log4j.reset=true -# exclusivly for timelinetest build - - -log4j.rootLogger=ERROR, file -#log4j.rootLogger=WARN, stdout - -#log4j.logger.Timeline.EventInfoWindow=DEBUG -#log4j.logger.Timeline.EventTools=DEBUG -#log4j.logger.Timeline=WARN - - -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.MaxFileSize=1MB -log4j.appender.file.MaxBackupIndex=9 -log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_timeline.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n - -# Appnd_Console -########################################################### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n - -########################################################### -########################################################### -########################################################### -# 1) Available Levels in LOG4CXX Version 0.10.0: -# TRACE, -# DEBUG, -# INFO, -# WARN, -# ERROR -# FATAL -# -# d = date/time -# p = priority (Level) -# c = category/channel -# m = message -# F = file -# L = line -# n = platform dependent line separator character or characters. -# .... -# -# Examples for console: -# a) -# %d %p %c #%m# (%.50F:%L)%n -# date/time -# priority -# category/channel -# #message# -# (Maximal 50Character des Filename von rechts:Line) -# line separator -# -# b) -# %d (%.15F:%L) %-5p %c |#| %m%n -# date/time -# (Maximal 15Character des Filename von rechts:Line) -# priority, Right pad with spaces if the priority name is less than 5 characters long. -# category/channel -# message -# line separator -# -# Examples for files: - diff --git a/Lts/Common/LtsTimeline/main.cpp b/Lts/Common/LtsTimeline/main.cpp deleted file mode 100755 index 36556030d..000000000 --- a/Lts/Common/LtsTimeline/main.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include -#include - - - - - - - - - - - - - -using namespace LtsTimeline; - - - - - - - -class AbstractStuff -{ -public: - virtual void thing() = 0; -}; - - -class TheStuff : public AbstractStuff -{ - void thing() override { fprintf(stderr, "thing() !\n"); } -}; - - -std::unique_ptr get() -{ - return std::make_unique(std::move(TheStuff())); -} - - -std::vector> test; - - -void move(std::unique_ptr &&stuff) -{ - test.push_back(std::move(stuff)); -} - - - - - -void populateModel(LtsTimeline::TimelineModel *model) -{ - { - std::unique_ptr asp = get(); - move(std::move(asp)); - } - - { - AbstractStuff &as = *test.front(); - as.thing(); - } - - - - -#define l() qCritical() - qint64 rtlnau = QDateTime::currentMSecsSinceEpoch(); - - class TestItem : public Item - { - public: - TestItem(qint64 startTime, int duration, const QColor &color, bool isEditable = true) - : Item(startTime, duration, color, isEditable) - , m_1234(1234) - {} - - int get1234() const { return m_1234; } - - private: - int m_1234 { 9876 }; - }; - - - int trackId; - int itemId; - - - - - - model->addTrack(0, Track(QStringLiteral("1st Track"))); - model->addItem(1, Item(rtlnau + 120'000, 30'000, Qt::green)); - - int firstChildId = - model->addTrack(1, Track(QStringLiteral("1st Track's 1st Child"))); - model->addItem(2, Item(rtlnau + 180'000, 30'000, Qt::magenta)); - l() << "firstChildId:" << firstChildId; - - model->addTrack(0, Track(QStringLiteral("2nd Track"))); - model->addItem(3, Item(rtlnau + 120'000, 60'000, Qt::blue)); - model->addItem(3, Item(rtlnau + 60'000, 30'000, Qt::red)); - - model->addTrack(0, Track(QStringLiteral("3rd Track"))); - model->addItem(4, Item(rtlnau + 150'000, 60'000, Qt::yellow)); - model->addItem(4, TestItem(rtlnau + 90'000, 30'000, Qt::cyan)); - - - - model->print(true); - - - - try - { - const TestItem &testItem = dynamic_cast(model->item(5)); - - } - catch(const std::exception &ex) - { - l() << "dynamic_cast back to TestItem failed!" << ex.what(); - } - - - (void)itemId; - (void)trackId; - - - - qint64 now = rtlnau; - - { -#define non_strict false -#define strict true -#define recursive true -#define sorted true -#define unsorted false - QVector x; - bool y; - - - - x = model->itemIdsBetween(now + 60'000 + 1, now + 150'000 - 1, non_strict, recursive); - - - x = model->itemIdsBetween(now + 60'000 + 1, now + 150'000 - 1, strict, recursive); - - - - y = model->isItemsBetween(now + 60'000 + 1, now + 150'000 - 1, -1, non_strict, recursive); - - - y = model->isItemsBetween(now + 60'000 + 1, now + 150'000 - 1, -1, strict, recursive); - - - - y = model->isItemsBetween(now + 60'000 + 1, now + 150'000 - 1, 5, non_strict, recursive); - - - y = model->isItemsBetween(now + 60'000 + 1, now + 150'000 - 1, 5, strict, recursive); - - - - - - x = model->itemIdsBetween(now + 60'000, now + 150'000, non_strict, recursive); - - - x = model->itemIdsBetween(now + 60'000, now + 150'000, strict, recursive); - - - - y = model->isItemsBetween(now + 60'000, now + 150'000, -1, non_strict, recursive); - - - y = model->isItemsBetween(now + 60'000, now + 150'000, -1, strict, recursive); - - - - y = model->isItemsBetween(now + 60'000, now + 150'000, 5, non_strict, recursive); - - - y = model->isItemsBetween(now + 60'000, now + 150'000, 5, strict, recursive); - - - - y = model->isItemsBetween(now + 60'000, now + 150'000, QVector({ 0, 3, 5 }), strict, recursive); - - - y = model->isItemsBetween(now + 60'000, now + 150'000, QVector({ 0, 5 }), strict, recursive); - - - - - x = model->itemIdsBetween(0, now + 999'000, non_strict, recursive, sorted); - // l() << "itemIdsBetween(0, now + 999'000, non_strict, recursive, sorted)" << x << "- okay?" << (x == QVector({ 3, 5, 0, 2, 4, 1 })); -#undef non_strict -#undef strict -#undef recursive -#undef unsorted -#undef sorted - } - - - try - { - // item 1 von track 2 auf track 3, zu startTime 210'000 - model->moveItemToTrack(1, 3, now + 210'000); - } - catch(...) - { - fprintf(stderr, "That didn't work. %s:%d\n", __FUNCTION__, __LINE__); - } - - - model->print(true); - - - try - { - // item 3 von track 3 auf track 2, zu startTime 150'000 - model->moveItemToTrack(3, 2, now + 150'000); - } - catch(...) - { - fprintf(stderr, "That didn't work. %s:%d\n", __FUNCTION__, __LINE__); - } - - - /* now - /30 /60 /90 /120 /150 /180 /210 /240 - | ' ' ' | ' ' ' | ' ' ' | ' ' ' | ' ' ' | ' ' ' | ' ' ' | - T1 000000000 - >T2 333333333 - T3 2222222222222222111111111 - T4 55555555 44444444444444444 - */ - - - model->print(true); - - -#undef l -} - - -int main(int argc, char *argv[]) -{ -// qputenv("TDS_DEVEL", "yes, of course"); - - qWarning() << "LtsTimeline - Test"; - NGLApplication app(argc, argv); - app.createLogger("Timeline", "log4cxx_timeline.props"); - - STARTUP_LOG(app.baseLogger()); - - int ret = 0; - - LtsTimeline::TimelineWidget *timeline = new LtsTimeline::TimelineWidget(); - LtsTimeline::TimelineModel *model = new LtsTimeline::TimelineModel(timeline->view()); - model->setSnapEnabled(true); - populateModel(model); - timeline->setModel(model); - timeline->showDebugWidget(); - timeline->setWindowState(Qt::WindowMaximized); - timeline->show(); - -// timeline->setMaximumZoomFactor(LtsTimeline::ZoomFactor::ThirtyMinutes); - timeline->setZoomFactor(LtsTimeline::ZoomFactor::SixtyMinutes); - timeline->setStartTime(QDateTime::currentMSecsSinceEpoch()); - timeline->setDuration(12 * 60 * 60 * 1000); - - ret = app.exec(); - - SHUTDOWN_LOG(app.baseLogger()); - - return ret; -} diff --git a/Lts/Common/LtsTimeline/timeline_header_view.cpp b/Lts/Common/LtsTimeline/timeline_header_view.cpp deleted file mode 100755 index 6d9d4688f..000000000 --- a/Lts/Common/LtsTimeline/timeline_header_view.cpp +++ /dev/null @@ -1,679 +0,0 @@ -#include "timeline_header_view.h" -#include "timeline_view.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -QPixmap getMinLimitIndicator() -{ - QPixmap pixmap(11, 11); - pixmap.fill(Qt::transparent); - QPolygon triangle; - triangle << QPoint(4, 5); - triangle << QPoint(10, 0); - triangle << QPoint(10, 10); - triangle << QPoint(4, 5); - QPainter painter(&pixmap); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - painter.setPen(QGuiApplication::palette().color(QPalette::Text)); - painter.setBrush(QBrush(QGuiApplication::palette().color(QPalette::Text))); - painter.drawLine(1, 0, 1, 10); - painter.drawConvexPolygon(triangle); - return pixmap; -} - -QPixmap getMaxLimitIndicator() -{ - QPixmap pixmap(11, 11); - pixmap.fill(Qt::transparent); - QPolygon triangle; - triangle << QPoint(6, 5); - triangle << QPoint(0, 0); - triangle << QPoint(0, 10); - triangle << QPoint(6, 5); - QPainter painter(&pixmap); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - painter.setPen(QGuiApplication::palette().color(QPalette::Text)); - painter.setBrush(QBrush(QGuiApplication::palette().color(QPalette::Text))); - painter.drawLine(10, 0, 10, 10); - painter.drawConvexPolygon(triangle); - return pixmap; -} - - -const QColor TimelineHeaderView::backgroundColor() -{ - return QGuiApplication::palette().color(QPalette::Base); -} - -const QColor TimelineHeaderView::highlightColor() -{ - return QRgb(0x43b0e0); -} - - -const int TimelineHeaderView::s_limitHeight = 22; - - -TimelineHeaderView::TimelineHeaderView(QWidget *parent) - : QHeaderView(Qt::Horizontal, parent) - , m_mouseHandler(new TimelineHeaderViewMouseHandler(this)) - , m_liveTimer(new QTimer(this)) - , m_minLimitIndicator(getMinLimitIndicator()) - , m_maxLimitIndicator(getMaxLimitIndicator()) -{ - const QColor color = QGuiApplication::palette().color(QPalette::Inactive, QPalette::Highlight); - setStyleSheet(QStringLiteral("QTreeView { background-color: %1 }").arg(color.name(QColor::HexRgb))); - try - { - m_timelineView = qobject_cast(parent); - updateSection(Section::eActuator); - updateSection(Section::eItems); - if (m_timelineView) - { - connect(m_timelineView, &TimelineView::currentTimeChanged, this, &TimelineHeaderView::onUpdateTicks); - connect(m_timelineView, &TimelineView::rangeChanged, this, &TimelineHeaderView::onUpdateTicks); - connect(m_timelineView, &TimelineView::zoomChanged, this, &TimelineHeaderView::onUpdateTicks); - } - else - qWarning() << "WARNING: main functionalities are inoperable without a TimelineView as parent."; - } - catch (...) - { - qWarning() << "WARNING: main functionalities are inoperable without a TimelineView as parent."; - } - setStretchLastSection(true); - setMinimumSectionSize(200); - setFixedHeight(50); - connect(this, &TimelineHeaderView::sectionResized, this, &TimelineHeaderView::onUpdateTicks); - connect(m_liveTimer, &QTimer::timeout, this, &TimelineHeaderView::onLiveTimeout); - - m_liveIndicatorColor = QGuiApplication::palette().color(QPalette::Link); -} - -TimelineHeaderView::~TimelineHeaderView() -{ - m_timelineView = nullptr; -} - - - - -void TimelineHeaderView::setParent(QWidget *) -{ - qWarning() << QStringLiteral("WARNING: setting parent after construction is not allowed."); -} - -void TimelineHeaderView::setTrackLabel(const QString &trackLabel) -{ - m_trackLabel = trackLabel; - updateSection(Section::eActuator); -} - - -void TimelineHeaderView::setDateTimeFormat(DateTimeFormat dateTimeFormat) -{ - if (!m_isDateTimeFormatChangable) - return; - - m_dateTimeFormat = dateTimeFormat; - for (Tick &majorTick : m_majorTicks) - majorTick.label = DateTime::toString(majorTick.time, m_dateTimeFormat); - for (Tick &mediumTick : m_mediumTicks) - mediumTick.label = DateTime::toString(mediumTick.time, m_dateTimeFormat); - for (Tick &minorTick : m_minorTicks) - minorTick.label = DateTime::toString(minorTick.time, m_dateTimeFormat); - updateSection(Section::eItems); -} - - -void TimelineHeaderView::setIndicatorScalingFactor(double factor) -{ - if (factor == m_indicatorScalingFactor || factor <= 0.) - return; - - m_indicatorScalingFactor = factor; - viewport()->update(); -} - -void TimelineHeaderView::setIndicatorTime(qint64 time) -{ - if (time != -1 && m_timelineView) - if (m_timelineView->timelineModel()) - if (m_timelineView->timelineModel()->isSnapEnabled()) - time = m_timelineView->timelineModel()->snapTime(time); - - - - - if (time == m_indicatorTime) - return; - - m_indicatorTime = time; - viewport()->update(); - emit indicatorTimeChanged(m_indicatorTime); -} - -void TimelineHeaderView::setIndicatorPosition(int position) -{ - setIndicatorTime(toTime(position)); -} - -void TimelineHeaderView::setIndicatorVisible(bool isIndicatorVisible) -{ - if (isIndicatorVisible == m_isIndicatorVisible) - return; - - m_isIndicatorVisible = isIndicatorVisible; - m_timelineView->viewport()->update(); - emit indicatorVisibilityChanged(m_isIndicatorVisible); -} - - -void TimelineHeaderView::setLiveIndicatorScalingFactor(double factor) -{ - if (factor == m_liveIndicatorScalingFactor || factor <= 0.) - return; - - m_liveIndicatorScalingFactor = factor; - viewport()->update(); -} - -void TimelineHeaderView::setLiveIndicatorColor(const QColor &color) -{ - if (color == m_liveIndicatorColor) - return; - - m_liveIndicatorColor = color; - viewport()->update(); -} - -void TimelineHeaderView::setLiveIndicatorTime(qint64 time) -{ - if (time != -1 && m_timelineView) - if (m_timelineView->timelineModel()) - if (m_timelineView->timelineModel()->isSnapEnabled()) - time = m_timelineView->timelineModel()->snapTime(time); - - if (time == m_liveIndicatorTime) - return; - - m_liveIndicatorTime = time; - viewport()->update(); - emit liveIndicatorTimeChanged(m_liveIndicatorTime); -} - -void TimelineHeaderView::setLiveIndicatorPosition(int position) -{ - setLiveIndicatorTime(toTime(position)); -} - -void TimelineHeaderView::setLiveIndicatorVisible(bool isLiveIndicatorVisible) -{ - if (isLiveIndicatorVisible == m_isLiveIndicatorVisible) - return; - - m_isLiveIndicatorVisible = isLiveIndicatorVisible; - viewport()->update(); - m_timelineView->viewport()->update(); - emit liveIndicatorVisibilityChanged(m_isLiveIndicatorVisible); -} - -void TimelineHeaderView::startLiveTimer(qint64 startTime, int interval) -{ - if (interval <= 0) - { - qCritical() << LOGFUNCTION << "invalid time interval:" << interval; - return; - } - - if (m_liveTimer->isActive()) - { - qWarning() << LOGFUNCTION << "timer is already active - restarting"; - m_liveTimer->stop(); - } - - if (startTime < 0) - startTime = m_timelineView->currentTime(); - - m_liveTimerInterval = interval; - setLiveIndicatorTime(startTime); - setLiveIndicatorVisible(true); - m_liveTimer->setSingleShot(false); - m_liveTimer->start(m_liveTimerInterval); -} - -void TimelineHeaderView::stopLiveTimer() -{ - if (!m_liveTimer->isActive()) - return; - - qDebug() << LOGFUNCTION; - m_liveTimer->stop(); - m_liveIndicatorTime = -1; - setLiveIndicatorVisible(false); -} - - -void TimelineHeaderView::setHoverTime(qint64 time) -{ - if (!m_isHoverVisible) - return; - - if (time < m_timelineView->currentTime() || m_timelineView->currentTime() + m_timelineView->zoomFactor() < time) - time = -1; - - m_hoverTime = time; - m_hoverPosition = toPixel(m_hoverTime); - updateSection(Section::eItems); - emit hoverPositionChanged(m_hoverPosition); -} - -void TimelineHeaderView::setHoverPosition(int position) -{ - if (!m_isHoverVisible) - return; - - m_hoverPosition = position; - m_hoverTime = toTime(m_hoverPosition); - updateSection(Section::eItems); - emit hoverPositionChanged(m_hoverPosition); -} - -void TimelineHeaderView::setHoverVisible(bool visible) -{ - if (visible == m_isHoverVisible) - return; - - m_isHoverVisible = visible; - updateSection(Section::eItems); - m_timelineView->viewport()->update(); - emit hoverVisibilityChanged(m_isHoverVisible); -} - -void TimelineHeaderView::setHoverTimeVisible(bool visible) -{ - if (visible == m_isHoverTimeVisible) - return; - - m_isHoverTimeVisible = visible; - updateSection(Section::eItems); - m_timelineView->viewport()->update(); -} - -void TimelineHeaderView::setHoverDateTimeFormat(DateTimeFormat format) -{ - if (format == m_hoverDateTimeFormat) - return; - - m_hoverDateTimeFormat = format; - updateSection(Section::eItems); - m_timelineView->viewport()->update(); -} - - -void TimelineHeaderView::setLimitsVisible(bool visible) -{ - if (visible == m_isLimitsVisible) - return; - - m_isLimitsVisible = visible; - if (m_isLimitsVisible) - setFixedHeight(70); - else - setFixedHeight(50); - viewport()->update(); -} - -void TimelineHeaderView::setLimitsDateTimeFormat(DateTimeFormat format) -{ - if (format == m_limitsDateTimeFormat) - return; - - m_limitsDateTimeFormat = format; - viewport()->update(); -} - - -int TimelineHeaderView::toPixel(qint64 time) const -{ - if (time == -1) - return -1; - - return static_cast(double(time - m_timelineView->currentTime()) / m_timelineView->zoomFactor() * - (geometry().width() - sectionPosition(Section::eItems)) + sectionPosition(Section::eItems)); -} - -qint64 TimelineHeaderView::toTime(int pixel) const -{ - return m_timelineView->currentTime() + - (m_timelineView->zoomFactor() * (double(pixel - sectionPosition(Section::eItems)) / - (geometry().width() - sectionPosition(Section::eItems)))); -} - - - - -void TimelineHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const -{ - if (logicalIndex == Section::eActuator) - { - painter->save(); - painter->setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing); - painter->setPen(backgroundColor()); - painter->setBrush(QBrush(backgroundColor())); - painter->drawRect(rect); - painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 2)); - painter->drawLine(rect.topRight(), QPoint(rect.right() - rect.height() / 2, rect.top() + rect.height() / 2)); - painter->drawLine(QPoint(rect.right() - rect.height() / 2, rect.top() + rect.height() / 2), rect.bottomRight()); - painter->setFont(m_font); - painter->drawText(rect, Qt::AlignCenter, m_trackLabel); - painter->restore(); - return; - } - - painter->save(); - - painter->setClipping(true); - painter->setClipRect(rect); - painter->setFont(m_font); - painter->setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing); - painter->setPen(backgroundColor()); - painter->setBrush(backgroundColor()); - painter->drawRect(rect); - - int bottom = m_isLimitsVisible ? rect.bottom() - s_limitHeight : rect.bottom(); - int height = (bottom - rect.top()) / 2; - - - for (const Tick &tick : qAsConst(m_majorTicks)) - { - painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1)); - painter->drawText(QRect(tick.pos - 160 / 2, rect.top(), 160, height), Qt::AlignCenter, tick.label); - if (m_isLimitsVisible) - painter->drawLine(tick.pos, bottom, tick.pos, rect.bottom()); - painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1)); - painter->drawLine(tick.pos, rect.top() + height, tick.pos, bottom); - } - - painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1)); - - - for (const Tick &tick : qAsConst(m_mediumTicks)) - painter->drawLine(tick.pos, bottom - static_cast(double(height) * 2 / 3), tick.pos, bottom); - - - for (const Tick &tick : qAsConst(m_minorTicks)) - painter->drawLine(tick.pos, bottom - static_cast(double(height) / 3), tick.pos, bottom); - - - if (m_isLimitsVisible) - { - QString minLimitString = DateTime::toString(m_timelineView->m_currentTime, m_limitsDateTimeFormat); - QString maxLimitString = DateTime::toString(m_timelineView->m_currentTime + m_timelineView->m_zoomFactor, m_limitsDateTimeFormat); - int minLimitWidth = painter->fontMetrics().horizontalAdvance(minLimitString); - int maxLimitWidth = painter->fontMetrics().horizontalAdvance(maxLimitString); - painter->setPen(backgroundColor()); - painter->setBrush(QBrush(backgroundColor())); - painter->drawRect(rect.left() + 4, geometry().bottom() - s_limitHeight, minLimitWidth + 11, 19); - painter->drawRect(rect.right() - maxLimitWidth - 18, geometry().bottom() - s_limitHeight, maxLimitWidth + 11, 19); - painter->setPen(QGuiApplication::palette().color(QPalette::Text)); - painter->drawPixmap(rect.left() + 4, geometry().bottom() - 15, 11, 11, m_minLimitIndicator); - painter->drawPixmap(rect.right() - 15, geometry().bottom() - 15, 11, 11, m_maxLimitIndicator); - painter->drawText( - QRect(rect.left() + 18, geometry().bottom() - s_limitHeight, minLimitWidth, s_limitHeight - 1), - Qt::AlignLeft, minLimitString - ); - painter->drawText( - QRect(rect.right() - maxLimitWidth - 18, geometry().bottom() - s_limitHeight, maxLimitWidth, s_limitHeight - 1), - Qt::AlignRight, maxLimitString - ); - } - - - if (m_isHoverVisible) - { - if (m_hoverPosition != -1) - { - painter->setPen(highlightColor()); - painter->drawLine(m_hoverPosition, rect.top(), m_hoverPosition, rect.bottom()); - if (m_isHoverTimeVisible) - { - QString dateTimeString = DateTime::toString(m_hoverTime, m_hoverDateTimeFormat); - - - - const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString) + 2; - const int textHeight = m_isLimitsVisible ? s_limitHeight - 1 : 19; - QRect dateTimeRect(m_hoverPosition, geometry().bottom() - textHeight, textWidth, textHeight - 1); - if (dateTimeRect.right() >= geometry().right()) - { - dateTimeRect.setLeft(m_hoverPosition - textWidth); - dateTimeRect.setWidth(textWidth); - } - painter->setBrush(QBrush(backgroundColor())); - painter->drawRect(dateTimeRect); - painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString); - } - } - } - - - if (m_isIndicatorVisible) - { - int pos = toPixel(m_indicatorTime); - if (m_indicatorTime != -1 && (m_timelineView->currentTime() <= m_indicatorTime && - m_indicatorTime <= m_timelineView->currentTime() + m_timelineView->zoomFactor())) - { - painter->setPen(highlightColor()); - painter->setBrush(QBrush(highlightColor())); - int sideLength = double(m_isLimitsVisible ? rect.height() - height : rect.height()) * m_indicatorScalingFactor; - int top = bottom - static_cast(0.866 * sideLength); - QPolygon polygon; - polygon << QPoint(pos, bottom); - polygon << QPoint(pos + sideLength / 2, top); - polygon << QPoint(pos - sideLength / 2, top); - polygon << QPoint(pos, bottom); - QString dateTimeString = DateTime::toString(m_indicatorTime, m_hoverDateTimeFormat); - const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString); - painter->setClipRect(rect.adjusted(-textWidth / 2, 0, 0, 0)); - painter->drawPolygon(polygon); - if (m_isLimitsVisible) - painter->drawLine(pos, bottom, pos, rect.bottom()); - QRect dateTimeRect(pos - textWidth / 2, geometry().top(), textWidth + 2, 19); - painter->setBrush(QBrush(backgroundColor())); - painter->drawRect(dateTimeRect); - painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString); - painter->drawLine(pos, geometry().top() + 19, pos, bottom); - } - } - - - if (m_isLiveIndicatorVisible) - { - int pos = toPixel(m_liveIndicatorTime); - if (pos != -1 && (m_timelineView->currentTime() <= m_liveIndicatorTime && - m_liveIndicatorTime <= m_timelineView->currentTime() + m_timelineView->zoomFactor())) - { - painter->setPen(m_liveIndicatorColor); - painter->setBrush(QBrush(m_liveIndicatorColor)); - int sideLength = double(m_isLimitsVisible ? rect.height() - height : rect.height()) * m_liveIndicatorScalingFactor; - int top = bottom - static_cast(0.866 * sideLength); - QPolygon polygon; - polygon << QPoint(pos, bottom); - polygon << QPoint(pos + sideLength / 2, top); - polygon << QPoint(pos - sideLength / 2, top); - polygon << QPoint(pos, bottom); - QString dateTimeString = DateTime::toString(m_liveIndicatorTime, m_hoverDateTimeFormat); - const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString); - painter->setClipRect(rect.adjusted(-textWidth / 2, 0, 0, 0)); - painter->drawPolygon(polygon); - QRect dateTimeRect(pos - textWidth / 2, geometry().top(), textWidth + 2, 19); - if (dateTimeRect.right() > geometry().right() - 20) - dateTimeRect.adjust(-22, 0, -22, 0); - painter->setBrush(QBrush(backgroundColor())); - painter->drawRect(dateTimeRect); - painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString); - } - } - - painter->restore(); -} - - - - -void TimelineHeaderView::updateMajorTicks() -{ - m_majorTicks.clear(); - - if (m_timelineView->duration() <= 0) - return; - - const int interval = Zoom::majorInterval(m_timelineView->zoomFactor()); - if (interval == -1) - return; - - qint64 time = m_timelineView->currentTime(); - const int mod = time % interval; - if (mod) - time += interval - mod; - const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor(); - while (time <= end) - { - QString label = DateTime::toString(time, m_dateTimeFormat); - Tick tick(toPixel(time), time, label); - m_majorTicks.append(tick); - time += interval; - } -} - -void TimelineHeaderView::updateMediumTicks() -{ - m_mediumTicks.clear(); - - if (m_timelineView->duration() <= 0) - return; - - const int interval = Zoom::mediumInterval(m_timelineView->zoomFactor()); - if (interval == -1) - return; - - qint64 time = m_timelineView->currentTime(); - const int mod = time % interval; - if (mod) - time += interval - mod; - const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor(); - while (time <= end) - { - Tick tick(time); - - - auto it = std::find_if(m_majorTicks.begin(), m_majorTicks.end(), [tick](const Tick &other) - { - return tick.time == other.time; - }); - if (it != m_majorTicks.end()) - { - time += interval; - continue; - } - - tick.pos = toPixel(tick.time); - tick.label = DateTime::toString(tick.time, m_dateTimeFormat); - m_mediumTicks.append(tick); - time += interval; - } -} - -void TimelineHeaderView::updateMinorTicks() -{ - m_minorTicks.clear(); - - if (m_timelineView->duration() <= 0) - return; - - const int interval = Zoom::minorInterval(m_timelineView->zoomFactor()); - if (interval == -1) - return; - - qint64 time = m_timelineView->currentTime(); - const int mod = time % interval; - if (mod) - time += interval - mod; - const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor(); - while (time <= end) - { - Tick tick(time); - - - auto it = std::find_if(m_majorTicks.begin(), m_majorTicks.end(), [tick](const Tick &other) - { - return tick.time == other.time; - }); - if (it != m_majorTicks.end()) - { - time += interval; - continue; - } - - - it = std::find_if(m_mediumTicks.begin(), m_mediumTicks.end(), [tick](const Tick &other) - { - return tick.time == other.time; - }); - if (it != m_mediumTicks.end()) - { - time += interval; - continue; - } - - tick.pos = toPixel(tick.time); - tick.label = DateTime::toString(tick.time, m_dateTimeFormat); - m_minorTicks.append(tick); - time += interval; - } -} - - - - -void TimelineHeaderView::onUpdateTicks() -{ - updateMajorTicks(); - updateMediumTicks(); - updateMinorTicks(); - - if (m_isIndicatorVisible) - setIndicatorTime(m_indicatorTime); - - if (m_isLiveIndicatorVisible) - setLiveIndicatorTime(m_liveIndicatorTime); - - updateSection(Section::eItems); - emit ticksChanged(m_majorTicks); -} - - - - -void TimelineHeaderView::onLiveTimeout() -{ - setLiveIndicatorTime(m_liveIndicatorTime + m_liveTimerInterval); -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_header_view.h b/Lts/Common/LtsTimeline/timeline_header_view.h deleted file mode 100755 index ecf074f40..000000000 --- a/Lts/Common/LtsTimeline/timeline_header_view.h +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once - -#include "timeline_header_view_mouse_handler.h" -#include "timeline_utils.h" - -#include - -#include - - -class QMouseEvent; -class QPainter; -class QRect; -class QTimer; -class QWheelEvent; - - -namespace LtsTimeline -{ - -class TimelineView; - - -class LTSTIMELINE_EXPORT TimelineHeaderView : public QHeaderView -{ - Q_OBJECT - - friend class TimelineHeaderViewMouseHandler; - friend class TimelineViewMouseHandler; - -public: - static const QColor backgroundColor(); - static const QColor highlightColor(); - - static const int s_limitHeight; - - enum Section { eActuator = 0, eItems = 1 }; - - TimelineHeaderView(QWidget *parent = nullptr); - ~TimelineHeaderView(); - - void setParent(QWidget *); - void setTrackLabel(const QString &trackLabel); - - const Ticks &majorTicks() const { return m_majorTicks; } - const Ticks &mediumTicks() const { return m_mediumTicks; } - const Ticks &minorTicks() const { return m_minorTicks; } - - DateTimeFormat dateTimeFormat() const { return m_dateTimeFormat; } - void setDateTimeFormat(DateTimeFormat dateTimeFormat); - void setDateTimeFormatChangable(bool changable) { m_isDateTimeFormatChangable = changable; } - bool isDateTimeFormatChangable() const { return m_isDateTimeFormatChangable; } - - double indicatorScalingFactor() const { return m_indicatorScalingFactor; } - void setIndicatorScalingFactor(double factor); - qint64 indicatorTime() const { return m_indicatorTime; } - void setIndicatorTime(qint64 time); - int indicatorPosition() const { return toPixel(m_indicatorTime); } - void setIndicatorPosition(int position); - bool isIndicatorVisible() const { return m_isIndicatorVisible; } - void setIndicatorVisible(bool isIndicatorVisible); - bool indicatorLocked() const { return m_isIndicatorLocked; } - void setIndicatorLocked(bool locked) { m_isIndicatorLocked = locked; } - void lockIndicator() { setIndicatorLocked(true); } - void unlockIndicator() { setIndicatorLocked(false); } - - double liveIndicatorScalingFactor() const { return m_liveIndicatorScalingFactor; } - void setLiveIndicatorScalingFactor(double factor); - QColor liveIndicatorColor() const { return m_liveIndicatorColor; } - void setLiveIndicatorColor(const QColor &color); - qint64 liveIndicatorTime() const { return m_liveIndicatorTime; } - void setLiveIndicatorTime(qint64 time); - int liveIndicatorPosition() const { return toPixel(m_liveIndicatorTime); } - void setLiveIndicatorPosition(int position); - bool isLiveIndicatorVisible() const { return m_isLiveIndicatorVisible; } - void setLiveIndicatorVisible(bool isLiveIndicatorVisible); - void startLiveTimer(qint64 startTime = -1, int interval = 1000); - void stopLiveTimer(); - - qint64 hoverTime() const { return m_hoverTime; } - void setHoverTime(qint64 time); - int hoverPosition() const { return m_hoverPosition; } - void setHoverPosition(int position); - bool isHoverVisible() const { return m_isHoverVisible; } - void setHoverVisible(bool visible); - bool isHoverTimeVisible() const { return m_isHoverTimeVisible; } - void setHoverTimeVisible(bool visible); - DateTimeFormat hoverDateTimeFormat() const { return m_hoverDateTimeFormat; } - void setHoverDateTimeFormat(DateTimeFormat format); - - bool isLimitsVisible() const { return m_isLimitsVisible; } - void setLimitsVisible(bool visible); - DateTimeFormat limitsDateTimeFormat() const { return m_limitsDateTimeFormat; } - void setLimitsDateTimeFormat(DateTimeFormat format); - - int toPixel(qint64 time) const; - qint64 toTime(int pixel) const; - - QFont &font() { return m_font; } - - TimelineHeaderViewMouseHandler *mouseHandler() { return m_mouseHandler; } - - void wheelEvent(QWheelEvent *e) override { m_mouseHandler->wheelEvent(e); } - -protected: - void mouseMoveEvent(QMouseEvent *e) override { m_mouseHandler->mouseMoveEvent(e); } - void mousePressEvent(QMouseEvent *e) override { m_mouseHandler->mousePressEvent(e); } - void mouseReleaseEvent(QMouseEvent *e) override { m_mouseHandler->mouseReleaseEvent(e); } - - void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; - -private: - void updateMajorTicks(); - void updateMediumTicks(); - void updateMinorTicks(); - -public slots: - void onUpdateTicks(); - -private slots: - void onLiveTimeout(); - -signals: - void ticksChanged(const Ticks &); - void indicatorTimeChanged(qint64); - void indicatorVisibilityChanged(bool); - void liveIndicatorTimeChanged(qint64); - void liveIndicatorVisibilityChanged(bool); - void hoverPositionChanged(int); - void hoverVisibilityChanged(bool); - -private: - TimelineHeaderViewMouseHandler *m_mouseHandler{nullptr}; - TimelineView *m_timelineView{nullptr}; - Ticks m_majorTicks; - Ticks m_mediumTicks; - Ticks m_minorTicks; - DateTimeFormat m_dateTimeFormat{DateTimeFormat::DTG}; - bool m_isDateTimeFormatChangable{true}; - - double m_indicatorScalingFactor{0.5}; - qint64 m_indicatorTime{-1}; - bool m_isIndicatorVisible{true}; - bool m_isIndicatorLocked{false}; - - double m_liveIndicatorScalingFactor{0.5}; - QColor m_liveIndicatorColor; - qint64 m_liveIndicatorTime{-1}; - bool m_isLiveIndicatorVisible{false}; - QTimer *m_liveTimer{nullptr}; - int m_liveTimerInterval{1000}; - - qint64 m_hoverTime{-1}; - int m_hoverPosition{-1}; - bool m_isHoverVisible{true}; - bool m_isHoverTimeVisible{true}; - DateTimeFormat m_hoverDateTimeFormat{DateTimeFormat::DTG}; - - bool m_isLimitsVisible{false}; - DateTimeFormat m_limitsDateTimeFormat{DateTimeFormat::DTG}; - const QPixmap m_minLimitIndicator; - const QPixmap m_maxLimitIndicator; - - QString m_trackLabel; - - QFont m_font; - -}; - -typedef TimelineHeaderView::Section Section; - -} diff --git a/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp b/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp deleted file mode 100755 index 8b39e09ed..000000000 --- a/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "timeline_header_view_mouse_handler.h" -#include "timeline_header_view.h" -#include "timeline_view.h" - -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -TimelineHeaderViewMouseHandler::TimelineHeaderViewMouseHandler(TimelineHeaderView *header) - : m_header(header) -{} - - - - - -void TimelineHeaderViewMouseHandler::mouseMoveEvent(QMouseEvent *e) -{ - if (m_isLeftButtonPressed) - m_hasMouseMovedLeft = true; - if (m_isRightButtonPressed) - m_hasMouseMovedRight = true; - - if (!m_isResizingSection) - { - if (m_header->sectionPosition(Section::eItems) - 10 < e->pos().x() && e->pos().x() < m_header->sectionPosition(Section::eItems)) - m_header->m_timelineView->setCursor(QCursor(Qt::SizeHorCursor)); - else if (e->pos().y() <= m_header->geometry().bottom()) - m_header->m_timelineView->setCursor(QCursor(Qt::ArrowCursor)); - } - else - m_header->resizeSection(Section::eActuator, std::min(e->pos().x(), m_header->geometry().width() / 2)); - - const bool isWithinRangeX = isMouseWithinRangeX(e->pos().x()); - if (!isWithinRangeX && !m_isLeftButtonPressed) - { - m_header->setHoverPosition(-1); - return; - } - - if (isWithinRangeX) - m_header->setHoverPosition(e->pos().x()); - if (m_isLeftButtonPressed) - { - if (m_isIndicatorSelected && !m_header->m_isIndicatorLocked) - { - m_header->m_timelineView->setCursor(QCursor(Qt::ClosedHandCursor)); - if (isWithinRangeX) - m_header->setIndicatorPosition(e->pos().x()); - } - } - else - { - if (isMouseOverIndicator(e->pos()) && m_header->m_isIndicatorVisible && !m_header->m_isIndicatorLocked) - m_header->m_timelineView->setCursor(QCursor(Qt::OpenHandCursor)); - else if (e->pos().y() <= m_header->geometry().bottom()) - m_header->m_timelineView->setCursor(QCursor(Qt::ArrowCursor)); - } -} - -void TimelineHeaderViewMouseHandler::mousePressEvent(QMouseEvent *e) -{ - if (e->button() == Qt::LeftButton) - { - m_lastPos = e->pos(); - m_isLeftButtonPressed = true; - if (isMouseOverIndicator(e->pos()) && m_header->m_isIndicatorVisible && !m_header->m_isIndicatorLocked) - { - m_header->m_timelineView->setCursor(QCursor(Qt::ClosedHandCursor)); - m_isIndicatorSelected = true; - } - else if (m_header->m_timelineView->cursor().shape() == Qt::SizeHorCursor) - { - m_isResizingSection = true; - } - } - if (e->button() == Qt::RightButton) - m_isRightButtonPressed = true; -} - -void TimelineHeaderViewMouseHandler::mouseReleaseEvent(QMouseEvent *e) -{ - if (e->button() == Qt::LeftButton) - { - if (!m_hasMouseMovedLeft && isMouseWithinRangeX(e->pos().x()) && !m_header->m_isIndicatorLocked) - m_header->setIndicatorPosition(e->pos().x()); - if (isMouseOverIndicator(e->pos())) - m_header->m_timelineView->setCursor(QCursor(Qt::OpenHandCursor)); - else - m_header->m_timelineView->setCursor(QCursor(Qt::ArrowCursor)); - m_lastPos = {}; - m_isIndicatorSelected = false; - m_isResizingSection = false; - m_isLeftButtonPressed = false; - m_hasMouseMovedLeft = false; - } - if (e->button() == Qt::RightButton) - { - m_header->setDateTimeFormat(static_cast((m_header->dateTimeFormat() + 1) % DateTime::numberOfDateTimeFormats())); - m_isRightButtonPressed = false; - m_hasMouseMovedRight = false; - } -} - -void TimelineHeaderViewMouseHandler::wheelEvent(QWheelEvent *e) -{ - if (!m_header->m_timelineView) - return; - - if (QGuiApplication::queryKeyboardModifiers() == Qt::ControlModifier) - { - if (e->angleDelta().y() < 0) - m_header->m_timelineView->setZoomFactor(Zoom::increment(m_header->m_timelineView->zoomFactor())); - else - m_header->m_timelineView->setZoomFactor(Zoom::decrement(m_header->m_timelineView->zoomFactor())); - } - else - { - if (e->angleDelta().y() > 0) - m_header->m_timelineView->setCurrentTime( - m_header->m_timelineView->currentTime() - Zoom::majorInterval(m_header->m_timelineView->zoomFactor()) - ); - else - m_header->m_timelineView->setCurrentTime( - m_header->m_timelineView->currentTime() + Zoom::majorInterval(m_header->m_timelineView->zoomFactor()) - ); - } -} - - - - - - - - - -bool TimelineHeaderViewMouseHandler::isMouseWithinRangeX(int x) const -{ - return m_header->sectionPosition(Section::eItems) < x && x < m_header->geometry().right(); -} - -bool TimelineHeaderViewMouseHandler::isMouseWithinRangeY(int y) const -{ - return m_header->geometry().top() < y && y < m_header->geometry().bottom(); -} - -bool TimelineHeaderViewMouseHandler::isMouseOverIndicator(const QPoint &pos) const -{ - const int height = m_header->height(); - return m_header->indicatorPosition() - height / 4 <= pos.x() && pos.x() <= m_header->indicatorPosition() + height / 4 && - m_header->geometry().center().y() <= pos.y() && pos.y() < m_header->geometry().bottom(); -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h b/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h deleted file mode 100755 index 8ca858f9f..000000000 --- a/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -#include - - -class QMouseEvent; -class QWheelEvent; - - -namespace LtsTimeline -{ - -class TimelineHeaderView; - - -class LTSTIMELINE_EXPORT TimelineHeaderViewMouseHandler -{ - -public: - explicit TimelineHeaderViewMouseHandler(TimelineHeaderView *header); - - void mouseMoveEvent(QMouseEvent *e); - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void wheelEvent(QWheelEvent *e); - - bool isBusy() const { return m_isIndicatorSelected || m_isResizingSection; } - bool isIndicatorSelected() const { return m_isIndicatorSelected; } - -private: - - bool isMouseWithinRangeX(int x) const; - bool isMouseWithinRangeY(int y) const; - bool isMouseOverIndicator(const QPoint &pos) const; - -private: - TimelineHeaderView *m_header{nullptr}; - QPoint m_lastPos; - bool m_isLeftButtonPressed{false}; - bool m_isRightButtonPressed{false}; - bool m_hasMouseMovedLeft{false}; - bool m_hasMouseMovedRight{false}; - bool m_isIndicatorSelected{false}; - bool m_isResizingSection{false}; -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_model.cpp b/Lts/Common/LtsTimeline/timeline_model.cpp deleted file mode 100755 index 89a9ca27e..000000000 --- a/Lts/Common/LtsTimeline/timeline_model.cpp +++ /dev/null @@ -1,761 +0,0 @@ - - - -#include "timeline_model.h" -#include "timeline_view_drag_handler.h" -#include "track.h" - -#include - - -#include - - -namespace LtsTimeline -{ - -TimelineModel::TimelineModel(TimelineView *view, QObject *parent) - : QAbstractItemModel(parent) - , m_view(view) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineModel")) -{ - m_rootTrack.m_id = nextFreeTrackId(); - m_trackCache.insert(std::pair(m_rootTrack.m_id, &m_rootTrack)); -} - - -void TimelineModel::clear() -{ - m_rootTrack.m_childrenTracks.clear(); - m_rootTrack.m_items.clear(); - - - m_rootTrack.m_copyItemIds.clear(); - m_trackCache.clear(); - - m_itemCache.clear(); - - m_currentTrackId = 1; - m_currentItemId = 0; - m_trackCache.insert(std::pair(m_rootTrack.m_id, &m_rootTrack)); -} - - -void TimelineModel::clearItems() -{ - for (std::pair p : m_trackCache) - p.second->clearItems(); - - m_itemCache.clear(); - m_currentItemId = 0; -} - - -Track &TimelineModel::track(int id) noexcept(false) -{ - if (!m_trackCaching) - { - if (id == m_rootTrack.id()) - return m_rootTrack; - return m_rootTrack.track(id); - } - - auto it = m_trackCache.find(id); - - if (it == m_trackCache.end()) - throw QStringLiteral("Model: track with id %1 not found in cache").arg(id); - - return *it->second; -} - - -const Track &TimelineModel::track(int id) const noexcept(false) -{ - if (!m_trackCaching) - { - if (id == m_rootTrack.id()) - return m_rootTrack; - return m_rootTrack.track(id); - } - - std::map::const_iterator it = m_trackCache.find(id); - - if (it == m_trackCache.cend()) - throw QStringLiteral("Model: const track with id %1 not found in cache").arg(id); - - return *it->second; -} - - -Item &TimelineModel::item(int id) noexcept(false) -{ - if (!m_itemCaching) - return m_rootTrack.item(id); - - auto it = m_itemCache.find(id); - - if (it == m_itemCache.end()) - throw QStringLiteral("Model: item with id %1 not found in cache").arg(id); - - return *it->second; -} - - -const Item &TimelineModel::item(int id) const noexcept(false) -{ - if (!m_itemCaching) - return m_rootTrack.item(id); - - decltype(m_itemCache)::const_iterator it = m_itemCache.find(id); - - if (it == m_itemCache.cend()) - throw QStringLiteral("Model: const item with id %1 not found in cache").arg(id); - - return *it->second; -} - - -const QVector TimelineModel::itemIds() const -{ - if (!m_itemCaching) - return m_rootTrack.itemIds(true); - - QVector ids; - std::transform(m_itemCache.cbegin(), m_itemCache.cend(), std::back_inserter(ids), - [](const std::pair &pair) - { - return pair.first; - }); - - return ids; -} - - - -const QVector TimelineModel::itemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted) const -{ - return m_rootTrack.itemIdsBetween(start, end, strict, complete, recursive, sorted); -} - -bool TimelineModel::isItemsBetween(qint64 start, qint64 end, const QVector &ignoreIds, bool strict, bool complete, bool recursive) const -{ - return m_rootTrack.isItemsBetween(start, end, ignoreIds, strict, complete, recursive); -} - -const QVector TimelineModel::copyItemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool sorted) const -{ - return m_rootTrack.copyItemIdsBetween(start, end, strict, complete, true, sorted); -} - -bool TimelineModel::isItemsBetween(qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive) const -{ - return m_rootTrack.isItemsBetween(start, end, ignoreId, strict, complete, recursive); -} - - -const QVector TimelineModel::_copyItemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const -{ - QVector itemIds; - itemIds.reserve(t.itemCount()); - - - for (const int itemId : qAsConst(t.copyItemIds())) - { - const Item &i = item(itemId); - if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration)) - itemIds += i.id(); - } - - if (recursive) - for (const TrackPtr &trackPtr : qAsConst(t.tracks())) - itemIds += _copyItemIdsBetween(*trackPtr.get(), start, end, strict, complete, true, false, minimumDuration); - - - itemIds.erase(std::unique(itemIds.begin(), itemIds.end()), itemIds.end()); - - itemIds.squeeze(); - - if (!sorted) - return itemIds; - - - std::sort(itemIds.begin(), itemIds.end(), [this](const int itemIdA, const int itemIdB) - { - try - { - return item(itemIdA).startTime() < item(itemIdB).startTime(); - } - catch (...) { return false; } - }); - - return itemIds; -} - - -const QVector TimelineModel::_itemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const -{ - QVector itemIds; - itemIds.reserve(t.itemCount()); - - for (const ItemPtr &itemPtr : qAsConst(t.items())) - { - const Item &i = *itemPtr; - if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration)) - itemIds += i.id(); - } - - if (recursive) - for (const TrackPtr &trackPtr : qAsConst(t.tracks())) - itemIds += _itemIdsBetween(*trackPtr.get(), start, end, strict, complete, true, false, minimumDuration); - - - - - itemIds.squeeze(); - - if (!sorted) - return itemIds; - - - std::sort(itemIds.begin(), itemIds.end(), [this](const int itemIdA, const int itemIdB) - { - try - { - return item(itemIdA).startTime() < item(itemIdB).startTime(); - } - catch (...) { return false; } - }); - - return itemIds; -} - - -bool TimelineModel::_isItemsBetween(const Track &t, qint64 start, qint64 end, const QVector &ignoreIds, bool strict, bool complete, bool recursive, qint64 minimumDuration) const -{ - for (const ItemPtr &itemPtr : qAsConst(t.items())) - { - const Item &i = *itemPtr; - if (!ignoreIds.contains(i.id())) - if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration)) - return true; - } - - if (!recursive) - return false; - - for (const TrackPtr &trackPtr : qAsConst(t.tracks())) - if (_isItemsBetween(*trackPtr.get(), start, end, ignoreIds, strict, complete, true, minimumDuration)) - return true; - - return false; -} - - -bool TimelineModel::_isItemsBetween(const Track &t, qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive, qint64 minimumDuration) const -{ - return _isItemsBetween(t, start, end, QVector({ ignoreId }), strict, complete, recursive, minimumDuration); -} - - -QVariant TimelineModel::data(const QModelIndex &index, int role) const -{ - if ((role != Qt::DisplayRole && role != Qt::UserRole) || !index.isValid()) - return {}; - - int trackId = index.internalId(); - int column = index.column(); - - if (column == 1 && role == Qt::DisplayRole) - return trackId; - - if (role == Qt::UserRole) - { - if (column == 0) - return trackId; - - try - { - const Track &t = track(trackId); - return t.userData(); - } - catch(...) - { - Q_ASSERT(false); - return {}; - } - } - - - if (column != 0) - return {}; - - - QString text; - - try - { - const Track &t = track(trackId); - -#if 0 - text = QStringLiteral("[%1] %3").arg(t.id()).arg(t.text()); -#else - text = t.text(); -#endif - } - catch(...) - { - Q_ASSERT(false); - return QStringLiteral("[?????]"); - } - - if (text.isEmpty()) - text = tr("[unknown]"); - - return text; -} - - -Qt::ItemFlags TimelineModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return QAbstractItemModel::flags(index); -} - - -QModelIndex TimelineModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) - return {}; - - int childTrackId; - try - { - const Track &parentTrack = parent.isValid() ? track(parent.internalId()) : m_rootTrack; - const Track &childTrack = parentTrack.trackAt(row); - childTrackId = childTrack.id(); - } - catch (const QString &msg) - { - DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get parent track " << msg; - return {}; - } - catch (const std::out_of_range &oor) - { - DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get child track " << QString::fromUtf8(oor.what()); - return {}; - } - - return createIndex(row, column, childTrackId); -} - - -int TimelineModel::rowOfTrackId(int id) const noexcept(false) -{ - const Track &parent = track(track(id).parentId()); - - if (parent.items().empty()) - throw("parent doesn't have items"); - - int row = 0; - - for (const TrackPtr &trackPtr : qAsConst(parent.tracks())) - { - if (trackPtr->id() == id) - return row; - row++; - } - - throw("not found"); -} - - -const Track &TimelineModel::trackOfIndex(const QModelIndex &index) const -{ - if (!index.isValid()) - throw QStringLiteral("trackOfIndex: index is invalid!"); - - - try - { - return track(index.internalId()); - } - catch (const QString &msg) - { - throw QStringLiteral("trackOfIndex: cannot find track by internal id %1: %2").arg(index.internalId()).arg(msg); - } - - - return m_rootTrack; -} - - - - - - - - - - - - - - - - - - -const Track &TimelineModel::parentOfTrackId(int id) const noexcept(false) -{ - if (!m_trackCaching) - { - if (m_rootTrack.hasTrack(id)) - return m_rootTrack; - - - } - - const Track &theTrack = track(id); - - if (theTrack.parentId() == m_rootTrack.id()) - return m_rootTrack; - - return track(theTrack.parentId()); -} - - -QModelIndex TimelineModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - - - - - - - - - - - - - int trackId = index.internalId(); - int parentId; - int superParentId; - try - { - const Track &parent = parentOfTrackId(trackId); - parentId = parent.id(); - superParentId = parent.parentId(); - } - catch (const QString &msg) - { - DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get parent of track with id" << trackId << msg; - return {}; - } - - if (parentId == m_rootTrack.id()) - return {}; - - int parentsRow; - try - { - const Track &superParent = track(superParentId); - - try - { - parentsRow = superParent.rowOfTrack(parentId); - } - catch (const QString &msg) - { - DEBUG_LOG(m_logger) << Q_FUNC_INFO << "row of track for parent not found for parent id" << parentId << "-" << msg; - return {}; - } - } - catch (const QString &msg) - { - DEBUG_LOG(m_logger) << Q_FUNC_INFO << "super parent not found for super parent id" << superParentId << "-" << msg; - return {}; - } - - return createIndex(parentsRow, 0, parentId); -} - - -void TimelineModel::setItemStartTime(Track &t, Item &i, qint64 newStart) -{ - i._setStartTime(newStart); - t.sort(); -} - - -void TimelineModel::setItemStartTime(Item &i, qint64 newStart) noexcept(false) -{ - setItemStartTime(track(i.parentTrackId()), i, newStart); -} - - -void TimelineModel::setItemStartTime(int itemId, qint64 newStart) noexcept(false) -{ - setItemStartTime(item(itemId), newStart); -} - - -void TimelineModel::setItemDuration(Track &t, Item &i, int duration) -{ - i._setDuration(std::max(duration, m_minItemDuration)); - t.sort(); -} - -void TimelineModel::setItemDuration(Item &i, int duration) noexcept(false) -{ - setItemDuration(track(i.parentTrackId()), i, duration); -} - -void TimelineModel::setItemDuration(int itemId, int duration) noexcept(false) -{ - setItemDuration(item(itemId), duration); -} - - -void TimelineModel::copyToClipboard(const QVector &ids) noexcept(false) -{ - m_clipboard.clear(); - m_clipboard.reserve(ids.size()); - qint64 tStartMin = LLONG_MAX; - for (int itemId : qAsConst(ids)) - { - const Item &i = item(itemId); - ItemPtr iPtr = std::make_unique(Item(i.startTime(), i.duration(), i.color())); - iPtr->m_parentTrackId = i.m_parentTrackId; - m_clipboard.push_back(std::move(iPtr)); - tStartMin = std::min(tStartMin, i.startTime()); - } - for (ItemPtr &iPtr : m_clipboard) - iPtr->_setStartTime(iPtr->startTime() - tStartMin); -} - -void TimelineModel::cutToClipboard(const QVector &ids) noexcept(false) -{ - copyToClipboard(ids); - for (int itemId : qAsConst(ids)) - removeItem(itemId); -} - -void TimelineModel::pasteFromClipboard(qint64 time) noexcept(false) -{ - if (m_isSnapEnabled) - time -= (time % m_snapStep); - for (ItemPtr &iPtr : m_clipboard) - { - iPtr->_setStartTime(time + iPtr->startTime()); - addItem(iPtr->parentTrackId(), std::move(iPtr)); - } - m_clipboard.clear(); -} - - -int TimelineModel::rowCount(const QModelIndex &parent) const -{ - if (parent.column() > 0) - return 0; - - if (!parent.isValid()) - return m_rootTrack.tracks().size(); - - try - { - return track(parent.internalId()).tracks().size(); - } - catch(...) {} - - return 0; -} - - -QModelIndex TimelineModel::indexOfTrack(int trackId) const -{ - for (int row = 0; row < rowCount(); ++row) - { - QModelIndex idx = indexOfTrackFromIndex(index(row, 0, {}), trackId); - if (idx.isValid()) - return idx; - } - return {}; -} - - -QModelIndex TimelineModel::indexOfTrackFromIndex(const QModelIndex &idx, int trackId) const -{ - if (static_cast(idx.internalId()) == trackId) - return index(idx.row(), 1, idx.parent()); - - for (int row = 0; row < rowCount(idx); ++row) - { - const QModelIndex i = indexOfTrackFromIndex(index(row, 0, idx), trackId); - if (i.isValid()) - return i; - } - - return {}; -} - - -QModelIndex TimelineModel::indexOfTrackSafe(int trackId) const noexcept(false) -{ - for (int row = 0; row < rowCount(); ++row) - { - const QModelIndex idx(indexOfTrackFromIndex(index(row, 0 , {}), trackId)); - if (idx.isValid()) - return idx; - } - - QString trackText; - try - { - trackText = track(trackId).text(); - } catch(...){} - - throw QStringLiteral("indexOfTrackSafe: index for trackId %1 ('%2') not found ").arg(trackId).arg(trackText.isEmpty() ? QStringLiteral("(unknown)") : trackText); -} - - -QModelIndex TimelineModel::indexOfTrackFromIndexSafe(const QModelIndex &idx, int trackId) const noexcept(false) -{ - if (static_cast(idx.internalId()) == trackId) - return idx; - - for (int row = 0; row < rowCount(idx); ++row) - { - const QModelIndex i = indexOfTrackFromIndex(index(row, 1, idx), trackId); - if (i.isValid()) - return i; - } - - throw QStringLiteral("indexOfTrackFromIndexSafe: index for trackId %1 not found").arg(trackId); -} - - -void TimelineModel::moveItemToTrack(int itemId, int trackId, qint64 time) noexcept(false) -{ - - - - const Item &theItem = item(itemId); - Track &oldTrack = track(theItem.parentTrackId()); - Track &newTrack = track(trackId); - - ItemPtr itemPtr = oldTrack.takeItem(itemId); - - Item *theItemPtr = newTrack.addItem(std::move(itemPtr)); - setItemStartTime(newTrack, *theItemPtr, time); - theItemPtr->m_parentTrackId = trackId; -} - - -void TimelineModel::removeItem(int itemId) noexcept(false) -{ - const Item &i = item(itemId); - Track &t = track(i.parentTrackId()); - - t.removeItem(itemId); - - auto it = m_itemCache.find(itemId); - if (it == m_itemCache.end()) - { - Q_ASSERT(false); - throw QStringLiteral("removeItem: item with id %1 not found").arg(itemId); - } - - m_itemCache.erase(it); - - - -} - - -void TimelineModel::removeTrack(int trackId) noexcept(false) -{ - if (trackId == m_rootTrack.id()) - { - Q_ASSERT(false); - throw QStringLiteral("removeTrack: the root track shouldn't and *can't* be deleted"); - } - - - const Track &t = track(trackId); - Track &parentTrack = track(t.parentId()); - - for (int itemId : t.itemIds(true)) - { - auto it = m_itemCache.find(itemId); - - if (it == m_itemCache.end()) - { - Q_ASSERT(false); - continue; - } - - m_itemCache.erase(it); - } - - - parentTrack.removeTrack(trackId); - - auto it = m_trackCache.find(trackId); - if (it == m_trackCache.end()) - { - Q_ASSERT(false); - return; - } - - m_trackCache.erase(it); -} - - -void TimelineModel::setMinimumDuration(int duration) -{ - if (duration < 0) - { - WARNING_LOG(m_logger) << "negative duration is not possible -> setting minimum duration to 0"; - duration = 0; - } - if (duration % 1000 && m_isSnapEnabled) - WARNING_LOG(m_logger) << "Snapping to 1000ms enabled, yet minimum duration is not a multiple of 1000ms: " << duration; - m_minItemDuration = duration; -} - -bool TimelineModel::canDropItem(const Item &item, int trackId, qint64 start) const -{ - Q_UNUSED(item); - Q_UNUSED(trackId); - Q_UNUSED(start); - return true; -} - -bool TimelineModel::canMoveItem(const Item &item, qint64 start) const -{ - Q_UNUSED(item); - Q_UNUSED(start); - return true; -} - -bool TimelineModel::canResizeItem(const Item &item, qint64 start, int duration) const -{ - Q_UNUSED(item); - Q_UNUSED(start); - Q_UNUSED(duration); - return true; -} - - -bool TimelineModel::isItemDragged(int _itemId) const -{ - return m_view->dragHandler() ? m_view->dragHandler()->dragItemIds().contains(_itemId) : false; -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_model.h b/Lts/Common/LtsTimeline/timeline_model.h deleted file mode 100755 index 40fd3ffea..000000000 --- a/Lts/Common/LtsTimeline/timeline_model.h +++ /dev/null @@ -1,247 +0,0 @@ - - - -#pragma once - -#include "item.h" -#include "track.h" - -#include - -#include -#include - -#include - - -namespace LtsTimeline -{ - -class TimelineView; - - -class LTSTIMELINE_EXPORT TimelineModel : public QAbstractItemModel -{ - Q_OBJECT - - friend class TimelineViewSelectHandler; - -public: - explicit TimelineModel(TimelineView *view, QObject *parent = nullptr); - virtual ~TimelineModel() = default; - - void clear(); - Track &rootTrack() { return m_rootTrack; } - Track &track(int id) noexcept(false); - const Track &track(int id) const noexcept(false); - const Item &item(int id) const noexcept(false); - Item &item(int id) noexcept(false); - const QVector itemIds() const; - const QVector itemIdsBetween(qint64 start, qint64 end, bool strict = false, bool complete = false, bool recursive = false, bool sorted = false) const; - const QVector copyItemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool sorted) const; - - bool isItemsBetween(qint64 start, qint64 end, const QVector &ignoreIds = {}, bool strict = false, bool complete = false, bool recursive = false) const; - bool isItemsBetween(qint64 start, qint64 end, int ignoreId = -1 , bool strict = false, bool complete = false, bool recursive = false) const; - const Track &parentOfTrackId(int id) const noexcept(false); - - virtual void setItemStartTime(Track &t, Item &i, qint64 newStart); - virtual void setItemStartTime(Item &i, qint64 newStart) noexcept(false); - virtual void setItemStartTime(int itemId, qint64 newStart) noexcept(false); - - virtual void setItemDuration(Track &t, Item &i, int duration); - virtual void setItemDuration(Item &i, int duration) noexcept(false); - virtual void setItemDuration(int itemId, int duration) noexcept(false); - - - void print(bool withItems = false) const { m_rootTrack.printTrackIds(withItems); } - - template ::value, TrackClass>::type> - int addTrack(int parentTrackId, TrackClass &&newTrack ) noexcept(false) - { - int id = nextFreeTrackId(); - - TrackClass _track(std::move(newTrack)); - _track.m_id = id; - _track.m_parentId = parentTrackId; - - - - - - auto *ptr = track(parentTrackId).addTrack(std::move(_track)); - m_trackCache.insert(std::pair(id, ptr)); - - - - return id; - } - - template ::value, ItemClass>::type> - int addItem(int parentTrackId, ItemClass &&item) noexcept(false) - { - int id = nextFreeItemId(); - - ItemClass _item(std::move(item)); - _item.m_id = id; - _item.m_parentTrackId = parentTrackId; - - if (m_isSnapEnabled) - { - _item.m_startTime = _item.m_startTime - (_item.m_startTime % 1000); - _item.m_duration = _item.m_duration - (_item.m_duration % 1000); - } - - Item *ptr = track(parentTrackId).addItem(std::move(_item)); - m_itemCache.insert(std::pair(id, ptr)); - - QModelIndex idx = indexOfTrack(parentTrackId); - emit dataChanged(idx, idx); - - return id; - } - - bool isEditable() const { return m_isEditable; } - void setEditable(bool editable) { m_isEditable = editable; } - bool isSnapEnabled() const { return m_isSnapEnabled; } - void setSnapEnabled(bool enabled) { m_isSnapEnabled = enabled; } - int snapStep() const { return m_snapStep; } - void setSnapStep(int step) { m_snapStep = step; } - int snapTolerance() const { return m_snapTol; } - void setSnapTolerance(int tolerance) { m_snapTol = tolerance; } - qint64 snapTime(qint64 time) const { return m_isSnapEnabled ? time / m_snapStep * m_snapStep : time; } - int minimumDuration() const { return m_minItemDuration; } - void setMinimumDuration(int duration); - - QModelIndex indexOfTrack(int trackId) const; - QModelIndex indexOfTrackSafe(int trackId) const noexcept(false); - - virtual void moveItemToTrack(int itemId, int trackId, qint64 time) noexcept(false); - virtual void removeItem(int itemId) noexcept(false); - virtual void removeTrack(int trackId) noexcept(false); - - virtual bool canDropItem(const Item &item, int trackId, qint64 start) const; - virtual bool canMoveItem(const Item &item, qint64 start) const; - virtual bool canResizeItem(const Item &item, qint64 start, int duration) const; - - virtual bool isItemDragged(int _itemId) const; - - virtual void copyToClipboard(const QVector &ids) noexcept(false); - virtual void cutToClipboard(const QVector &ids) noexcept(false); - virtual void pasteFromClipboard(qint64 time) noexcept(false); - - bool isClipboardEmpty() const { return m_clipboard.empty(); } - - - int rowCount(const QModelIndex &parent = {}) const override; - QVariant data(const QModelIndex &index, int role) const override; - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - QModelIndex parent(const QModelIndex &index) const override; - int columnCount(const QModelIndex &) const override { return 2; } - -protected: - ItemPtrNCVRef _itemNCV(Track &track, int id) noexcept(false) - { - return track._itemNCV(id); - } - - ItemPtrNCVRef _itemNCV(const TrackPtr &trackPtr, int id) noexcept(false) - { - return trackPtr->_itemNCV(id); - } - - const ItemPtrRefs _items(const TrackPtr &trackPtr, bool recursive) const - { - return trackPtr->_items(recursive); - } - - ItemPtrNCVRefs _itemsNCV(const TrackPtr &trackPtr, bool recursive) - { - return trackPtr->_itemsNCV(recursive); - } - - - - int addItem(int parentTrackId, ItemPtr &&itemPtr, bool forceTheCompilerToUseMe = false) noexcept(false) - { - (void)forceTheCompilerToUseMe; - - int id = nextFreeItemId(); - - ItemPtr _itemPtr(std::move(itemPtr)); - - _itemPtr->m_id = id; - - _itemPtr->m_parentTrackId = parentTrackId; - - if (m_isSnapEnabled) - { - _itemPtr->m_startTime = _itemPtr->m_startTime - (_itemPtr->m_startTime % 1000); - _itemPtr->m_duration = _itemPtr->m_duration - (_itemPtr->m_duration % 1000); - } - Item *ptr = track(parentTrackId).addItem(std::move(_itemPtr)); - - m_itemCache.insert(std::pair(id, ptr)); - - QModelIndex idx = indexOfTrack(parentTrackId); - emit dataChanged(idx, idx); - - return id; - } - - - - - - - - - - void clearItems(); - int rowOfTrackId(int id) const noexcept(false); - - - - const Track &trackOfIndex(const QModelIndex &index) const; - int indexOf(int id) const; - - const QVector _itemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const; - const QVector _copyItemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const; - bool _isItemsBetween(const Track &t, qint64 start, qint64 end, const QVector &ignoreIds, bool strict, bool complete, bool recursive, qint64 minimumDuration) const; - bool _isItemsBetween(const Track &t, qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive, qint64 minimumDuration) const; - -private: - int nextFreeTrackId() { return m_currentTrackId++; } - int nextFreeItemId() { return m_currentItemId++; } - - QModelIndex indexOfTrackFromIndex(const QModelIndex &idx, int trackId) const; - QModelIndex indexOfTrackFromIndexSafe(const QModelIndex &idx, int trackId) const noexcept(false); - -public slots: - virtual void onInsertTrack(const QModelIndex &index) { Q_UNUSED(index); } - virtual void onRemoveTrack(const QModelIndex &index) { Q_UNUSED(index); } - -protected: - TimelineView *m_view{nullptr}; - - Track m_rootTrack; - int m_currentTrackId{0}; - int m_currentItemId{0}; - bool m_isEditable{true}; - bool m_isSnapEnabled{false}; - int m_snapStep{500}; - int m_snapTol{1000}; - int m_minItemDuration{0}; - - - bool m_itemCaching{true}; - bool m_trackCaching{true}; - std::map m_trackCache; - - std::map m_itemCache; - Items m_clipboard; - - NGL::INGLLogger &m_logger; -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_utils.cpp b/Lts/Common/LtsTimeline/timeline_utils.cpp deleted file mode 100755 index a717a4afc..000000000 --- a/Lts/Common/LtsTimeline/timeline_utils.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "timeline_utils.h" - -#include -#include - -#include -#include - - -namespace LtsTimeline -{ - -namespace DateTime -{ - - - - -int numberOfDateTimeFormats() -{ - return 6; -} - -QString toString(qint64 time, Format format) -{ - switch (format) - { - case Format::DTG: - return DTG::toStringMSec(time, DTG::DisplayFormat::Long); - case Format::ISODate: - return QDateTime::fromMSecsSinceEpoch(time).toString(Qt::ISODate); - case Format::Standard: - return QDateTime::fromMSecsSinceEpoch(time).toString(QStringLiteral("dd.MM.yyyy hh:mm:ss")); - case Format::DTGShort: - { - QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(time).toUTC().addSecs(DTG::timezone2Hours(DTG::timezone()) * 3600); - return dateTime.toString(QStringLiteral("hhmmss ")) + QStringLiteral("%1").arg(DTG::timezone()); - } - case Format::DTGVeryShort: - { - QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(time).toUTC().addSecs(DTG::timezone2Hours(DTG::timezone()) * 3600); - return dateTime.toString(QStringLiteral("hhmm")); - } - case Format::TimeOnly: - return QTime::fromMSecsSinceStartOfDay(time).toString(); - } - return {}; -} - -} - - - - - -namespace Zoom -{ - -int numberOfZoomFactors() -{ - return 15; -} - -Factor LTSTIMELINE_EXPORT increment(Factor factor) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return ZoomFactor::ThirtySeconds; - case ZoomFactor::ThirtySeconds: - return ZoomFactor::SixtySeconds; - case ZoomFactor::SixtySeconds: - return ZoomFactor::TwoMinutes; - case ZoomFactor::TwoMinutes: - return ZoomFactor::ThreeMinutes; - case ZoomFactor::ThreeMinutes: - return ZoomFactor::FiveMinutes; - case ZoomFactor::FiveMinutes: - return ZoomFactor::TenMinutes; - case ZoomFactor::TenMinutes: - return ZoomFactor::FifteenMinutes; - case ZoomFactor::FifteenMinutes: - return ZoomFactor::ThirtyMinutes; - case ZoomFactor::ThirtyMinutes: - return ZoomFactor::SixtyMinutes; - case ZoomFactor::SixtyMinutes: - return ZoomFactor::TwoHours; - case ZoomFactor::TwoHours: - return ZoomFactor::FourHours; - case ZoomFactor::FourHours: - return ZoomFactor::EightHours; - case ZoomFactor::EightHours: - return ZoomFactor::TwelveHours; - case ZoomFactor::TwelveHours: - return ZoomFactor::OneDay; - case ZoomFactor::OneDay: - return ZoomFactor::OneDay; - } - return factor; -} - -Factor LTSTIMELINE_EXPORT decrement(Factor factor) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return ZoomFactor::FifteenSeconds; - case ZoomFactor::ThirtySeconds: - return ZoomFactor::FifteenSeconds; - case ZoomFactor::SixtySeconds: - return ZoomFactor::ThirtySeconds; - case ZoomFactor::TwoMinutes: - return ZoomFactor::SixtySeconds; - case ZoomFactor::ThreeMinutes: - return ZoomFactor::TwoMinutes; - case ZoomFactor::FiveMinutes: - return ZoomFactor::ThreeMinutes; - case ZoomFactor::TenMinutes: - return ZoomFactor::FiveMinutes; - case ZoomFactor::FifteenMinutes: - return ZoomFactor::TenMinutes; - case ZoomFactor::ThirtyMinutes: - return ZoomFactor::FifteenMinutes; - case ZoomFactor::SixtyMinutes: - return ZoomFactor::ThirtyMinutes; - case ZoomFactor::TwoHours: - return ZoomFactor::SixtyMinutes; - case ZoomFactor::FourHours: - return ZoomFactor::TwoHours; - case ZoomFactor::EightHours: - return ZoomFactor::FourHours; - case ZoomFactor::TwelveHours: - return ZoomFactor::EightHours; - case ZoomFactor::OneDay: - return ZoomFactor::TwelveHours; - } - return factor; -} - -Factor ceil(int value) -{ - if (value >= Factor::OneDay) - return Factor::OneDay; - - Factor ret = Factor::FifteenSeconds; - while (static_cast(ret) < value) - { - ret = increment(ret); - } - return ret; -} - -Factor floor(int value) -{ - if (value <= Factor::FifteenSeconds) - return Factor::FifteenSeconds; - - Factor ret = Factor::OneDay; - while (static_cast(ret) > value) - { - ret = decrement(ret); - } - return ret; -} - -QVector availableZoomFactors(Factor min, Factor max) -{ - if (min > max) - return {}; - - ZoomFactors ret; - ret.reserve(numberOfZoomFactors()); - Factor factor = min; - do - { - ret.append(factor); - if (factor == max) - break; - factor = increment(factor); - } while (increment(factor) > factor); - return ret; -} - -QString toString(ZoomFactor factor, bool useShort) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return useShort ? QApplication::tr("15 sec") : QApplication::tr("15 Seconds"); - case ZoomFactor::ThirtySeconds: - return useShort ? QApplication::tr("30 sec") : QApplication::tr("30 Seconds"); - case ZoomFactor::SixtySeconds: - return useShort ? QApplication::tr("1 min") : QApplication::tr("1 Minute"); - case ZoomFactor::TwoMinutes: - return useShort ? QApplication::tr("2 min") : QApplication::tr("2 Minutes"); - case ZoomFactor::ThreeMinutes: - return useShort ? QApplication::tr("3 min") : QApplication::tr("3 Minutes"); - case ZoomFactor::FiveMinutes: - return useShort ? QApplication::tr("5 min") : QApplication::tr("5 Minutes"); - case ZoomFactor::TenMinutes: - return useShort ? QApplication::tr("10 min") : QApplication::tr("10 Minutes"); - case ZoomFactor::FifteenMinutes: - return useShort ? QApplication::tr("15 min") : QApplication::tr("15 Minutes"); - case ZoomFactor::ThirtyMinutes: - return useShort ? QApplication::tr("30 min") : QApplication::tr("30 Minutes"); - case ZoomFactor::SixtyMinutes: - return useShort ? QApplication::tr("1 h") : QApplication::tr("1 Hour"); - case ZoomFactor::TwoHours: - return useShort ? QApplication::tr("2 h") : QApplication::tr("2 Hours"); - case ZoomFactor::FourHours: - return useShort ? QApplication::tr("4 h") : QApplication::tr("4 Hours"); - case ZoomFactor::EightHours: - return useShort ? QApplication::tr("8 h") : QApplication::tr("8 Hours"); - case ZoomFactor::TwelveHours: - return useShort ? QApplication::tr("12 h") : QApplication::tr("12 Hours"); - case ZoomFactor::OneDay: - return useShort ? QApplication::tr("1 d") : QApplication::tr("1 Day"); - } - return useShort ? QApplication::tr("N/A") : QApplication::tr("Unknown"); -} - -QString toShortString(Factor factor) -{ - return toString(factor, true); -} - -int majorInterval(ZoomFactor factor) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return 3000; - case ZoomFactor::ThirtySeconds: - return 5000; - case ZoomFactor::SixtySeconds: - return 10000; - case ZoomFactor::TwoMinutes: - return 15000; - case ZoomFactor::ThreeMinutes: - return 30000; - case ZoomFactor::FiveMinutes: - return 60000; - case ZoomFactor::TenMinutes: - return 60000; - case ZoomFactor::FifteenMinutes: - return 180000; - case ZoomFactor::ThirtyMinutes: - return 300000; - case ZoomFactor::SixtyMinutes: - return 600000; - case ZoomFactor::TwoHours: - return 900000; - case ZoomFactor::FourHours: - return 1800000; - case ZoomFactor::EightHours: - return 3600000; - case ZoomFactor::TwelveHours: - return 5400000; - case ZoomFactor::OneDay: - return 7200000; - } - return -1; -} - -int mediumInterval(ZoomFactor factor) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return -1; - case ZoomFactor::ThirtySeconds: - return -1; - case ZoomFactor::SixtySeconds: - return 5000; - case ZoomFactor::TwoMinutes: - return 6000; - case ZoomFactor::ThreeMinutes: - return 10000; - case ZoomFactor::FiveMinutes: - return 15000; - case ZoomFactor::TenMinutes: - return 20000; - case ZoomFactor::FifteenMinutes: - return 60000; - case ZoomFactor::ThirtyMinutes: - return 100000; - case ZoomFactor::SixtyMinutes: - return 150000; - case ZoomFactor::TwoHours: - return 300000; - case ZoomFactor::FourHours: - return 600000; - case ZoomFactor::EightHours: - return 900000; - case ZoomFactor::TwelveHours: - return 1800000; - case ZoomFactor::OneDay: - return 2400000; - } - return -1; -} - -int minorInterval(ZoomFactor factor) -{ - switch (factor) - { - case ZoomFactor::FifteenSeconds: - return 1000; - case ZoomFactor::ThirtySeconds: - return 1000; - case ZoomFactor::SixtySeconds: - return 1000; - case ZoomFactor::TwoMinutes: - return 3000; - case ZoomFactor::ThreeMinutes: - return 5000; - case ZoomFactor::FiveMinutes: - return 5000; - case ZoomFactor::TenMinutes: - return 10000; - case ZoomFactor::FifteenMinutes: - return 20000; - case ZoomFactor::ThirtyMinutes: - return 25000; - case ZoomFactor::SixtyMinutes: - return 50000; - case ZoomFactor::TwoHours: - return 60000; - case ZoomFactor::FourHours: - return 150000; - case ZoomFactor::EightHours: - return 300000; - case ZoomFactor::TwelveHours: - return 600000; - case ZoomFactor::OneDay: - return 800000; - } - return -1; -} - -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_utils.h b/Lts/Common/LtsTimeline/timeline_utils.h deleted file mode 100755 index 2b710c471..000000000 --- a/Lts/Common/LtsTimeline/timeline_utils.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include - -#include - - -namespace LtsTimeline -{ - -struct LTSTIMELINE_EXPORT Tick -{ - int pos{-1}; - qint64 time{0}; - QString label; - Tick() {} - explicit Tick(qint64 time) : time(time) {} - explicit Tick(int pos, qint64 time, const QString &label) : pos(pos), time(time), label(label) {} -}; - -typedef QVector Ticks; - - - - - -namespace DateTime -{ - -enum Format { DTG = 0, ISODate = Qt::ISODate, Standard = 2, DTGShort = 3, DTGVeryShort, TimeOnly = 5 }; - -extern int numberOfDateTimeFormats(); - -extern LTSTIMELINE_EXPORT QString toString(qint64 time, Format format); - -} - -typedef DateTime::Format DateTimeFormat; - - - - - -namespace Zoom -{ - -enum Factor : int -{ - FifteenSeconds = 15 * 1000, ThirtySeconds = 30 * 1000, SixtySeconds = 60 * 1000, - TwoMinutes = 2 * 60 * 1000, ThreeMinutes = 3 * 60 * 1000, FiveMinutes = 5 * 60 * 1000, - TenMinutes = 10 * 60 * 1000, FifteenMinutes = 15 * 60 * 1000, ThirtyMinutes = 30 * 60 * 1000, SixtyMinutes = 60 * 60 * 1000, - TwoHours = 2 * 60 * 60 * 1000, FourHours = 4 * 60 * 60 * 1000, EightHours = 8 * 60 * 60 * 1000, TwelveHours = 12 * 60 * 60 * 1000, - OneDay = 24 * 60 * 60 * 1000 -}; - -extern LTSTIMELINE_EXPORT int numberOfZoomFactors(); - -extern LTSTIMELINE_EXPORT Factor increment(Factor factor); - -extern LTSTIMELINE_EXPORT Factor decrement(Factor factor); - -extern LTSTIMELINE_EXPORT Factor ceil(int value); - -extern LTSTIMELINE_EXPORT Factor floor(int value); - -extern LTSTIMELINE_EXPORT QVector availableZoomFactors(Factor min, Factor max); - -extern LTSTIMELINE_EXPORT QString toString(Factor factor, bool useShort = false); - -extern LTSTIMELINE_EXPORT QString toShortString(Factor factor); - -extern LTSTIMELINE_EXPORT int majorInterval(Factor factor); - -extern LTSTIMELINE_EXPORT int mediumInterval(Factor factor); - -extern LTSTIMELINE_EXPORT int minorInterval(Factor factor); - -} - -typedef Zoom::Factor ZoomFactor; -typedef QVector ZoomFactors; - -} diff --git a/Lts/Common/LtsTimeline/timeline_view.cpp b/Lts/Common/LtsTimeline/timeline_view.cpp deleted file mode 100755 index b02f46d36..000000000 --- a/Lts/Common/LtsTimeline/timeline_view.cpp +++ /dev/null @@ -1,672 +0,0 @@ - - - -#include "timeline_view.h" -#include "item_delegate.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -const int TimelineView::s_textHeight = 20; - - -TimelineView::TimelineView(QWidget *parent) - : QTreeView(parent) - , m_header(new TimelineHeaderView(this)) - , m_dragHandler(new TimelineViewDragHandler(this)) - , m_resizeHandler(new TimelineViewResizeHandler(this)) - , m_selectHandler(new TimelineViewSelectHandler(this)) - , m_trackHandler(new TimelineViewTrackHandler(this)) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineView")) -{ - setAlternatingRowColors(true); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setSelectionBehavior(QAbstractItemView::SelectItems); - setSelectionMode(QAbstractItemView::NoSelection); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - setHeader(m_header); - setAcceptDrops(true); -} - - - - -void TimelineView::setHeader(QHeaderView *header) -{ - try - { - if (m_header && m_header != header) - delete m_header; - m_header = qobject_cast(header); - QTreeView::setHeader(m_header); - connect(m_header, &TimelineHeaderView::indicatorTimeChanged, this, [this]() - { - viewport()->update(); - }); - connect(m_header, &TimelineHeaderView::indicatorVisibilityChanged, this, [this]() - { - viewport()->update(); - }); - connect(m_header, &TimelineHeaderView::hoverPositionChanged, this, [this]() - { - if (m_model != nullptr) - emit m_model->layoutChanged(); - }); - connect(m_header, &TimelineHeaderView::hoverVisibilityChanged, this, [this]() - { - viewport()->update(); - }); - } - catch (...) - { - ERROR_LOG(m_logger) << "header view should be of type TimelineHeaderView"; - } -} - - -void TimelineView::setModel(QAbstractItemModel *model) -{ - m_model = model; - QTreeView::setModel(model); - setMouseTracking(true); - - - m_timelineModel = qobject_cast(model); - - if (m_timelineModel != nullptr) - return; - - - m_proxyModel = qobject_cast(model); - if (m_proxyModel != nullptr) - m_timelineModel = qobject_cast(m_proxyModel->sourceModel()); - - if (m_timelineModel == nullptr) - qWarning() << "TimelineSelectHandler:c_tor: model is not a QAbstractItemModel!"; - - Q_ASSERT(m_timelineModel != nullptr); -} - - -void TimelineView::setProxyModel(QAbstractProxyModel *proxy) noexcept(false) -{ - if (!qobject_cast(proxy->sourceModel())) - throw QStringLiteral("source model cannot be cast to TimelineModel"); - - m_model = qobject_cast(proxy->sourceModel()); - m_proxyModel = proxy; - QTreeView::setModel(m_proxyModel); -} - - -void TimelineView::setItemDelegate(QAbstractItemDelegate *delegate) -{ - try - { - m_delegate = static_cast(delegate); - QTreeView::setItemDelegate(m_delegate); - } - catch (...) - { - ERROR_LOG(m_logger) << "item delegate for TimelineView must be derived from ItemDelegate"; - } -} - - -void TimelineView::setZoomFactor(ZoomFactor zoomFactor) -{ - if (zoomFactor == m_zoomFactor) - return; - - if (m_minZoomFactor > zoomFactor) - { - setZoomFactor(m_minZoomFactor); - return; - } - - if (m_upperZoomFactor < zoomFactor) - { - setZoomFactor(m_upperZoomFactor); - return; - } - - m_zoomFactor = zoomFactor; - emit zoomChanged(m_zoomFactor); - if (m_currentTime + m_zoomFactor > endTime()) - setCurrentTime(endTime() - m_zoomFactor); - else - viewport()->update(); -} - -void TimelineView::setMinimumZoomFactor(ZoomFactor zoomFactor) -{ - if (zoomFactor == m_minZoomFactor) - return; - - m_minZoomFactor = zoomFactor; - if (m_zoomFactor < m_minZoomFactor) - setZoomFactor(m_minZoomFactor); -} - -void TimelineView::setMaximumZoomFactor(ZoomFactor zoomFactor) -{ - if (zoomFactor == m_maxZoomFactor) - return; - - m_maxZoomFactor = zoomFactor; - if (m_upperZoomFactor > m_maxZoomFactor) - m_upperZoomFactor = m_maxZoomFactor; - if (m_zoomFactor > m_maxZoomFactor) - setZoomFactor(m_maxZoomFactor); -} - - -void TimelineView::setStartTime(qint64 time) -{ - if (time == m_startTime || time < 0) - return; - - m_startTime = time; - if (m_currentTime < m_startTime) - setCurrentTime(m_startTime); - if (m_duration > 0) - if (m_currentTime > endTime() - m_zoomFactor) - setCurrentTime(std::max(endTime() - m_zoomFactor, m_startTime)); - emit rangeChanged(m_startTime, endTime()); -} - -void TimelineView::setCurrentTime(qint64 time) -{ - if (time == m_currentTime) - return; - - if (time < m_startTime) - time = m_startTime; - if (m_duration > 0) - if (time > endTime() - m_zoomFactor) - time = std::max(endTime() - m_zoomFactor, m_startTime); - - if (time == m_currentTime) - return; - - m_currentTime = time; - emit currentTimeChanged(m_currentTime); - viewport()->update(); -} - -void TimelineView::setEndTime(qint64 time) -{ - if (time == m_startTime + m_duration) - return; - - if (time - m_startTime < m_minZoomFactor) - time = m_startTime + m_minZoomFactor; - - setDuration(time - m_startTime); -} - -void TimelineView::setDuration(int duration) -{ - if (duration == m_duration) - return; - - if (duration <= m_minZoomFactor) - duration = m_minZoomFactor; - m_duration = duration; - emit rangeChanged(m_startTime, endTime()); - ZoomFactor upperZoomFactor = Zoom::floor(duration); - m_upperZoomFactor = std::min(upperZoomFactor, m_maxZoomFactor); - if (duration < m_zoomFactor) - setZoomFactor(m_upperZoomFactor); - if (m_currentTime > endTime()) - setCurrentTime(endTime() - m_zoomFactor); - viewport()->update(); -} - -void TimelineView::setRange(qint64 start, qint64 end) -{ - if (start == m_startTime && end - start == m_duration) - return; - - if (start > end) - { - m_startTime = end; - m_duration = std::max(start - m_startTime, static_cast(m_minZoomFactor)); - } - else - { - m_startTime = start; - m_duration = std::max(end - m_startTime, static_cast(m_minZoomFactor)); - } - if (m_currentTime < m_startTime) - m_currentTime = m_startTime; - if (m_duration > 0) - if (m_currentTime > endTime() - m_zoomFactor) - m_currentTime = std::max(endTime() - m_zoomFactor, m_startTime); - ZoomFactor upperZoomFactor = Zoom::floor(m_duration); - m_upperZoomFactor = upperZoomFactor > m_maxZoomFactor ? m_maxZoomFactor : upperZoomFactor; - emit rangeChanged(m_startTime, endTime()); - viewport()->update(); -} - - -void TimelineView::setCursorTextVisible(bool visible) -{ - m_isCursorTextVisible = visible; - viewport()->update(); -} - -void TimelineView::setCursorText(const QString &text) -{ - m_cursorText = text; - viewport()->update(); -} - - -int TimelineView::toPixel(qint64 time) const -{ - return m_header ? m_header->toPixel(time) : -1; -} - -qint64 TimelineView::toTime(int pixel) const -{ - return m_header ? m_header->toTime(pixel) : -1; -} - -qint64 TimelineView::toDuration(int pixel) const -{ - if (!m_header) - return -1; - - int pos = m_header->sectionPosition(Section::eItems); - return double(pixel - pos) / (geometry().width() - pos) * m_zoomFactor; -} - -int TimelineView::fromDuration(qint64 time) const -{ - if (!m_header) - return -1; - - return (time - m_startTime) / m_zoomFactor * (geometry().width() - m_header->sectionPosition(Section::eItems)); -} - - -void TimelineView::clearSelectedItems() -{ - m_selectedItemIds.clear(); - emit selectedItemsChanged(m_selectedItemIds); - viewport()->update(); -} - -void TimelineView::selectAllItems() -{ - if (m_model == nullptr) - return; - - m_selectedItemIds.clear(); - m_selectedItemIds = m_timelineModel->itemIds(); - emit selectedItemsChanged(m_selectedItemIds); - viewport()->update(); -} - -void TimelineView::selectItem(int itemId, bool reset) -{ - if (reset) - m_selectedItemIds.clear(); - else if (m_selectedItemIds.contains(itemId)) - return; - m_selectedItemIds.append(itemId); - emit selectedItemsChanged(m_selectedItemIds); - viewport()->update(); -} - -void TimelineView::selectItems(const QVector &itemIds, bool reset) -{ - if (reset) - m_selectedItemIds.clear(); - for (const int &itemId : qAsConst(itemIds)) - if (!m_selectedItemIds.contains(itemId)) - m_selectedItemIds.append(itemId); - emit selectedItemsChanged(m_selectedItemIds); - viewport()->update(); -} - -void TimelineView::unselectItem(int itemId) -{ - const int iItem = m_selectedItemIds.indexOf(itemId); - if (iItem == -1) - return; - m_selectedItemIds.remove(iItem); - emit selectedItemsChanged(m_selectedItemIds); - viewport()->update(); -} - - -bool TimelineView::isCursorWithinRangeX(int x) const -{ - if (m_header) - return m_header->sectionPosition(Section::eItems) < x && x < geometry().right(); - return false; -} - -bool TimelineView::isCursorWithinRangeY(int y) const -{ - return 0 < y && y < geometry().bottom(); - -} - - - - -void TimelineView::dragEnterEvent(QDragEnterEvent *event) -{ - if (m_dragHandler) - m_dragHandler->dragEnterEvent(event); -} - -void TimelineView::dragMoveEvent(QDragMoveEvent *event) -{ - if (m_dragHandler) - m_dragHandler->dragMoveEvent(event); -} - -void TimelineView::dragLeaveEvent(QDragLeaveEvent *event) -{ - if (m_dragHandler) - m_dragHandler->dragLeaveEvent(event); -} - -void TimelineView::dropEvent(QDropEvent *event) -{ - if (m_dragHandler) - m_dragHandler->dropEvent(event); -} - -void TimelineView::mouseDoubleClickEvent(QMouseEvent *event) -{ - if (m_trackHandler) - m_trackHandler->mouseDoubleClickEvent(event); - if (m_selectHandler) - m_selectHandler->mouseDoubleClickEvent(event); -} - -void TimelineView::mouseMoveEvent(QMouseEvent *event) -{ - m_cursorPosition = event->pos(); - - - const QModelIndex index(indexAt(event->pos())); - - if (m_header) - { - m_header->setHoverPosition(isCursorWithinRange(event->pos()) ? event->pos().x() : -1); - if (m_header->mouseHandler()->isBusy()) - { - m_header->mouseHandler()->mouseMoveEvent(event); - return; - } - if (cursor() == Qt::SizeHorCursor) - { - if (m_resizeHandler) - { - if (!m_resizeHandler->isActive()) - setCursor(Qt::ArrowCursor); - } - else - setCursor(Qt::ArrowCursor); - } - } - - if (!m_model) - return; - - if (m_dragHandler && m_timelineModel->isEditable()) - if (cursor().shape() == Qt::ClosedHandCursor || cursor().shape() == Qt::ForbiddenCursor) - if (!m_dragHandler->m_dragItemIds.isEmpty() || !m_selectedItemIds.isEmpty()) - { - m_dragHandler->mouseMoveEvent(event); - return; - } - - if (m_resizeHandler && m_timelineModel->isEditable()) - if (cursor().shape() == Qt::SizeHorCursor) - if (m_resizeHandler->isActive()) - { - m_resizeHandler->mouseMoveEvent(event); - return; - } - - if (m_selectHandler) - m_selectHandler->mouseMoveEvent(event); - - if (!index.isValid() || index.column() == Section::eActuator) - { - if (m_resizeHandler && m_timelineModel->isEditable()) - if (!m_resizeHandler->isActive()) - setCursor(QCursor(Qt::ArrowCursor)); - - event->ignore(); - return; - } - - if (!m_timelineModel->isEditable()) - return; - - try - { - Track &track = m_timelineModel->track(index.data().toInt()); - Item &item = track.itemAt(toTime(event->pos().x())); - int startPos = toPixel(item.startTime()); - int endPos = toPixel(item.startTime() + item.duration()); - if (endPos - startPos < 15 || (startPos + 5 < event->pos().x() && event->pos().x() < endPos - 5)) - setCursor(QCursor(Qt::ArrowCursor)); - else - setCursor(QCursor(Qt::SizeHorCursor)); - } - catch (...) - { - setCursor(QCursor(Qt::ArrowCursor)); - } -} - -void TimelineView::mousePressEvent(QMouseEvent *event) -{ - if (!m_model) - return; - - if (m_header) - if (m_header->mouseHandler()->isBusy()) - { - m_header->mouseHandler()->mousePressEvent(event); - return; - } - - - const QModelIndex index = indexAt(event->pos()); - if (index.column() == Section::eActuator) - { - QTreeView::mousePressEvent(event); - if (m_trackHandler) - m_trackHandler->mousePressEvent(event); - return; - } - - if (m_timelineModel->isEditable() && cursor().shape() == Qt::SizeHorCursor) - { - if (m_resizeHandler) - m_resizeHandler->mousePressEvent(event); - } - else - { - if (m_selectHandler) - m_selectHandler->mousePressEvent(event); - - if (m_dragHandler && m_timelineModel->isEditable()) - { - m_dragHandler->mousePressEvent(event); - if (m_dragHandler->isActive()) - return; - } - - if (event->button() == Qt::RightButton) - emit customContextMenuRequested(event->pos()); - } -} - -void TimelineView::mouseReleaseEvent(QMouseEvent *event) -{ - if (!m_model) - return; - - if (m_header) - if (m_header->mouseHandler()->isBusy()) - { - m_header->mouseHandler()->mouseReleaseEvent(event); - return; - } - - if (m_trackHandler) - m_trackHandler->mouseReleaseEvent(event); - - if (m_dragHandler && m_timelineModel->isEditable()) - if (m_dragHandler->isActive()) - { - m_dragHandler->mouseReleaseEvent(event); - viewport()->update(); - return; - } - - if (m_resizeHandler && m_timelineModel->isEditable()) - if (m_resizeHandler->isActive()) - { - m_resizeHandler->mouseReleaseEvent(event); - viewport()->update(); - return; - } - - if (m_selectHandler) - if (m_selectHandler->isActive() || !m_timelineModel->isEditable()) - m_selectHandler->mouseReleaseEvent(event); - - viewport()->update(); -} - -void TimelineView::keyPressEvent(QKeyEvent *event) -{ - if (m_model == nullptr) - return; - if (m_timelineModel->isEditable()) - { - if (m_resizeHandler) - if (m_resizeHandler->isActive()) - return; - - if (m_dragHandler) - m_dragHandler->keyPressEvent(event); - } - if (m_selectHandler) - m_selectHandler->keyPressEvent(event); - - viewport()->update(); -} - -void TimelineView::wheelEvent(QWheelEvent *event) -{ - if (event->pos().y() <= m_header->geometry().bottom()) - m_header->wheelEvent(event); - else if (QGuiApplication::queryKeyboardModifiers() == Qt::ControlModifier) - { - if (event->angleDelta().y() < 0) - setZoomFactor(Zoom::increment(m_zoomFactor)); - else - setZoomFactor(Zoom::decrement(m_zoomFactor)); - } - else - QTreeView::wheelEvent(event); -} - -void TimelineView::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QWidget *viewPort = viewport(); - QPainter painter(viewPort); - - painter.save(); - QTreeView::paintEvent(event); - if (m_dragHandler) - { - ItemDelegate *delegate = qobject_cast(itemDelegate()); - if (delegate) - delegate->paintDragItems(&painter); - } - painter.restore(); - - painter.save(); - - painter.setClipping(true); - - - if (m_selectHandler) - if (m_selectHandler->isRubberBandActive()) - { - QColor color = TimelineHeaderView::highlightColor(); - painter.setPen(color); - color.setAlpha(100); - painter.setBrush(QBrush(color)); - painter.drawRect(m_selectHandler->m_rubberBand); - } - - - if (m_header->isIndicatorVisible() && m_header->indicatorPosition() != -1 && - m_header->indicatorPosition() >= m_header->sectionPosition(Section::eItems)) - { - painter.setPen(TimelineHeaderView::highlightColor()); - painter.drawLine(m_header->indicatorPosition(), 0, m_header->indicatorPosition(), geometry().bottom()); - } - - - if (m_header->isHoverVisible() && m_header->hoverPosition() != -1) - { - painter.setPen(TimelineHeaderView::highlightColor()); - painter.drawLine(m_header->hoverPosition(), 0, m_header->hoverPosition(), geometry().bottom()); - if (m_isCursorTextVisible && !m_cursorText.isEmpty()) - { - painter.setBrush(QBrush(TimelineHeaderView::backgroundColor())); - QRect rect = QRect( - m_cursorPosition.x(), m_cursorPosition.y() - s_textHeight - 2, - painter.fontMetrics().horizontalAdvance(m_cursorText) + 2, s_textHeight + 2 - ); - painter.drawRect(rect); - painter.drawText(rect.adjusted(1, 1, -1, -1), Qt::AlignCenter, m_cursorText); - } - } - - - if (m_header->isLiveIndicatorVisible() && m_header->liveIndicatorTime() != -1 && - m_currentTime <= m_header->liveIndicatorTime() && m_header->liveIndicatorTime() <= m_currentTime + m_zoomFactor) - { - painter.setPen(m_header->liveIndicatorColor()); - painter.drawLine(m_header->liveIndicatorPosition(), 0, m_header->liveIndicatorPosition(), geometry().bottom()); - } - - painter.restore(); -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_view.h b/Lts/Common/LtsTimeline/timeline_view.h deleted file mode 100755 index 87d667bb5..000000000 --- a/Lts/Common/LtsTimeline/timeline_view.h +++ /dev/null @@ -1,175 +0,0 @@ - - - -#pragma once - -#include "timeline_view_drag_handler.h" -#include "timeline_view_resize_handler.h" -#include "timeline_view_selection_handler.h" -#include "timeline_view_track_handler.h" -#include "timeline_header_view.h" -#include "timeline_model.h" - -#include - -#include - -#include -#include - - -class QAbstractProxyModel; - - -namespace LtsTimeline -{ - -class TimelineViewDragHandler; -class TimelineViewResizeHandler; -class TimelineViewSelectHandler; -class TimelineViewTrackHandler; -class TimelineWidget; - - -class LTSTIMELINE_EXPORT TimelineView : public QTreeView -{ - Q_OBJECT - - friend class ItemDelegate; - friend class TimelineHeaderView; - friend class TimelineViewDragHandler; - friend class TimelineViewResizeHandler; - friend class TimelineViewSelectHandler; - friend class TimelineViewTrackHandler; - friend class TimelineWidget; - -public: - static const int s_textHeight; - - - - explicit TimelineView(QWidget *parent = nullptr); - - TimelineHeaderView *header() { return m_header; } - void setHeader(QHeaderView *header); - - QAbstractItemModel *model() { return m_model; } - TimelineModel *timelineModel() { return m_timelineModel; } - void setModel(QAbstractItemModel *model) override; - - QAbstractProxyModel *proxyModel() { return m_proxyModel; } - void setProxyModel(QAbstractProxyModel *proxy) noexcept(false); - - void setItemDelegate(QAbstractItemDelegate *delegate); - ItemDelegate *itemDelegate() const { return m_delegate; } - - ZoomFactor zoomFactor() const { return m_zoomFactor; } - - - void setZoomFactor(ZoomFactor zoomFactor); - - ZoomFactor minimumZoomFactor() const { return m_minZoomFactor; } - void setMinimumZoomFactor(ZoomFactor zoomFactor); - - ZoomFactor maximumZoomFactor() const { return m_maxZoomFactor; } - void setMaximumZoomFactor(ZoomFactor zoomFactor); - - ZoomFactor upperZoomFactor() const { return m_upperZoomFactor; } - - qint64 startTime() const { return m_startTime; } - void setStartTime(qint64 time); - - qint64 currentTime() const { return m_currentTime; } - void setCurrentTime(qint64 time); - - qint64 endTime() const { return m_startTime + m_duration; } - void setEndTime(qint64 time); - - int duration() const { return m_duration; } - void setDuration(int duration); - - void setRange(qint64 start, qint64 end); - - bool isCursorTextVisible() const { return m_isCursorTextVisible; } - void setCursorTextVisible(bool visible); - void setCursorText(const QString &text); - - int toPixel(qint64 time) const; - qint64 toTime(int pixel) const; - qint64 toDuration(int pixel) const; - int fromDuration(qint64 time) const; - - const QVector &selectedItemIds() const { return m_selectedItemIds; } - bool hasSelectedItems() const { return !m_selectedItemIds.isEmpty(); } - void clearSelectedItems(); - void selectAllItems(); - void selectItem(int itemId, bool reset = true); - void selectItems(const QVector &itemIds, bool reset = true); - void unselectItem(int itemId); - - TimelineViewDragHandler *dragHandler() { return m_dragHandler; } - void setDragHandler(TimelineViewDragHandler *dragHandler) { m_dragHandler = dragHandler; } - TimelineViewResizeHandler *resizeHandler() { return m_resizeHandler; } - void setResizeHandler(TimelineViewResizeHandler *resizeHandler) { m_resizeHandler = resizeHandler; } - TimelineViewSelectHandler *selectHandler() { return m_selectHandler; } - void setSelectHandler(TimelineViewSelectHandler *selectHandler) { m_selectHandler = selectHandler; } - TimelineViewTrackHandler *trackHandler() { return m_trackHandler; } - void setTrackHandler(TimelineViewTrackHandler *trackHandler) { m_trackHandler = trackHandler; } - - bool isCursorWithinRange(const QPoint &pos) const { return isCursorWithinRangeX(pos.x()) && isCursorWithinRangeY(pos.y()); } - bool isCursorWithinRangeX(int x) const; - bool isCursorWithinRangeY(int y) const; - -protected: - void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; - void wheelEvent(QWheelEvent *event) override; - virtual void paintEvent(QPaintEvent *event) override; - -signals: - void rangeChanged(qint64, qint64); - void currentTimeChanged(qint64); - void zoomChanged(ZoomFactor); - void trackActivated(int); - void selectedItemsChanged(const QVector &); - void itemActivated(int); - void currentItemTimeChanged(); - -private: - TimelineHeaderView *m_header{nullptr}; - TimelineModel *m_timelineModel{nullptr}; - QAbstractItemModel *m_model{nullptr}; - QAbstractProxyModel *m_proxyModel{nullptr}; - ItemDelegate *m_delegate{nullptr}; - TimelineViewDragHandler *m_dragHandler{nullptr}; - TimelineViewResizeHandler *m_resizeHandler{nullptr}; - TimelineViewSelectHandler *m_selectHandler{nullptr}; - TimelineViewTrackHandler *m_trackHandler{nullptr}; - - ZoomFactor m_zoomFactor{ZoomFactor::ThirtyMinutes}; - ZoomFactor m_upperZoomFactor{ZoomFactor::OneDay}; - ZoomFactor m_minZoomFactor{ZoomFactor::FifteenSeconds}; - ZoomFactor m_maxZoomFactor{ZoomFactor::OneDay}; - - qint64 m_startTime{-1}; - qint64 m_currentTime{-1}; - int m_duration{-1}; - - bool m_isCursorTextVisible{false}; - QString m_cursorText; - QPoint m_cursorPosition; - - QVector m_selectedItemIds; - - NGL::INGLLogger &m_logger; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp b/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp deleted file mode 100755 index d2d5c2bf9..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp +++ /dev/null @@ -1,505 +0,0 @@ -#include "timeline_view_drag_handler.h" -#include "item.h" -#include "track.h" - -#include -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -TimelineViewDragHandler::TimelineViewDragHandler(TimelineView *view) - : m_view(view) -{} - -TimelineViewDragHandler::~TimelineViewDragHandler() -{ - m_view = nullptr; -} - - - - -void TimelineViewDragHandler::dragEnterEvent(QDragEnterEvent *e) -{ - if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - { - e->ignore(); - return; - } - if (e->mimeData()->hasFormat(Item::mimetype)) - e->acceptProposedAction(); - else - e->ignore(); -} - -void TimelineViewDragHandler::dragMoveEvent(QDragMoveEvent *e) -{ - if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - { - e->ignore(); - return; - } - if (!e->mimeData()->hasFormat(Item::mimetype)) - return; - - m_dragPos = e->pos(); - m_view->m_header->setHoverPosition(m_dragPos.x()); - e->setAccepted(canDragItemsTo(e->pos())); - m_view->viewport()->update(); -} - -void TimelineViewDragHandler::dragLeaveEvent(QDragLeaveEvent *e) -{ - Q_UNUSED(e); - m_dragPos = {}; -} - -void TimelineViewDragHandler::dropEvent(QDropEvent *e) -{ - if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty()) - { - e->ignore(); - return; - } - if (!e->mimeData()->hasFormat(Item::mimetype)) - return; - - if (!canDragItemsTo(e->pos())) - { - clear(); - e->acceptProposedAction(); - return; - } - - qint64 dt = deltaTime(); - try - { - for (const int &itemId : qAsConst(m_dragItemIds)) - { - const Item &item = m_view->timelineModel()->item(itemId); - m_view->timelineModel()->moveItemToTrack(itemId, dragItemTrackId(item), item.startTime() + dt); - } - } - catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; } - clear(); - e->acceptProposedAction(); -} - -void TimelineViewDragHandler::mouseMoveEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - - if (e->buttons() != Qt::LeftButton) - { - clear(); - return; - } - - if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - return; - - if ((e->pos() - m_dragStartPos).manhattanLength() < QApplication::startDragDistance()) - return; - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() == Section::eActuator) - { - m_view->setCursor(QCursor(Qt::ForbiddenCursor)); - return; - } - - m_dragPos = e->pos(); - m_view->setCursor(QCursor(canDragItemsTo(m_dragPos) ? Qt::ClosedHandCursor : Qt::ForbiddenCursor)); - if (index.data().toInt() == m_dragStartIndex.data().toInt()) - return; - - QMimeData *mimeData = new QMimeData(); - mimeData->setData(Item::mimetype, {}); - QDrag *drag = new QDrag(m_view); - drag->setMimeData(mimeData); - if (drag->exec(Qt::MoveAction) == Qt::MoveAction) - { - - } - else - { - - } -} - -void TimelineViewDragHandler::mousePressEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - - if (e->button() != Qt::LeftButton) - return; - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() == Section::eActuator) - return; - - try - { - Track &track = m_view->timelineModel()->track(index.data().toInt()); - Item &item = track.itemAt(m_view->toTime(e->pos().x())); - m_dragItemIds = m_view->m_selectedItemIds; - if (!m_dragItemIds.contains(item.id())) - m_dragItemIds = { item.id() }; - - for (const int &itemId : m_dragItemIds) - if (!m_view->timelineModel()->item(itemId).isEditable()) - return; - - m_dragStartIndex = index; - m_dragStartTime = m_view->toTime(e->pos().x()); - m_dragStartPos = e->pos(); - m_dragPos = m_dragStartPos; - m_view->setCursor(QCursor(Qt::ClosedHandCursor)); - } - catch (const QString &str) { DEBUG_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) {} -} - -void TimelineViewDragHandler::mouseReleaseEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - - if (e->button() != Qt::LeftButton) - return; - - if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - return; - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() == Section::eActuator) - return; - - if (index.data().toInt() != m_dragStartIndex.data().toInt()) - return; - - m_dragPos = e->pos(); - if (m_dragPos.x() == m_dragStartPos.x()) - { - clear(); - return; - } - if (!canMoveItemsTo(m_dragPos)) - { - clear(); - return; - } - - emit m_view->currentItemTimeChanged(); - qint64 dt = deltaTime(); - for (const int &itemId : m_dragItemIds) - { - Item &item = m_view->timelineModel()->item(itemId); - m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt); - } - clear(); -} - -void TimelineViewDragHandler::keyPressEvent(QKeyEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - if (m_view->m_selectedItemIds.isEmpty()) - return; - - if (e->key() == Qt::Key_Escape) - { - clear(); - return; - } - - if (e->key() != Qt::Key_Left && e->key() != Qt::Key_Right) - return; - - int dt = m_view->timelineModel()->snapStep(); - if (e->key() == Qt::Key_Left) - moveItemsLeft(m_view->selectedItemIds(), QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? -dt : -10 * dt); - else - moveItemsRight(m_view->selectedItemIds(), QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? dt : 10 * dt); -} - - -bool TimelineViewDragHandler::isActive() const -{ - return !m_dragItemIds.isEmpty() && (m_dragPos - m_dragStartPos).manhattanLength() >= QApplication::startDragDistance(); -} - - -int TimelineViewDragHandler::deltaX() const -{ - if (!m_view->timelineModel()->isSnapEnabled()) - return m_dragPos.x() - m_dragStartPos.x(); - return m_view->toPixel(m_dragStartTime + deltaTime()) - m_view->toPixel(m_dragStartTime); -} - -int TimelineViewDragHandler::deltaY() const -{ - - const QModelIndex dragIndex = m_view->indexAt(m_dragPos); - if (!dragIndex.isValid()) - return m_dragPos.y() - m_dragStartPos.y(); - return m_view->visualRect(dragIndex).top() - m_view->visualRect(m_dragStartIndex).top(); -} - -qint64 TimelineViewDragHandler::deltaTime() const -{ - if (!m_view->timelineModel()->isSnapEnabled()) - return m_view->toTime(m_dragPos.x()) - m_view->toTime(m_dragStartPos.x()); - - qint64 dt = ((m_view->toTime(m_dragPos.x()) - m_dragStartTime) / m_view->timelineModel()->snapStep()) * m_view->timelineModel()->snapStep(); - int dtAbsMin = 2 * m_view->timelineModel()->snapTolerance(); - int eps = m_view->timelineModel()->snapTolerance(); - for (const int &dragItemId : m_dragItemIds) - { - const Item &dragItem = m_view->timelineModel()->item(dragItemId); - qint64 start = dragItem.startTime() + dt; - qint64 end = start + dragItem.duration(); - for (int itemId : m_view->timelineModel()->itemIds()) - { - if (m_dragItemIds.contains(itemId)) - continue; - - const Item &item = m_view->timelineModel()->item(itemId); - if (start - eps < item.startTime() + item.duration() && item.startTime() + item.duration() < start + eps) - { - qint64 dtCurrent = item.startTime() + item.duration() - dragItem.startTime(); - qint64 dtAbsCurrent = std::abs(dtCurrent); - if (dtAbsCurrent < dtAbsMin) - { - dtAbsMin = dtAbsCurrent; - dt = dtCurrent; - } - } - else if (end - eps < item.startTime() && item.startTime() < end + eps) - { - qint64 dtCurrent = item.startTime() - (dragItem.startTime() + dragItem.duration()); - qint64 dtAbsCurrent = std::abs(dtCurrent); - if (dtAbsCurrent < dtAbsMin) - { - dtAbsMin = dtAbsCurrent; - dt = dtCurrent; - } - } - } - } - return dt; -} - - - - -bool TimelineViewDragHandler::canDragItemsTo(const QPoint &point) const -{ - if (!m_view->timelineModel()->isEditable()) - return false; - - if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - return false; - - - const QModelIndex index = m_view->indexAt(point); - if (!index.isValid() || index.column() == Section::eActuator) - return false; - - if (index.data().toInt() == m_dragStartIndex.data().toInt()) - return canMoveItemsTo(point); - else - { - try - { - qint64 dt = deltaTime(); - for (const int &itemId : m_dragItemIds) - { - const Item &item = m_view->timelineModel()->item(itemId); - int trackId = dragItemTrackId(item); - if (trackId == 0) - return false; - - const Track &track = m_view->timelineModel()->track(trackId); - qint64 startNew = item.startTime() + dt; - if (!m_view->timelineModel()->canDropItem(item, track.id(), startNew)) - return false; - - if (track.isItemsBetween(startNew, startNew + item.duration(), m_dragItemIds, true)) - return false; - } - } - catch (...) - { - return true; - } - } - return true; -} - -bool TimelineViewDragHandler::canMoveItemsTo(const QPoint &point) const -{ - if (!m_view->timelineModel()->isEditable()) - return false; - if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid()) - return false; - - - const QModelIndex index = m_view->indexAt(point); - if (!index.isValid() || index.column() == Section::eActuator || index.data().toInt() != m_dragStartIndex.data().toInt()) - return false; - - qint64 dt = deltaTime(); - for (const int &itemId : m_dragItemIds) - { - const Item &item = m_view->timelineModel()->item(itemId); - qint64 startNew = item.startTime() + dt; - if (!m_view->timelineModel()->canMoveItem(item, startNew)) - return false; - - const Track &track = m_view->timelineModel()->track(item.parentTrackId()); - if (track.isItemsBetween(startNew, startNew + item.duration(), m_dragItemIds, true)) - return false; - } - return true; -} - - -void TimelineViewDragHandler::moveItemsLeft(const QVector &itemIds, int dt) -{ - Q_ASSERT(dt < 0); - try - { - for (int itemId : itemIds) - { - const Item &item = m_view->timelineModel()->item(itemId); - const Track &track = m_view->timelineModel()->track(item.parentTrackId()); - if (track.isItemsBetween(item.startTime() + dt, item.startTime() + item.duration() + dt, itemIds, true)) - { - if (dt < - m_view->timelineModel()->snapStep()) - moveItemsLeft(itemIds, dt + m_view->timelineModel()->snapStep()); - return; - } - if (!m_view->timelineModel()->canMoveItem(item, item.startTime() + dt)) - { - if (dt < - m_view->timelineModel()->snapStep()) - moveItemsLeft(itemIds, dt + m_view->timelineModel()->snapStep()); - return; - } - } - } - catch (const QString &errMsg) - { - ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed: " << errMsg; - return; - } - catch (...) - { - ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; - return; - } - - try - { - for (int itemId : itemIds) - { - Item &item = m_view->timelineModel()->item(itemId); - m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt); - } - } - catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) {} -} - -void TimelineViewDragHandler::moveItemsRight(const QVector &itemIds, int dt) -{ - Q_ASSERT(dt > 0); - try - { - for (int itemId : itemIds) - { - const Item &item = m_view->timelineModel()->item(itemId); - const Track &track = m_view->timelineModel()->track(item.parentTrackId()); - if (track.isItemsBetween(item.startTime() + dt, item.startTime() + item.duration() + dt, itemIds, true)) - { - if (dt > m_view->timelineModel()->snapStep()) - moveItemsRight(itemIds, dt - m_view->timelineModel()->snapStep()); - return; - } - if (!m_view->timelineModel()->canMoveItem(item, item.startTime() + dt)) - { - if (dt > m_view->timelineModel()->snapStep()) - moveItemsRight(itemIds, dt - m_view->timelineModel()->snapStep()); - return; - } - } - } - catch (const QString &errMsg) - { - ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed: " << errMsg; - return; - } - catch (...) - { - ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; - return; - } - - try - { - for (int itemId : itemIds) - { - Item &item = m_view->timelineModel()->item(itemId); - m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt); - } - } - catch (const QString &errMsg) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << errMsg; } - catch (...) {} -} - - -int TimelineViewDragHandler::dragItemTrackId(const Item &item) const -{ - int dy = deltaY(); - - const QModelIndex index = m_view->indexAt(m_dragStartPos + QPoint(0, dy)); - if (index.data().toInt() == m_dragStartIndex.data().toInt()) - return item.parentTrackId(); - - try - { - - const QModelIndex originalIndex = m_view->timelineModel()->indexOfTrack(item.parentTrackId()); - - const QModelIndex newIndex = m_view->indexAt(m_view->visualRect(originalIndex).center() + QPoint(0, dy)); - return newIndex.data().toInt(); - } - catch (...) - { - return 0; - } -} - -void TimelineViewDragHandler::clear() -{ - m_dragStartIndex = {}; - m_dragStartTime = -1; - m_dragStartPos = {}; - m_dragPos = {}; - m_dragItemIds.clear(); - m_view->setCursor(QCursor(Qt::ArrowCursor)); - m_view->viewport()->update(); -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_drag_handler.h b/Lts/Common/LtsTimeline/timeline_view_drag_handler.h deleted file mode 100755 index 409c1d3c9..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_drag_handler.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "timeline_view.h" - -#include - -#include -#include -#include - - -class QDragEnterEvent; -class QDragMoveEvent; -class QDragLeaveEvent; -class QDropEvent; -class QMouseEvent; -class QKeyEvent; - - -namespace LtsTimeline -{ - -class Item; -class Track; -class TimelineView; -class ItemDelegate; - - -class LTSTIMELINE_EXPORT TimelineViewDragHandler -{ - friend class TimelineView; - friend class ItemDelegate; - -public: - explicit TimelineViewDragHandler(TimelineView *view); - virtual ~TimelineViewDragHandler(); - - virtual void dragEnterEvent(QDragEnterEvent *e); - virtual void dragMoveEvent(QDragMoveEvent *e); - virtual void dragLeaveEvent(QDragLeaveEvent *e); - virtual void dropEvent(QDropEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void keyPressEvent(QKeyEvent *e); - - virtual bool isActive() const; - - virtual int deltaX() const; - virtual int deltaY() const; - virtual qint64 deltaTime() const; - - - const QVector &dragItemIds() const { return m_dragItemIds; } - const QModelIndex &dragStartIndex() const { return m_dragStartIndex; } - qint64 dragStartTime() const { return m_dragStartTime; } - const QPoint &dragStartPos() const { return m_dragStartPos; } - const QPoint &dragPos() const { return m_dragPos; } - -protected: - virtual bool canDragItemsTo(const QPoint &point) const; - virtual bool canMoveItemsTo(const QPoint &point) const; - - virtual void moveItemsLeft(const QVector &itemIds, int dt); - virtual void moveItemsRight(const QVector &itemIds, int dt); - - int dragItemTrackId(const Item &item) const; - void clear(); - -protected: - TimelineView *m_view{nullptr}; - QVector m_dragItemIds; - QModelIndex m_dragStartIndex; - qint64 m_dragStartTime{-1}; - QPoint m_dragStartPos; - QPoint m_dragPos; - - int m_snapLinePos{-1}; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp b/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp deleted file mode 100755 index 9525b3f2f..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include "timeline_view_resize_handler.h" -#include "timeline_view.h" -#include "timeline_header_view.h" -#include "item.h" -#include "track.h" - -#include -#include -#include - - -namespace LtsTimeline -{ - -TimelineViewResizeHandler::TimelineViewResizeHandler(TimelineView *view) - : m_view(view) -{} - -TimelineViewResizeHandler::~TimelineViewResizeHandler() -{ - m_view = nullptr; -} - - - - -void TimelineViewResizeHandler::mouseMoveEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - if (m_currentTrackId == -1 || m_currentItemId == -1 || m_resizePos == ResizePosition::eNone) - return; - if (!m_view->timelineModel()->item(m_currentItemId).isEditable()) - return; - - - const QModelIndex index(m_view->indexAt(e->pos())); - if (index.column() == Section::eActuator || !index.isValid()) - return; - - Track &track = m_view->timelineModel()->track(index.data().toInt()); - if (track.id() != m_currentTrackId) - return; - - qint64 time = m_view->timelineModel()->isSnapEnabled() ? globalSnapTime(e->pos().x()) : localSnapTime(e->pos().x()); - try - { - Item &item = m_view->timelineModel()->item(m_currentItemId); - if (m_resizePos == ResizePosition::eStart) - { - if (item.startTime() == time) - return; - - if (!m_view->timelineModel()->canResizeItem(item, time, item.startTime() + item.duration() - time)) - return; - - m_view->timelineModel()->setItemDuration(track, item, item.startTime() + item.duration() - time); - m_view->timelineModel()->setItemStartTime(track, item, time); - } - else - { - if (item.duration() == time - item.startTime()) - return; - - if (!m_view->timelineModel()->canResizeItem(item, item.startTime(), time - item.startTime())) - return; - - m_view->timelineModel()->setItemDuration(track, item, time - item.startTime()); - } - } - catch (...) - { - mouseReleaseEvent(e); - } -} - -void TimelineViewResizeHandler::mousePressEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - - if (e->button() != Qt::LeftButton) - return; - - - const QModelIndex index(m_view->indexAt(e->pos())); - if (!index.isValid() || index.column() == Section::eActuator) - return; - - try - { - const Track &track = m_view->timelineModel()->track(index.data().toInt()); - const Item &item = track.itemAt(m_view->toTime(e->pos().x())); - if (!item.isEditable()) - return; - - const int startPos = m_view->toPixel(item.startTime()); - const int endPos = m_view->toPixel(item.startTime() + item.duration()); - if (endPos - startPos < 15 || (startPos + 5 < e->pos().x() && e->pos().x() < endPos - 5)) - { - m_resizePos = ResizePosition::eNone; - return; - } - else if (startPos + 5 >= e->pos().x()) - m_resizePos = ResizePosition::eStart; - else - m_resizePos = ResizePosition::eEnd; - - m_currentTrackId = track.id(); - m_currentItemId = item.id(); - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} -} - -void TimelineViewResizeHandler::mouseReleaseEvent(QMouseEvent *e) -{ - if (!m_view->timelineModel()->isEditable()) - return; - - if (e->button() != Qt::LeftButton) - return; - - emit m_view->currentItemTimeChanged(); - m_view->setCursor(QCursor(Qt::ArrowCursor)); - m_currentTrackId = -1; - m_currentItemId = -1; - m_resizePos = ResizePosition::eNone; -} - - - - -qint64 TimelineViewResizeHandler::localSnapTime(int pos) const -{ - qint64 time = m_view->toTime(pos); - if (m_currentItemId == -1 || m_resizePos == ResizePosition::eNone) - return time; - - try - { - Track &track = m_view->timelineModel()->track(m_currentTrackId); - const Item ¤tItem = m_view->timelineModel()->item(m_currentItemId); - if (m_resizePos == ResizePosition::eStart) - { - if (time >= currentItem.startTime() + currentItem.duration()) - time = currentItem.startTime() + currentItem.duration() - m_view->timelineModel()->minimumDuration(); - else - { - try - { - const Item &previous = track.previous(m_currentItemId); - const qint64 endTime = previous.startTime() + previous.duration(); - if (time <= endTime) - time = endTime; - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - } - } - else - { - if (time <= currentItem.startTime()) - time = currentItem.startTime() + m_view->timelineModel()->minimumDuration(); - else - { - try - { - Item &next = track.next(m_currentItemId); - if (time >= next.startTime()) - time = next.startTime(); - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - } - } - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - return time; -} - -qint64 TimelineViewResizeHandler::globalSnapTime(int pos) const -{ - qint64 tPos = (m_view->toTime(pos) / m_view->timelineModel()->snapStep()) * m_view->timelineModel()->snapStep(); - if (m_currentItemId == -1 || m_resizePos == ResizePosition::eNone) - return tPos; - - qint64 tMin = m_view->m_startTime; - qint64 tMax = m_view->endTime(); - try - { - const Item &previous = m_view->timelineModel()->track(m_currentTrackId).previous(m_currentItemId); - tMin = previous.startTime() + previous.duration(); - } - catch (const QString &str) - { - DEBUG_LOG(m_view->m_logger) << str; - } - catch (...) {} - try - { - const Item &next = m_view->timelineModel()->track(m_currentTrackId).next(m_currentItemId); - tMax = next.startTime(); - } - catch (const QString &str) - { - DEBUG_LOG(m_view->m_logger) << str; - } - catch (...) {} - - qint64 time = tPos; - try - { - const Item ¤tItem = m_view->timelineModel()->item(m_currentItemId); - - int minDist = 2 * m_view->timelineModel()->snapTolerance(); - int eps = m_view->timelineModel()->snapTolerance(); - for (int itemId : m_view->timelineModel()->itemIds()) - { - if (itemId == m_currentItemId) - continue; - - try - { - const Item &item = m_view->timelineModel()->item(itemId); - qint64 t; - if (time - eps < item.startTime() && item.startTime() < time + eps) - t = item.startTime(); - else if (time - eps < item.startTime() + item.duration() && item.startTime() + item.duration() < time + eps) - t = item.startTime() + item.duration(); - else - continue; - - if (std::abs(t - tPos) < minDist) - { - if ((m_resizePos == ResizePosition::eStart && t >= currentItem.startTime() + currentItem.duration()) || - (m_resizePos == ResizePosition::eEnd && t <= currentItem.startTime())) - continue; - time = t; - minDist = time - tPos; - } - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - } - - if (m_resizePos == ResizePosition::eStart && time >= currentItem.startTime() + currentItem.duration()) - return currentItem.startTime() + currentItem.duration() - m_view->timelineModel()->minimumDuration(); - else if (m_resizePos == ResizePosition::eEnd && time <= currentItem.startTime()) - return currentItem.startTime() + m_view->timelineModel()->minimumDuration(); - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - - time = std::max(tMin, time); - time = std::min(tMax, time); - return time; -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_resize_handler.h b/Lts/Common/LtsTimeline/timeline_view_resize_handler.h deleted file mode 100755 index 7fcaff9b5..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_resize_handler.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include - - -namespace LtsTimeline -{ - -class Track; -class TimelineView; - - -class LTSTIMELINE_EXPORT TimelineViewResizeHandler -{ - -public: - enum class ResizePosition { eNone, eStart, eEnd }; - - explicit TimelineViewResizeHandler(TimelineView *view); - virtual ~TimelineViewResizeHandler(); - - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - - virtual bool isActive() const { return m_currentTrackId != -1 && m_currentItemId != -1 && m_resizePos != ResizePosition::eNone; } - -private: - virtual qint64 localSnapTime(int pos) const; - virtual qint64 globalSnapTime(int pos) const; - -protected: - TimelineView *m_view{nullptr}; - int m_currentTrackId{-1}; - int m_currentItemId{-1}; - ResizePosition m_resizePos{ResizePosition::eNone}; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp b/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp deleted file mode 100755 index 9b14287d7..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "timeline_view_selection_handler.h" -#include "item_delegate.h" - -#include -#include -#include -#include -#include - -#include - - -namespace LtsTimeline -{ - -TimelineViewSelectHandler::TimelineViewSelectHandler(TimelineView *view) - : m_view(view) -{ -} - -TimelineViewSelectHandler::~TimelineViewSelectHandler() -{ - m_view = nullptr; -} - - - - -void TimelineViewSelectHandler::mouseDoubleClickEvent(QMouseEvent *e) -{ - if (e->button() != Qt::LeftButton) - return; - - m_view->clearSelectedItems(); - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() == Section::eActuator) - return; - - try - { - Track &track = m_view->timelineModel()->track(index.data().toInt()); - Item &item = track.itemAt(m_view->toTime(e->pos().x())); - emit m_view->itemActivated(item.id()); - } - catch (const QString &str) { DEBUG_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; } -} - -void TimelineViewSelectHandler::mouseMoveEvent(QMouseEvent *e) -{ - if (m_isRubberBandActive) - { - m_view->setCursor(QCursor(Qt::CrossCursor)); - if (!m_view->isCursorWithinRangeX(e->pos().x())) - m_rubberBand.setHeight(e->pos().y() - m_rubberBand.top()); - - - else - m_rubberBand.setRect( - m_rubberBand.left(), m_rubberBand.top(), e->pos().x() - m_rubberBand.left(), e->pos().y() - m_rubberBand.top() - ); - } - else - m_selectedItemId = -1; -} - -void TimelineViewSelectHandler::mousePressEvent(QMouseEvent *e) -{ - if (e->button() != Qt::LeftButton && e->button() != Qt::RightButton) - return; - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (index.isValid()) - { - if (index.column() == Section::eActuator) - { - qWarning() << "clicked on row with actuator" << index.data().toString(); - return; - } - - try - { - Track &track = m_view->timelineModel()->track(index.data().toInt()); - Item &item = track.itemAt(m_view->toTime(e->pos().x())); - if (e->button() == Qt::LeftButton) - m_selectedItemId = item.id(); - return; - } - catch (...) - { - if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - return; - - if (e->button() == Qt::RightButton) - { - emit m_view->customContextMenuRequested(e->pos()); - return; - } - - m_view->clearSelectedItems(); - m_rubberBand.setLeft(e->pos().x()); - m_rubberBand.setTop(e->pos().y()); - m_rubberBand.setSize(QSize(0, 0)); - m_isRubberBandActive = true; - return; - } - - return; - } - else - { - qWarning() << "TimelineViewSelectHandler::mousePressEvent: index invalid"; - } - - if (e->button() == Qt::RightButton) - return; - - if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - return; - - m_view->clearSelectedItems(); - - if (!m_view->isCursorWithinRangeX(e->pos().x())) - return; - - m_rubberBand.setLeft(e->pos().x()); - m_rubberBand.setTop(e->pos().y()); - m_rubberBand.setSize(QSize(0, 0)); - m_isRubberBandActive = true; -} - -void TimelineViewSelectHandler::mouseReleaseEvent(QMouseEvent *e) -{ - if (e->button() == Qt::RightButton) - { - - return; - } - - if (e->button() != Qt::LeftButton) - return; - - if (m_isRubberBandActive) - { - m_view->selectItems(getItemsWithinRubberBand()); - m_isRubberBandActive = false; - m_rubberBand.setSize(QSize(0, 0)); - m_view->setCursor(QCursor(Qt::ArrowCursor)); - return; - } - else - { - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() == Section::eActuator) - { - m_selectedItemId = -1; - return; - } - - try - { - Track &track = m_view->timelineModel()->track(index.data().toInt()); - Item &item = track.itemAt(m_view->toTime(e->pos().x())); - if (item.id() == m_selectedItemId) - { - if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - { - if (m_view->selectedItemIds().contains(m_selectedItemId)) - m_view->unselectItem(m_selectedItemId); - else - m_view->selectItem(m_selectedItemId, false); - } - else - { - if (m_view->m_selectedItemIds.size() > 1 || !m_view->m_selectedItemIds.contains(m_selectedItemId)) - m_view->selectItem(m_selectedItemId); - else - m_view->unselectItem(m_selectedItemId); - } - } - } catch (...) {} - m_selectedItemId = -1; - } -} - -void TimelineViewSelectHandler::keyPressEvent(QKeyEvent *e) -{ - if (e->key() == Qt::Key_Escape) - { - m_selectedItemId = -1; - m_view->clearSelectedItems(); - } - else if (e->key() == Qt::Key_Delete) - { - if (!m_view->m_selectedItemIds.isEmpty()) - { - for (const int &itemId : qAsConst(m_view->m_selectedItemIds)) - { - try - { - m_view->timelineModel()->removeItem(itemId); - } - catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; } - } - m_view->clearSelectedItems(); - m_selectedItemId = -1; - } - } - else if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - { - try - { - switch (e->key()) - { - case Qt::Key_A: - m_view->selectAllItems(); - m_selectedItemId = -1; - break; - - case Qt::Key_C: - if (m_view->hasSelectedItems()) - m_view->timelineModel()->copyToClipboard(m_view->selectedItemIds()); - break; - - case Qt::Key_X: - if (m_view->hasSelectedItems()) - { - m_view->timelineModel()->cutToClipboard(m_view->selectedItemIds()); - m_view->clearSelectedItems(); - } - break; - - case Qt::Key_V: - if (!m_view->timelineModel()->m_clipboard.empty()) - { - qint64 time = m_view->currentTime(); - if (m_view->m_header->isIndicatorVisible() && m_view->m_header->indicatorTime() != -1) - time = m_view->m_header->indicatorTime(); - m_view->timelineModel()->pasteFromClipboard(time); - } - break; - - default: - break; - } - } - catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; } - catch (...) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; } - } -} - - -bool TimelineViewSelectHandler::isActive() const -{ - if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - return true; - if (m_isRubberBandActive) - return true; - return m_selectedItemId != -1; -} - - - - -QVector TimelineViewSelectHandler::getItemsWithinRubberBand() const -{ - const QPoint topLeft = { std::min(m_rubberBand.left(), m_rubberBand.right()), std::min(m_rubberBand.top(), m_rubberBand.bottom()) }; - const QPoint bottomRight = { std::max(m_rubberBand.left(), m_rubberBand.right()), std::max(m_rubberBand.top(), m_rubberBand.bottom()) }; - const qint64 start = m_view->toTime(topLeft.x()); - const qint64 end = m_view->toTime(bottomRight.x()); - if (start == end) - return {}; - - QVector itemsWithinRubberBand; - const int h = m_view->itemDelegate()->sizeHint({}, {}).height(); - for (int y = topLeft.y(); y <= bottomRight.y(); y += h) - { - const QPoint point = { topLeft.x(), y }; - - const QModelIndex index = m_view->indexAt(point); - if (!index.isValid()) - continue; - - try - { - const Track &track = m_view->timelineModel()->track(index.data().toInt()); - itemsWithinRubberBand += track.itemIdsBetween(start, end); - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - } - if (topLeft.y() + h > bottomRight.y()) - { - - const QModelIndex index = m_view->indexAt(bottomRight); - if (!index.isValid()) - return itemsWithinRubberBand; - - try - { - const Track &track = m_view->timelineModel()->track(index.data().toInt()); - itemsWithinRubberBand += track.itemIdsBetween(start, end); - } - catch (const QString &str) - { - ERROR_LOG(m_view->m_logger) << str; - } - catch (...) {} - } - return itemsWithinRubberBand; -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_selection_handler.h b/Lts/Common/LtsTimeline/timeline_view_selection_handler.h deleted file mode 100755 index 354a2cc55..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_selection_handler.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "timeline_view.h" - -#include - -#include - - -class QMouseEvent; -class QKeyEvent; - - -namespace LtsTimeline -{ - -class TimelineView; - - -class LTSTIMELINE_EXPORT TimelineViewSelectHandler -{ - friend class TimelineView; - -public: - explicit TimelineViewSelectHandler(TimelineView *view); - virtual ~TimelineViewSelectHandler(); - - virtual void mouseDoubleClickEvent(QMouseEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void keyPressEvent(QKeyEvent *e); - - virtual bool isActive() const; - virtual bool isRubberBandActive() const { return m_isRubberBandActive; } - -protected: - virtual QVector getItemsWithinRubberBand() const; - -protected: - TimelineView *m_view{nullptr}; - int m_selectedItemId{-1}; - QRect m_rubberBand; - bool m_isRubberBandActive{false}; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp b/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp deleted file mode 100755 index 4c2098e47..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "timeline_view_resize_handler.h" -#include "timeline_view.h" -#include "timeline_header_view.h" -#include "item.h" -#include "track.h" - -#include - -#include -#include -#include - - -namespace LtsTimeline -{ - -TimelineViewTrackHandler::TimelineViewTrackHandler(TimelineView *view) - : m_view(view) -{ -} - -TimelineViewTrackHandler::~TimelineViewTrackHandler() -{ - m_view = nullptr; -} - - - - -void TimelineViewTrackHandler::mouseDoubleClickEvent(QMouseEvent *e) -{ - if (e->button() != Qt::LeftButton) - return; - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() != Section::eActuator) - return; - - emit m_view->trackActivated(index.data().toInt()); -} - -void TimelineViewTrackHandler::mousePressEvent(QMouseEvent *e) -{ - if (e->button() != Qt::LeftButton) - { - if (e->button() == Qt::RightButton) - emit m_view->customContextMenuRequested(e->pos()); - return; - } - - - const QModelIndex index = m_view->indexAt(e->pos()); - if (!index.isValid() || index.column() != Section::eActuator) - return; - - try - { - if(!m_view->timelineModel()->isEditable()) - return; - - const Track &track = m_view->timelineModel()->track(index.data(Qt::UserRole).toInt()); - QRect visRect = m_view->visualRect(index); - int buttonLength = visRect.height() / 2; - if (track.hasInsertButton()) - { - QRect rect = QRect(visRect.right() - buttonLength, visRect.top(), buttonLength, buttonLength); - if (rect.contains(e->pos())) - { - m_pressedInsertButtonIndex = index; - - m_view->timelineModel()->onInsertTrack(index); - return; - } - } - if (track.hasRemoveButton()) - { - QRect rect = QRect(visRect.right() - buttonLength, visRect.center().y(), buttonLength, buttonLength); - if (rect.contains(e->pos())) - { - m_pressedRemoveButtonIndex = index; - - auto msg = new LtsWidgets::LtsMessageDialogNew(m_view, {}, QApplication::translate("LtsTimeline", "Do you really want to remove this filter track and all of its contents?"), - LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel); - - QObject::connect(msg, &LtsWidgets::LtsMessageDialogNew::yes, - m_view, [this, index]() - { - - m_view->timelineModel()->onRemoveTrack(index); - }); - - return; - } - } - } - catch (...) {} -} - -void TimelineViewTrackHandler::mouseReleaseEvent(QMouseEvent *e) -{ - Q_UNUSED(e); - m_pressedInsertButtonIndex = {}; - m_pressedRemoveButtonIndex = {}; -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_view_track_handler.h b/Lts/Common/LtsTimeline/timeline_view_track_handler.h deleted file mode 100755 index 16b108291..000000000 --- a/Lts/Common/LtsTimeline/timeline_view_track_handler.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - - -namespace LtsTimeline -{ - -class Track; -class TimelineView; - - -class LTSTIMELINE_EXPORT TimelineViewTrackHandler -{ - -public: - explicit TimelineViewTrackHandler(TimelineView *view); - virtual ~TimelineViewTrackHandler(); - - virtual void mouseDoubleClickEvent(QMouseEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - - bool isInsertButtonPressed(const QModelIndex &index) const { return index == m_pressedInsertButtonIndex; } - bool isRemoveButtonPressed(const QModelIndex &index) const { return index == m_pressedRemoveButtonIndex; } - -protected: - TimelineView *m_view{nullptr}; - - QModelIndex m_pressedInsertButtonIndex; - QModelIndex m_pressedRemoveButtonIndex; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_widget.cpp b/Lts/Common/LtsTimeline/timeline_widget.cpp deleted file mode 100755 index 6e153b6c6..000000000 --- a/Lts/Common/LtsTimeline/timeline_widget.cpp +++ /dev/null @@ -1,402 +0,0 @@ - - - -#include "timeline_widget.h" -#include "ui_timeline_widget.h" -#include "timeline_view.h" -#include "timeline_header_view.h" -#include "timeline_model.h" -#include "item_delegate.h" - -#include - -#include -#include -#include -#include -#include -#include - - -namespace LtsTimeline -{ - -TimelineWidget::TimelineWidget(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::Timeline) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.Widget")) -{ - m_ui->setupUi(this); - m_ui->debugWidget->hide(); - m_ui->wPlaceholder->hide(); - m_ui->pbZoomIn->setProperty("const", true); - m_ui->pbZoomOut->setProperty("const", true); - m_ui->cbZoom->setProperty("const", true); - - connect(m_ui->cbZoom, qOverload(&QComboBox::currentIndexChanged), - this, &TimelineWidget::onZoomSelected); - m_ui->pbZoomIn->setIcon(QIcon(QStringLiteral("icon:bgv_zoom_in.svg"))); - connect(m_ui->pbZoomIn, &QPushButton::clicked, - this, &TimelineWidget::onZoomIn); - m_ui->pbZoomOut->setIcon(QIcon(QStringLiteral("icon:bgv_zoom_out.svg"))); - connect(m_ui->pbZoomOut, &QPushButton::clicked, - this, &TimelineWidget::onZoomOut); - - connect(m_ui->view, &TimelineView::rangeChanged, this, [this](qint64 min, qint64 max) - { - m_ui->slider->setRange(min / 1000, (max - m_ui->view->zoomFactor()) / 1000); - onUpdateZoomButtons(); - }); - connect(m_ui->view, &TimelineView::currentTimeChanged, this, [this](qint64 time) - { - m_ui->slider->setValue(time / 1000); - }); - - connect(m_ui->view, &TimelineView::zoomChanged, - this, &TimelineWidget::onZoomChanged); - - connect(m_ui->view->header(), &TimelineHeaderView::sectionResized, - this, &TimelineWidget::onSectionResized); - - connect(m_ui->slider, &QScrollBar::valueChanged, - this, &TimelineWidget::onSliderValueChanged); - - onZoomChanged(m_ui->view->zoomFactor()); -} - -TimelineWidget::~TimelineWidget() -{ - delete m_ui; -} - - - - -TimelineView *TimelineWidget::view() -{ - return m_ui->view; -} - - -TimelineHeaderView *TimelineWidget::header() -{ - return m_ui->view->header(); -} - - -TimelineModel *TimelineWidget::timelineModel() -{ - return m_ui->view->timelineModel(); -} - - -void TimelineWidget::setModel(QAbstractItemModel *model) -{ - m_ui->view->setModel(model); -} - - -QScrollBar *TimelineWidget::slider() -{ - return m_ui->slider; -} - - -ZoomFactor TimelineWidget::zoomFactor() const -{ - return m_ui->view->zoomFactor(); -} - -void TimelineWidget::setZoomFactor(ZoomFactor factor) -{ - m_ui->view->setZoomFactor(factor); -} - - -ZoomFactor TimelineWidget::minimumZoomFactor() const -{ - return m_ui->view->minimumZoomFactor(); -} - -void TimelineWidget::setMinimumZoomFactor(ZoomFactor factor) -{ - m_ui->view->setMinimumZoomFactor(factor); - populateZoomFactorComboBox(); -} - - -ZoomFactor TimelineWidget::maximumZoomFactor() const -{ - return m_ui->view->maximumZoomFactor(); -} - -void TimelineWidget::setMaximumZoomFactor(ZoomFactor factor) -{ - m_ui->view->setMaximumZoomFactor(factor); - populateZoomFactorComboBox(); -} - - -qint64 TimelineWidget::startTime() const -{ - return m_ui->view->startTime(); -} - -void TimelineWidget::setStartTime(qint64 time) -{ - m_ui->view->setStartTime(time); -} - - -qint64 TimelineWidget::currentTime() const -{ - return m_ui->view->currentTime(); -} - -void TimelineWidget::setCurrentTime(qint64 time) -{ - m_ui->view->setCurrentTime(time); -} - - -qint64 TimelineWidget::endTime() const -{ - return m_ui->view->endTime(); -} - -void TimelineWidget::setEndTime(qint64 time) -{ - m_ui->view->setEndTime(time); -} - - -int TimelineWidget::duration() const -{ - return m_ui->view->duration(); -} - -void TimelineWidget::setDuration(int duration) -{ - m_ui->view->setDuration(duration); -} - - -bool TimelineWidget::isCursorTextVisible() const -{ - return m_ui->view->isCursorTextVisible(); -} - -void TimelineWidget::setCursorTextVisible(bool visible) -{ - m_ui->view->setCursorTextVisible(visible); -} - -void TimelineWidget::setCursorText(const QString &text) -{ - m_ui->view->setCursorText(text); -} - - -bool TimelineWidget::isSnapEnabled() const -{ - return m_ui->view->timelineModel()->isSnapEnabled(); -} - -void TimelineWidget::setSnapEnabled(bool enabled) -{ - m_ui->view->timelineModel()->setSnapEnabled(enabled); -} - -void TimelineWidget::enableSnap() -{ - m_ui->view->timelineModel()->setSnapEnabled(true); -} - -void TimelineWidget::disableSnap() -{ - m_ui->view->timelineModel()->setSnapEnabled(false); -} - -void TimelineWidget::triggerSnap() -{ - m_ui->view->timelineModel()->setSnapEnabled(!m_ui->view->timelineModel()->isSnapEnabled()); - if (m_ui->debugWidget->isVisible()) - m_ui->pbDebugTriggerSnap->setText(m_ui->view->timelineModel()->isSnapEnabled() ? QStringLiteral("Disable Snap") : - QStringLiteral("Enable Snap")); -} - - -void TimelineWidget::setZoomInIcon(QIcon icon) -{ - m_ui->pbZoomIn->setIcon(icon); -} - -void TimelineWidget::setZoomOutIcon(QIcon icon) -{ - m_ui->pbZoomOut->setIcon(icon); -} - - -QWidget *TimelineWidget::placeholder() -{ - if (m_ui->wPlaceholder->layout()->count() == 0) - return nullptr; - return m_ui->wPlaceholder->layout()->itemAt(0)->widget(); -} - -void TimelineWidget::setPlaceholder(QWidget *widget) -{ - QWidget *ptr = placeholder(); - if (ptr) - { - if (ptr == widget) - return; - m_ui->wPlaceholder->layout()->removeWidget(ptr); - delete ptr; - } - m_ui->wPlaceholder->layout()->addWidget(widget); -} - - -void TimelineWidget::showDebugWidget() -{ - m_ui->debugWidget->show(); - - m_ui->sbDebugSnapStep->setValue(m_ui->view->timelineModel()->snapStep()); - m_ui->sbDebugSnapTol->setValue(m_ui->view->timelineModel()->snapTolerance()); - m_ui->pbDebugTriggerSnap->setText(m_ui->view->header()->isIndicatorVisible() ? QStringLiteral("Disable Snap") : - QStringLiteral("Enable Snap")); - m_ui->pbDebugTriggerIndicator->setText(m_ui->view->header()->isIndicatorVisible() ? QStringLiteral("Disable Indicator") : - QStringLiteral("Enable Indicator")); - m_ui->pbDebugTriggerHover->setText(m_ui->view->header()->isIndicatorVisible() ? QStringLiteral("Disable Hover") : - QStringLiteral("Enable Hover")); - - connect(m_ui->view, &TimelineView::currentTimeChanged, this, &TimelineWidget::onUpdateDebugInfo); - connect(m_ui->view, &TimelineView::rangeChanged, this, &TimelineWidget::onUpdateDebugInfo); - connect(m_ui->view, &TimelineView::selectedItemsChanged, this, &TimelineWidget::onUpdateDebugInfo); - connect(m_ui->view, &TimelineView::zoomChanged, this, &TimelineWidget::onUpdateDebugInfo); - connect(m_ui->pbDebugTriggerSnap, &QPushButton::clicked, this, &TimelineWidget::triggerSnap); - connect(m_ui->sbDebugSnapStep, qOverload(&QSpinBox::valueChanged), m_ui->view->timelineModel(), &TimelineModel::setSnapStep); - connect(m_ui->sbDebugSnapTol, qOverload(&QSpinBox::valueChanged), m_ui->view->timelineModel(), &TimelineModel::setSnapTolerance); - connect(m_ui->pbDebugTriggerIndicator, &QPushButton::clicked, this, [this]() - { - m_ui->view->header()->setIndicatorVisible(!m_ui->view->header()->isIndicatorVisible()); - m_ui->pbDebugTriggerIndicator->setText(m_ui->view->header()->isIndicatorVisible() ? QStringLiteral("Disable Indicator") : - QStringLiteral("Enable Indicator")); - }); - connect(m_ui->pbDebugTriggerHover, &QPushButton::clicked, this, [this]() - { - m_ui->view->header()->setHoverVisible(!m_ui->view->header()->isHoverVisible()); - m_ui->pbDebugTriggerHover->setText(m_ui->view->header()->isHoverVisible() ? QStringLiteral("Disable Hover") : - QStringLiteral("Enable Hover")); - }); - - onUpdateDebugInfo(); -} - -void TimelineWidget::hideDebugWidget() -{ - m_ui->debugWidget->hide(); - disconnect(m_ui->view, &TimelineView::currentTimeChanged, this, &TimelineWidget::onUpdateDebugInfo); - disconnect(m_ui->view, &TimelineView::rangeChanged, this, &TimelineWidget::onUpdateDebugInfo); - disconnect(m_ui->view, &TimelineView::selectedItemsChanged, this, &TimelineWidget::onUpdateDebugInfo); - disconnect(m_ui->view, &TimelineView::zoomChanged, this, &TimelineWidget::onUpdateDebugInfo); - disconnect(m_ui->pbDebugTriggerSnap, &QPushButton::clicked, this, &TimelineWidget::triggerSnap); - disconnect(m_ui->sbDebugSnapStep, qOverload(&QSpinBox::valueChanged), m_ui->view->timelineModel(), &TimelineModel::setSnapStep); - disconnect(m_ui->sbDebugSnapTol, qOverload(&QSpinBox::valueChanged), m_ui->view->timelineModel(), &TimelineModel::setSnapTolerance); - disconnect(m_ui->pbDebugTriggerIndicator); - disconnect(m_ui->pbDebugTriggerHover); -} - - - - -void TimelineWidget::populateZoomFactorComboBox() -{ - disconnect(m_ui->cbZoom, qOverload(&QComboBox::currentIndexChanged), - this, &TimelineWidget::onZoomSelected); - m_ui->cbZoom->clear(); - QVector availableZooms = Zoom::availableZoomFactors(m_ui->view->minimumZoomFactor(), m_ui->view->upperZoomFactor()); - for (ZoomFactor factor : qAsConst(availableZooms)) - { - m_ui->cbZoom->addItem(Zoom::toString(factor), QVariant(factor)); - if (factor == zoomFactor()) - m_ui->cbZoom->setCurrentIndex(m_ui->cbZoom->findData(static_cast(zoomFactor()))); - } - connect(m_ui->cbZoom, qOverload(&QComboBox::currentIndexChanged), - this, &TimelineWidget::onZoomSelected); -} - - - - -void TimelineWidget::onSectionResized(int logicalIndex, int oldSize, int newSize) -{ - Q_UNUSED(oldSize); - if (logicalIndex == Section::eItems) - { - int width = m_ui->view->verticalScrollBar()->width(); - m_ui->slider->setMinimumHeight(width); - m_ui->slider->setMaximumHeight(width); - m_ui->slider->setMaximumWidth(newSize); - m_ui->slider->setMinimumWidth(newSize); - } -} - -void TimelineWidget::onSliderValueChanged(int value) -{ - setCurrentTime(qint64(value) * 1000); -} - -void TimelineWidget::onUpdateZoomButtons() -{ - m_ui->pbZoomIn->setEnabled(zoomFactor() > minimumZoomFactor()); - m_ui->pbZoomOut->setEnabled(zoomFactor() < m_ui->view->upperZoomFactor()); - populateZoomFactorComboBox(); -} - -void TimelineWidget::onZoomChanged(ZoomFactor factor) -{ - m_ui->slider->setMaximum((m_ui->view->endTime() - m_ui->view->zoomFactor()) / 1000); - m_ui->slider->setPageStep(factor / 1000); - m_ui->slider->setSingleStep(Zoom::minorInterval(factor)); - onUpdateZoomButtons(); - if (static_cast(m_ui->cbZoom->currentData().toInt()) != factor) - m_ui->cbZoom->setCurrentIndex(m_ui->cbZoom->findData(QVariant(factor))); -} - -void TimelineWidget::onZoomSelected() -{ - ZoomFactor factor = static_cast(m_ui->cbZoom->currentData().toInt()); - if (factor != zoomFactor()) - setZoomFactor(factor); -} - -void TimelineWidget::onZoomIn() -{ - setZoomFactor(Zoom::decrement(zoomFactor())); -} - -void TimelineWidget::onZoomOut() -{ - setZoomFactor(Zoom::increment(zoomFactor())); -} - - -void TimelineWidget::onUpdateDebugInfo() -{ - m_ui->lblDebugStart->setText(DateTime::toString(startTime(), DateTimeFormat::DTG)); - m_ui->lblDebugEnd->setText(DateTime::toString(endTime(), DateTimeFormat::DTG)); - m_ui->lblDebugDuration->setText(QString::number(duration() / 1000) + QStringLiteral("s")); - m_ui->lblDebugCurrent->setText(DateTime::toString(currentTime(), DateTimeFormat::DTG)); - m_ui->lblDebugSlider->setText(DateTime::toString(qint64(m_ui->slider->value()) * 1000, DateTimeFormat::DTG)); - m_ui->lblDebugZoom->setText(Zoom::toString(m_ui->view->zoomFactor())); - QString selection; - for (int itemId : qAsConst(m_ui->view->selectedItemIds())) - selection += QString::number(itemId) + QStringLiteral(", "); - if (selection.size() > 1) - selection.chop(2); - m_ui->lblDebugSelection->setText(selection); -} - -} diff --git a/Lts/Common/LtsTimeline/timeline_widget.h b/Lts/Common/LtsTimeline/timeline_widget.h deleted file mode 100755 index baa4642cc..000000000 --- a/Lts/Common/LtsTimeline/timeline_widget.h +++ /dev/null @@ -1,114 +0,0 @@ - - - -#pragma once - -#include "timeline_utils.h" - -#include -#include - -#include - - -class QScrollBar; -class QAbstractItemModel; - - -namespace Ui -{ -class Timeline; -} - - -namespace LtsTimeline -{ - -class TimelineView; -class TimelineHeaderView; -class TimelineModel; - - -class LTSTIMELINE_EXPORT TimelineWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TimelineWidget(QWidget *parent = nullptr); - ~TimelineWidget(); - - TimelineView *view(); - - TimelineHeaderView *header(); - - TimelineModel *timelineModel(); - void setModel(QAbstractItemModel *model); - - QScrollBar *slider(); - - ZoomFactor zoomFactor() const; - void setZoomFactor(ZoomFactor factor); - - ZoomFactor minimumZoomFactor() const; - void setMinimumZoomFactor(ZoomFactor factor); - - ZoomFactor maximumZoomFactor() const; - void setMaximumZoomFactor(ZoomFactor factor); - - qint64 startTime() const; - void setStartTime(qint64 time); - - qint64 currentTime() const; - void setCurrentTime(qint64 time); - - qint64 endTime() const; - void setEndTime(qint64 time); - - int duration() const; - void setDuration(int duration); - - bool isCursorTextVisible() const; - void setCursorTextVisible(bool visible); - void setCursorText(const QString &text); - void setCursorText(const QStringList &texts); - - bool isSnapEnabled() const; - void setSnapEnabled(bool enabled); - void enableSnap(); - void disableSnap(); - void triggerSnap(); - - void setZoomInIcon(QIcon icon); - void setZoomOutIcon(QIcon icon); - - QWidget *placeholder(); - void setPlaceholder(QWidget *widget); - - void showDebugWidget(); - void hideDebugWidget(); - -private: - void populateZoomFactorComboBox(); - -public: - void onUpdateZoomButtons(); - -private slots: - void onSectionResized(int logicalIndex, int oldSize, int newSize); - void onSliderValueChanged(int value); - void onZoomChanged(ZoomFactor factor); - void onZoomSelected(); - void onZoomIn(); - void onZoomOut(); - - void onUpdateDebugInfo(); - -signals: - -private: - Ui::Timeline *m_ui{nullptr}; - NGL::INGLLogger &m_logger; - -}; - -} diff --git a/Lts/Common/LtsTimeline/timeline_widget.ui b/Lts/Common/LtsTimeline/timeline_widget.ui deleted file mode 100755 index acc52166e..000000000 --- a/Lts/Common/LtsTimeline/timeline_widget.ui +++ /dev/null @@ -1,434 +0,0 @@ - - - Timeline - - - - 0 - 0 - 1298 - 409 - - - - true - - - Timeline - - - - - - - - - - - - - - start: - - - - - - - [START] - - - - - - - Qt::Vertical - - - - - - - end: - - - - - - - [END] - - - - - - - Qt::Vertical - - - - - - - current: - - - - - - - - - - [CURRENT] - - - - - - - Qt::Vertical - - - - - - - duration: - - - - - - - [DURATION] - - - - - - - Qt::Vertical - - - - - - - slider: - - - - - - - [SLIDER] - - - - - - - Qt::Vertical - - - - - - - zoom: - - - - - - - [ZOOM] - - - - - - - Qt::Vertical - - - - - - - selection: - - - - - - - - - - [SELECTION] - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Update - - - - - - - - - - Enable Snap - - - - - - - Snap Step: - - - - - - - 60000 - - - 100 - - - - - - - Snap Tol: - - - - - - - 60000 - - - 100 - - - - - - - - - - Disable Indicator - - - - - - - - - - Disable Hover - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - 0 - 20 - - - - - 16777215 - 16777215 - - - - ArrowCursor - - - true - - - Qt::NoFocus - - - Qt::LeftToRight - - - 0 - - - 0 - - - true - - - Qt::Horizontal - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 0 - - - - - 25 - 16777215 - - - - - - - - - - - - 0 - 0 - - - - false - - - QAbstractItemView::NoSelection - - - - - - - - 0 - 0 - - - - - 30 - 0 - - - - - 25 - 16777215 - - - - - - - - - - - true - - - - 0 - 0 - - - - - 20 - 0 - - - - - 125 - 16777215 - - - - - 0 - 0 - - - - - - - - - LtsTimeline::TimelineView - QTreeView -

timeline_view.h
- - - - - diff --git a/Lts/Common/LtsTimeline/track.cpp b/Lts/Common/LtsTimeline/track.cpp deleted file mode 100755 index 76bad68e6..000000000 --- a/Lts/Common/LtsTimeline/track.cpp +++ /dev/null @@ -1,853 +0,0 @@ - - - -#include "track.h" - -namespace LtsTimeline -{ - -const Track &Track::track(int id) const noexcept(false) -{ - - auto const it = std::find_if(m_childrenTracks.cbegin(), m_childrenTracks.cend(), - [id](const TrackPtr &trackPtr) - { - return (trackPtr.get()->id() == id); - }); - - if (it != m_childrenTracks.cend()) - return **it; - - - for (const TrackPtr &track : qAsConst(m_childrenTracks)) - { - try - { - return track->track(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("lookup failed!");; -} - - -bool Track::hasTrack(int id, bool recursive) const -{ - if (std::find_if(m_childrenTracks.cbegin(), m_childrenTracks.cend(), - [id](const TrackPtr &trackPtr) - { - return (trackPtr.get()->id() == id); - }) != m_childrenTracks.cend()) - return true; - - if (!recursive) - return false; - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - { - if (trackPtr->hasTrack(id, true)) - return true; - } - - return false; -} - - - - - - - - - - - - - - - - - - - - - - - - - -void Track::addCopyItemIds(const QSet &ids) -{ - m_copyItemIds += ids; -} - - -void Track::addCopyItemId(int id) -{ - m_copyItemIds += id; -} - - -const Track &Track::parentOf(int id) const noexcept(false) -{ - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - { - if (trackPtr->hasTrack(id)) - return *trackPtr; - - if (trackPtr->hasTrack(id, true)) - return trackPtr->parentOf(id); - } - - throw("not found"); -} - - -bool Track::isItemBetween(const Item &item, qint64 start, qint64 end, bool strict, bool complete, qint64 minimumDuration) -{ - if (end - start == -1) - { - if (item.duration() == -1) - { - if (!complete) - return true; - - return strict ? item.startTime() > start : item.startTime() >= start; - } - - if (strict) - { - return (complete && item.startTime() > start) || - (!complete && item.startTime() + item.duration() > start); - } - else - { - return (complete && item.startTime() >= start) || - (!complete && item.startTime() + item.duration() >= start); - } - } - - if (item.duration() == -1) - { - if (complete) - return false; - - return strict ? item.startTime() < end : item.startTime() <= end; - } - - Q_ASSERT(end+1 > start); - qint64 itemStart = item.startTime(), itemEnd = item.startTime() + (std::max((qint64)item.duration(), minimumDuration)); - - if (strict) - return ((complete && (itemStart > start && itemEnd < end)) || - (!complete && (itemEnd > start && itemStart < end))); - else - return ((complete && (itemStart >= start && itemEnd <= end)) || - (!complete && (itemEnd >= start && itemStart <= end))); -} - - -void Track::removeCopyItem(int itemId) noexcept(false) -{ - m_copyItemIds.remove(itemId); - - for (TrackPtr &t : m_childrenTracks) - { - t->removeCopyItem(itemId); - } -} - - -const QVector Track::copyItemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const -{ - auto itemPtrRefs = _copyItems(); - - - if (recursive) - { - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - { - auto itemPtrRefs2 = trackPtr->_copyItems(); - itemPtrRefs.insert(itemPtrRefs.end(), itemPtrRefs2.begin(), itemPtrRefs2.end()); - } - } - - itemPtrRefs.erase(std::remove_if(itemPtrRefs.begin(), itemPtrRefs.end(), - [this, start, end, strict, complete](const ItemPtr &itemPtr) - { - return !isItemBetween(*itemPtr, start, end, strict, complete); - }), itemPtrRefs.end()); - - if (sorted) - { - sort(itemPtrRefs); - } - - - - return transformItemPtrsToIds(itemPtrRefs); -} - - -const QVector Track::itemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const -{ - if (sorted) - { - auto itemPtrRefs = _items(recursive); - itemPtrRefs.erase(std::remove_if(itemPtrRefs.begin(), itemPtrRefs.end(), - [this, start, end, strict, complete, minimumDuration](const ItemPtr &itemPtr) - { - return !isItemBetween(*itemPtr, start, end, strict, complete, minimumDuration); - }), itemPtrRefs.end()); - - sort(itemPtrRefs); - - return transformItemPtrsToIds(itemPtrRefs); - } - - - - QVector itemIds; - itemIds.reserve(m_items.size()); - - for (const ItemPtr &itemPtr : qAsConst(m_items)) - { - const Item &item = *itemPtr; - if (isItemBetween(item, start, end, strict, complete, minimumDuration)) - itemIds += item.id(); - } - - if (!recursive) - { - itemIds.squeeze(); - return itemIds; - } - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - itemIds += trackPtr->itemIdsBetween(start, end, strict, complete, true, minimumDuration); - - return itemIds; -} - - -bool Track::isItemsBetween(qint64 start, qint64 end, const QVector &ignoreIds, bool strict, bool complete, bool recursive, qint64 minimumDuration) const -{ - for (const ItemPtr &itemPtr : qAsConst(m_items)) - { - const Item &i = *itemPtr; - if (!ignoreIds.contains(i.id())) - if (isItemBetween(i, start, end, strict, complete, minimumDuration)) - return true; - } - - - for (const int itemId : qAsConst(m_copyItemIds)) - { - const Item &i = item(itemId); - if (!ignoreIds.contains(i.id())) - if (isItemBetween(i, start, end, strict, complete, minimumDuration)) - return true; - } - - - if (!recursive) - return false; - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - if (trackPtr->isItemsBetween(start, end, ignoreIds, strict, complete, true, minimumDuration)) - return true; - - return false; -} - - -bool Track::isItemsBetween(qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive, qint64 minimumDuration) const -{ - return isItemsBetween(start, end, QVector({ ignoreId }), strict, complete, recursive, minimumDuration); -} - - -ItemPtrRefs Track::_items(bool recursive) const -{ - - ItemPtrRefs x(m_items.cbegin(), m_items.cend()); - - if (!m_copyItemIds.isEmpty()) - x.reserve(x.size() + m_copyItemIds.count()); - - - for (const int itemId : qAsConst(m_copyItemIds)) - { - try - { - x.push_back(std::cref(_item(itemId))); - } - catch (const QString &msg) - { - fprintf(stderr, "cannot add item ptr ref from copy item with copy item id %d: %s\n", itemId, msg.toStdString().c_str()); - } - } - - if (!recursive) - return x; - - - for (const TrackPtr &t : qAsConst(m_childrenTracks)) - { - const ItemPtrRefs x2(t->_items(true)); - x.insert(x.end(), x2.begin(), x2.end()); - - } - - - - return x; -} - - -ItemPtrRefs Track::_copyItems() const -{ - - ItemPtrRefs x; - - for (int id : qAsConst(m_copyItemIds)) - { - try - { - x.push_back(_item(id)); - } - catch(...) - { - fprintf(stderr, "Track::_copyItems: cannot find item for item id %d\n", id); - continue; - } - } - - return x; -} - - -ItemPtrNCVRefs Track::_itemsNCV(bool recursive) -{ - - ItemPtrNCVRefs x(m_items.begin(), m_items.end()); - - if (!recursive) - return x; - - - for (TrackPtr &t : m_childrenTracks) - { - ItemPtrNCVRefs x2(t->_itemsNCV(true)); - x.insert(x.end(), x2.begin(), x2.end()); - - } - - return x; -} - - - -const QVector Track::_itemIds(bool recursive) const -{ - QVector itemIds(transformItemPtrsToIds(m_items)); - - if (!recursive) - return itemIds; - - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - itemIds += trackPtr->_itemIds(true); - - return itemIds; -} - - -ItemPtrRef Track::_item(int id) const noexcept(false) -{ - - auto const it = std::find_if(m_items.cbegin(), m_items.cend(), - [id](const ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.cend()) - return *it; - - - for (const TrackPtr &track : qAsConst(m_childrenTracks)) - { - try - { - return track->_item(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("_item: item with id %1 not found!").arg(id); -} - - -void Track::printTrackIds(bool withItems, int level) const -{ - fprintf(stderr, "%*c Track %16.16s ", level * 2, '>', text().toStdString().c_str()); - - if (withItems) - { - fprintf(stderr, " ( "); - for (const ItemPtr &itemPtr : qAsConst(m_items)) - { - fprintf(stderr, "%d ", itemPtr->id()); - } - fprintf(stderr, ")"); - } - - fprintf(stderr, "\n"); - - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - trackPtr->printTrackIds(level + 1); -} - - -QVector Track::itemIds(bool recursive, bool sorted) const -{ - if (sorted) - { - if (!recursive) - return transformItemPtrsToIds(m_items); - - - ItemPtrRefs is(_items(true)); - - sort(is); - - return transformItemPtrsToIds(is); - } - - - - QVector itemIds; - itemIds.reserve(m_items.size()); - - itemIds += transformItemPtrsToIds(m_items); - - if (!recursive) - return itemIds; - - - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - itemIds += trackPtr->_itemIds(true); - - return itemIds; -} - - -void Track::addItemPtr(ItemPtr &&itemPtr) -{ - m_items.insert(std::upper_bound(m_items.begin(), m_items.end(), itemPtr->startTime(), - [](qint64 startTime, const ItemPtr &other) - { - return (other->startTime() > startTime); - }), std::move(itemPtr)); -} - - -void Track::removeItem(int itemId) noexcept(false) -{ - auto it = std::find_if(m_items.begin(), m_items.end(), - [itemId](const ItemPtr &itemPtr) - { - return (itemPtr->id() == itemId); - }); - - if (it == m_items.end()) - throw QStringLiteral("removeItem: item %1 not found").arg(itemId); - - m_items.erase(it); -} - - -void Track::removeTrack(int trackId) noexcept(false) -{ - auto it = std::find_if(m_childrenTracks.begin(), m_childrenTracks.end(), - [trackId](const TrackPtr &trackPtr) - { - return (trackPtr->id() == trackId); - }); - - if (it == m_childrenTracks.end()) - throw QStringLiteral("removeTrack: track %1 not found on track %2").arg(trackId, id()); - - m_childrenTracks.erase(it); -} - - -ItemPtr Track::takeItem(int itemId) noexcept(false) -{ - auto it = std::find_if(m_items.begin(), m_items.end(), - [itemId](const ItemPtr &itemPtr) - { - return (itemPtr->id() == itemId); - }); - - if (it == m_items.end()) - throw QStringLiteral("Track::takeItem: item with id %1 not found").arg(itemId); - - ItemPtr ptr = std::move(*it); - - m_items.erase(it); - - return ptr; -} - - - - - - - - - - - - - - - - - - - - - - - -Track &Track::track(int id) noexcept(false) -{ - - auto it = std::find_if(m_childrenTracks.begin(), m_childrenTracks.end(), - [id](TrackPtr &trackPtr) - { - return (trackPtr.get()->id() == id); - }); - - if (it != m_childrenTracks.end()) - { - Track &t = **it; - - return t; - } - - - for (TrackPtr &track : m_childrenTracks) - { - try - { - return track->track(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("Track: track with id %1 not found").arg(id); -} - - -ItemPtrNCVRef Track::_itemNCV(int id) noexcept(false) -{ - - auto const it = std::find_if(m_items.begin(), m_items.end(), - [id](ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.end()) - return *it; - - - for (TrackPtr &track : m_childrenTracks) - { - try - { - return track->_itemNCV(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("lookup failed!");; -} - - -Item &Track::item(int id) noexcept(false) -{ - - auto const it = std::find_if(m_items.begin(), m_items.end(), - [id](ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.end()) - return **it; - - - for (TrackPtr &track : m_childrenTracks) - { - try - { - return track->item(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("lookup failed!");; -} - - -const Item &Track::item(int id) const noexcept(false) -{ - - auto const it = std::find_if(m_items.cbegin(), m_items.cend(), - [id](const ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.cend()) - return **it; - - - for (const TrackPtr &track : qAsConst(m_childrenTracks)) - { - try - { - return track->item(id); - } - catch(...) - { - - - } - } - - throw QStringLiteral("lookup failed!");; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Item &Track::previous(int id) noexcept(false) -{ - - auto it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.end() && (*it) != m_items.front()) - return **(--it); - - throw QStringLiteral("Track::previous: previous item for id %1 not found").arg(id); -} - - -Item &Track::next(int id) noexcept(false) -{ - - auto it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) - { - return (itemPtr.get()->id() == id); - }); - - if (it != m_items.end() && (*it) != m_items.back()) - return **(++it); - - throw QStringLiteral("lookup failed!");; -} - - - - - - - - - - - - - - - - - - - - -int Track::rowOfTrack(int id) const noexcept(false) -{ - int row = 0; - for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) - { - if (trackPtr->id() == id) - return row; - row++; - } - - throw("not found"); -} - - -Track &Track::trackAt(int index) noexcept(false) -{ - return *m_childrenTracks.at(index); -} - - -const Track &Track::trackAt(int index) const noexcept(false) -{ - return *m_childrenTracks.at(index); -} - - -const Item &Track::copyItemAt(qint64 time) const noexcept(false) -{ - auto const itemPtrRefs = _copyItems(); - const ItemPtrRefs::const_iterator it = - std::find_if(itemPtrRefs.cbegin(), itemPtrRefs.cend(), [time](const ItemPtr &itemPtr) - { - return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); - }); - - if (it != itemPtrRefs.cend()) - return *it->get(); - - throw QStringLiteral("Track: can't find copy item at %1").arg(time); -} - - -Item &Track::itemAt(qint64 time) noexcept(false) -{ - - auto it = std::find_if(m_items.cbegin(), m_items.cend(), [time](const ItemPtr &itemPtr) - { - return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); - }); - - if (it != m_items.cend()) - return **it; - - throw QStringLiteral("lookup failed!");; -} - - -const Item &Track::itemAt(qint64 time) const noexcept(false) -{ - - auto const it = std::find_if(m_items.cbegin(), m_items.cend(), [time](const ItemPtr &itemPtr) - { - return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); - }); - - if (it != m_items.cend()) - return **it; - - throw QStringLiteral("lookup failed!");; -} - -} - - - - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsTimeline/track.h b/Lts/Common/LtsTimeline/track.h deleted file mode 100755 index 6ef03320d..000000000 --- a/Lts/Common/LtsTimeline/track.h +++ /dev/null @@ -1,263 +0,0 @@ - - - -#pragma once - -#include "item.h" - -#include - -#include -#include -#include - - -#include - - -namespace LtsTimeline -{ -class Track; - -class TimelineModel; - -typedef std::unique_ptr TrackPtr; -typedef std::vector Tracks; -typedef std::reference_wrapper TrackPtrRef; -typedef std::vector TrackPtrRefs; - - -class LTSTIMELINE_EXPORT Track -{ -public: - Track(const QString &text = {}) - : m_text(text) - {} - - Track(const QString &text, int specialTrackType) - : m_text(text) - , m_userData(specialTrackType) - {} - - Track(const Track &) = delete; - Track &operator=(const Track &) = delete; - Track(Track &&t) noexcept = default; - virtual ~Track() = default; - - [[deprecated("Use model's track(int) instead.")]] const Track &track(int id) const noexcept(false); - [[deprecated("Use model's track(int) instead.")]] Track &track(int id) noexcept(false); - [[deprecated("Use model's item(int) instead.")]] Item &item(int id) noexcept(false); - [[deprecated("Use model's item(int) instead.")]] const Item &item(int id) const noexcept(false); - Item &previous(int id) noexcept(false); - Item &next(int id) noexcept(false); - - const Item ©ItemAt(qint64 time) const noexcept(false); - Item &itemAt(qint64 time) noexcept(false); - const Item &itemAt(qint64 time) const noexcept(false); - Track &trackAt(int index) noexcept(false); - const Track &trackAt(int index) const noexcept(false); - bool hasTrack(int id, bool recursive = false) const; - bool hasTracks() const { return !m_childrenTracks.empty(); } - int rowOfTrack(int id) const noexcept(false); - const Track &parentOf(int id) const noexcept(false); - - - const QSet ©ItemIds() const { return m_copyItemIds; } - void clearCopyItemIds() { m_copyItemIds.clear(); } - void addCopyItemIds(const QSet &ids); - void addCopyItemId(int id); - bool hasItems() const { return !m_items.empty(); } - bool hasCopyItems() const { return !m_copyItemIds.empty(); } - int itemCount() const { return m_items.size(); } - Items &items() { return m_items; } - const Items &items() const { return m_items; } - int trackCount() const { return m_childrenTracks.size(); } - Tracks &tracks() { return m_childrenTracks; } - const Tracks &tracks() const { return m_childrenTracks; } - QVector itemIds(bool recursive = false, bool sorted = false) const; - const QVector itemIdsBetween(qint64 start, qint64 end, bool strict = false, bool complete = false, bool recursive = false, bool sorted = false, qint64 minimumDuration = 0) const; - [[deprecated]] const QVector copyItemIdsBetween(qint64 start, qint64 end, bool strict = false, bool complete = false, bool recursive = false, bool sorted = false, qint64 minimumDuration = 0) const; - bool isItemsBetween(qint64 start, qint64 end, const QVector &ignoreIds, bool strict = false, bool complete = false, bool recursive = false, qint64 minimumDuration = 0) const; - bool isItemsBetween(qint64 start, qint64 end, int ignoreId, bool strict = false, bool complete = false, bool recursive = false, qint64 minimumDuration = 0) const; - - - int id() const { return m_id; } - virtual const QString text() const { return m_text; } - int parent() const { return m_parentId; } - int parentId() const { return m_parentId; } - void printTrackIds(bool withItems = false, int level = 0) const; - void removeCopyItem(int itemId) noexcept(false); - void removeItem(int itemId) noexcept(false); - ItemPtr takeItem(int itemId) noexcept(false); - QVariant userData() const { return m_userData; } - void setUserData(const QVariant &data) { m_userData = data; } - - - inline static void sort(Items &items) - { - std::sort(items.begin(), items.end(), [](const ItemPtr &itemA, const ItemPtr &itemB) - { - return itemA->startTime() < itemB->startTime(); - }); - } - - - inline static void sort(ItemPtrNCVRefs &items) - { - std::sort(items.begin(), items.end(), [](const ItemPtrNCVRef &itemA, const ItemPtrNCVRef &itemB) - { - return itemA.get()->startTime() < itemB.get()->startTime(); - }); - } - - - inline static void sort(ItemPtrRefs &items) - { - std::sort(items.begin(), items.end(), [](const ItemPtrRef &itemA, const ItemPtrRef &itemB) - { - return itemA.get()->startTime() < itemB.get()->startTime(); - }); - } - - bool hasInsertButton() const { return m_hasInsertButton; } - void setInsertButton(bool hasButton) { m_hasInsertButton = hasButton; } - bool hasRemoveButton() const { return m_hasRemoveButton; } - void setRemoveButton(bool hasButton) { m_hasRemoveButton = hasButton; } - - -protected: - QString m_text; - QVariant m_userData; - - -private: - void removeTrack(int trackId) noexcept(false); - void addItemPtr(ItemPtr &&itemPtr); - - static bool isItemBetween(const Item &item, qint64 start, qint64 end, bool strict, bool complete, qint64 minimumDuration = 0); - ItemPtrRefs _copyItems() const; - ItemPtrRefs _items(bool recursive) const; - ItemPtrNCVRefs _itemsNCV(bool recursive); - ItemPtrNCVRef _itemNCV(int id) noexcept(false); - ItemPtrRef _item(int id) const noexcept(false); - const QVector _itemIds(bool recursive) const; - - -public: - void clearItems() { m_items.clear(); } -private: - - friend class TimelineModel; - - - void sort() - { - sort(m_items); - } - - - template - Track *addTrack(TrackClass &&trackClass) - { - TrackClass _trackClass = std::move(trackClass); - m_childrenTracks.push_back(std::move(std::move(std::make_unique(std::move(_trackClass))))); - return m_childrenTracks.back().get(); - } - - - template - [[deprecated("Use model's track(...) and call addTrack(TrackClass) directly on that track.")]] - Track *addTrack(int parentTrackId, TrackClass &&trackClass) noexcept(false) - { - if (m_id == parentTrackId) - return addTrack(std::move(trackClass)); - - return track(parentTrackId).addTrack(std::move(trackClass)); - } - - - template - Item *addItem(ItemClass &&itemClass) - { - ItemClass _itemClass(std::move(itemClass)); - - auto it = m_items.insert(std::upper_bound(m_items.begin(), m_items.end(), _itemClass.startTime(), - [](qint64 startTime, const ItemPtr &other) - { - return (other->startTime() > startTime); - }), std::move(std::make_unique(std::move(_itemClass)))); - - return it->get(); - } - - - Item *addItem(ItemPtr &&itemPtr) - { - auto it = m_items.insert(std::upper_bound(m_items.begin(), m_items.end(), itemPtr->startTime(), - [](qint64 startTime, const ItemPtr &other) - { - return (other->startTime() > startTime); - }), std::move(itemPtr)); - - return it->get(); - } - - - template - [[deprecated]] - QVector transformItemPtrsToIds(const ItemContainer &itemContainer) const - { - QVector ids; - std::transform(itemContainer.cbegin(), itemContainer.cend(), std::back_inserter(ids), - [](const ItemPtr &itemPtr) -> int - { - return itemPtr->id(); - }); - return ids; - } - - - - - - - - - - - - - - int m_id { -1 }; - Tracks m_childrenTracks; - Items m_items; - int m_parentId { -1 }; - bool m_hasInsertButton { false }; - bool m_hasRemoveButton { false }; - - - QSet m_copyItemIds; -}; - -} - - - - - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp b/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp deleted file mode 100755 index 75e4aae23..000000000 --- a/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp +++ /dev/null @@ -1,283 +0,0 @@ - - - -#include "AbstractTimelineModel.h" - -#include - - -namespace LtsTimelineClassic -{ - -AbstractTimelineModel::AbstractTimelineModel(QObject *parent) - : QAbstractItemModel(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.AbstractTimelineModel")) - , m_highestPositionedItem(0) - , m_lowestPositionedItem(0) - , m_getEventListsRecursive(true) - , m_rootItem(new Item(QLatin1String("#mwille/r00t/"))) - , m_markersItem(new Item(tr("Markers"), QStringLiteral("Markers"))) -{ - - - - m_rootItem->addItem(m_markersItem); -} - - -AbstractTimelineModel::~AbstractTimelineModel() -{ - delete m_rootItem; -} - - -void AbstractTimelineModel::retranslate() -{ - m_markersItem->setName(tr("Markers")); -} - - -void AbstractTimelineModel::invalidateTimeline() -{ - const LtsTimelineClassic::ItemList il = m_rootItem->getFullItemList(true); - for (auto i = il.constBegin(); i != il.constEnd(); ++i) - { - LtsTimelineClassic::Item *item = *i; - - if (item != nullptr) - item->setEventListChanged(); - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Event *AbstractTimelineModel::getEvent(int uniqueId) -{ - const LtsTimelineClassic::ItemList il = m_rootItem->getFullItemList(true); - for (auto i = il.constBegin(); i != il.constEnd(); ++i) - { - LtsTimelineClassic::Item *item = *i; - - if (item != nullptr && item->getIdentifier() != QStringLiteral("Markers")) - { - const LtsTimelineClassic::EventList el = item->getFullEventList(true, true); - - for (LtsTimelineClassic::Event *event : el) - { - if (event->uniqueId() == uniqueId) - return event; - } - } - } - - return nullptr; -} - - -void AbstractTimelineModel::addEvent(Event *event) -{ - QStringList actuatorPath = event->getActuator(); - - - Item *currentItem = m_rootItem; - QModelIndex currentIndex = QModelIndex(); - - for (const QString &curPath : qAsConst(actuatorPath)) - { - Item *oldItem = currentItem; - const ItemList *itemList = currentItem->getChildren(); - - if (itemList != nullptr) - { - for (int row = 0; row < itemList->count(); row++) - { - Item *childItem = itemList->at(row); - if (childItem->getName() == curPath) - { - currentIndex = index(row, 0, currentIndex); - currentItem = childItem; - break; - } - } - } - - if (oldItem == currentItem) - { - beginInsertRows(currentIndex, currentItem->childCount(), currentItem->childCount()); - Item *newItem = new Item(curPath); - currentItem->addItem(newItem); - endInsertRows(); - - currentIndex = index(currentItem->childCount() - 1, 0, currentIndex); - currentItem = newItem; - } - } - - currentItem->addEvent(event); -} - - -Item *AbstractTimelineModel::getRootItem() -{ - return m_rootItem; -} - - -Item *AbstractTimelineModel::itemFromIndex(const QModelIndex &idx) const -{ - if (!idx.isValid()) - return m_rootItem; - return static_cast(idx.internalPointer()); -} - - -QVariant AbstractTimelineModel::data(const QModelIndex &index, int role) const -{ - if (role != Qt::DisplayRole || !index.isValid()) - return QVariant(); - - Item *item = itemFromIndex(index); - if (!item) - return QVariant(); - - - - - - if (index.column() == 1) - { - PassiveEventContainer *pevcon = item->getPevcon(); - return QVariant::fromValue(pevcon); - } - - - QString name(item->getName()); - - if (name.isEmpty()) - name = tr("[unknown]"); - - return QVariant(name); -} - - -Qt::ItemFlags AbstractTimelineModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; -} - - -QVariant AbstractTimelineModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - { - - switch (section) - { - case 0: return QString(tr("Actuator") + QStringLiteral("\r\n")); - case 1: return QString(tr("Events") + QStringLiteral("\r\n")); - } - - - - - - - } - - return QVariant(); -} - - -QModelIndex AbstractTimelineModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - Item *item = itemFromIndex(parent); - Item *child = item ? item->child(row) : nullptr; - - if (child) - { - - return createIndex(row, column, child); - } - - return QModelIndex(); -} - - -QModelIndex AbstractTimelineModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - Item *item = itemFromIndex(index); - Item *parent = item ? item->getParent() : nullptr; - - if (parent == m_rootItem || parent == nullptr) - return QModelIndex(); - - int idx = parent->indexOfParent(); - - if (idx == -1) - return QModelIndex(); - - return createIndex(idx, 0, parent); -} - - -int AbstractTimelineModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) - { - int r = m_rootItem->visibleChildCount(); - - return r; - } - - Item *item = itemFromIndex(parent); - if (item) - { - int r = item->visibleChildCount(); - - return r; - } - - return 0; -} - - -int AbstractTimelineModel::columnCount(const QModelIndex &) const -{ - return 2; -} - -} diff --git a/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h b/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h deleted file mode 100755 index 88daea22a..000000000 --- a/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h +++ /dev/null @@ -1,112 +0,0 @@ - - - -#pragma once - -#include "Event.h" -#include "Item.h" -#include - -#include -#include - - -namespace LtsTimelineClassic -{ - - - -class LTSTIMELINECLASSIC_EXPORT AbstractTimelineModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - explicit AbstractTimelineModel( QObject *parent = nullptr); - ~AbstractTimelineModel(); - - void retranslate(); - - - - Item *getRootItem(); - - Event *getEvent(int uniqueId); - - - void invalidateTimeline(); - - - - void refreshData(); - - qint64 highestPositionedItem() const { return m_highestPositionedItem; } - qint64 lowestPositionedItem() const { return m_lowestPositionedItem; } - - -protected: - NGL::INGLLogger &m_logger; - - -protected: - - - void addEvent(Event *event); - - - - QVariant data(const QModelIndex &index, int role) const; - - - - Qt::ItemFlags flags(const QModelIndex &index) const; - - - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - - - - Item *itemFromIndex(const QModelIndex &idx) const; - - - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - - - - QModelIndex parent(const QModelIndex &index) const; - - - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - - - - int columnCount(const QModelIndex &parent = QModelIndex()) const; - - - virtual qint64 currentTime() const = 0; - - - qint64 m_highestPositionedItem; - qint64 m_lowestPositionedItem; - bool m_getEventListsRecursive; - Item *m_rootItem; - Item *m_markersItem; - - -signals: - - - void signalContextMenu(const QPoint &position, Event *affectedEvent); - - - - void signalClicked(Event *affectedEvent); - - - void signalToolTip(const QPoint &p, Event *e); - -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/CMakeLists.txt b/Lts/Common/LtsTimelineClassic/CMakeLists.txt deleted file mode 100755 index f61941877..000000000 --- a/Lts/Common/LtsTimelineClassic/CMakeLists.txt +++ /dev/null @@ -1,82 +0,0 @@ -# -# CMakeLists.txt for Timeline Library -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2017-02-10 -# - -SET(timeline_SRCS - EventDelegate.cpp - EventTools.cpp - Item.cpp - PassiveEventContainer.cpp - TimelineWidget.cpp - TimelineHeaderView.cpp - TimelineIndicator.cpp - TestTimelineModel.cpp - AbstractTimelineModel.cpp - TimelineView.cpp - Event.cpp - EventGraphic.cpp - - EventDelegate.h - EventList.h - EventTools.h - Item.h - PassiveEventContainer.h - TimelineWidget.h - TimelineHeaderView.h - TimelineIndicator.h - AbstractTimelineModel.h - TimelineView.h - #timeline_export.h - Event.h - TestTimelineModel.h - EventGraphic.h - LineMap.h -) - -SET(timeline_UICS - Ui/TimelineWidgetUi.ui -) - -NGL_WRAP_UI(timeline_SRCS ${timeline_UICS}) - -NGL_ADD_LIBRARY2(WS LtsTimelineClassic SHARED ${timeline_SRCS} ${timeline_UICS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsTimelineClassic - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsTimelineClassic - PRIVATE - NGLLogger - ngltools # DTG - Qt5::Core - Qt5::Gui - Qt5::Widgets - LtsSessionDataDds -) - - -IF(NEVER) - NGL_ADD_EXECUTABLE(WS LtsTimelineClassicTest main.cpp) - NGL_TARGET_LINK_LIBRARIES(LtsTimelineClassicTest - nglapplication - NGLLogger - Qt5::Core - Qt5::Gui - Qt5::Widgets - LtsTimelineClassic - svcEemCommon - svcEemClient - PdmDataPluginInterface - ) -ENDIF() - -NGL_INSTALL_TARGET(LtsTimelineClassic) - diff --git a/Lts/Common/LtsTimelineClassic/Event.cpp b/Lts/Common/LtsTimelineClassic/Event.cpp deleted file mode 100755 index d2305c973..000000000 --- a/Lts/Common/LtsTimelineClassic/Event.cpp +++ /dev/null @@ -1,219 +0,0 @@ - - - -#include "Event.h" - -#include - -#include -#include -#include - -#include - -namespace LtsTimelineClassic -{ -Event::Event(int uniqueId, int typeId, qint64 startTime, - qint64 endTime, int playerId, int partyId, int equipmentId, const QStringList &path, - const EventPaintFunc &paintFunc, const QColor &paintColor, const QJsonObject &detailData, int subEventId, - const LtsUtils::QPoint3D &pos, LtsSession::LtsSession *session) - : m_path(path) - , m_detailData(detailData) - , m_position(pos) - , m_paintFunc(paintFunc) - , m_session(session) - , m_eventColor(paintColor) - , m_startTime(startTime) - , m_endTime(endTime) - , m_uniqueId(uniqueId) - , m_typeId(typeId) - , m_subEventId(subEventId) - , m_playerId(playerId) - , m_partyId(partyId) - , m_equipmentId(equipmentId) - , m_deleted(false) - , m_visible(true) - -{ - checkDuration(); -} - -void Event::setVisible(bool visibility) -{ - if (visibility) - show(); - else - hide(); -} - - -void Event::hide() -{ - this->m_visible = false; -} - - -void Event::show() -{ - this->m_visible = true; -} - -QString Event::getShortActuator() const -{ - return m_path.isEmpty() ? QString() : m_path.last(); -} - - - - - - - - - - - - - - -void Event::setEndT(qint64 newEnd) -{ - m_endTime = newEnd; - - checkDuration(); -} - - -bool Event::checkDuration() -{ - - - - - - - - - - - - - - - - - - - - - return true; -} - - -qint64 Event::duration(bool ignoreLiveEvents) const -{ - if (m_endTime > m_startTime) - { - if (m_duration == -1) - m_duration = m_endTime - m_startTime; - - return m_duration; - } - - if (m_endTime < 0 && !ignoreLiveEvents) - { - qint64 currentTime = (m_session != nullptr) ? m_session->partitionTime() : time(nullptr) ; - qint64 duration = currentTime - m_startTime; - - return duration; - } - - return 0; -} - - -void Event::drawMultipleEventGraphic(QPainter *painter, const QRect &rect) -{ - painter->save(); - painter->setBrush(Qt::white); - - int x = rect.x(); - int y = rect.y(); - int h = rect.height(); - int w = rect.width(); - - - painter->drawRect(rect); - - - int s1 = y + h - (h * 0.4); - int s2 = y + h - (h * 0.2); - - painter->drawLine(QPoint(x, s1), - QPoint(x + w, s1)); - painter->drawLine(QPoint(x, s2), - QPoint(x + w, s2)); - - painter->restore(); -} - -void Event::drawCustomEventGraphicTriangle(QPainter *painter, const QRect &rect) -{ - int x = rect.x(); - int y = rect.y(); - int h = rect.height(); - int w = rect.width(); - - QPolygon pol; - pol.append(QPoint(x + 0, y + h)); - pol.append(QPoint(x + (w / 2), y + 0)); - pol.append(QPoint(x + w, y + h)); - pol.append(QPoint(x + 0, y + h)); - - QPainterPath path; - path.moveTo(x, y); - path.addPolygon(pol); - - painter->drawPath(path); -} - -void Event::drawCustomEventGraphicInvertTriangle(QPainter *painter, const QRect &rect) -{ - int x = rect.x(); - int y = rect.y(); - int h = rect.height(); - int w = rect.width(); - - QPolygon pol; - pol.append(QPoint( x + 0 , y + 0)); - pol.append(QPoint( x+ w , y + 0)); - pol.append(QPoint(x + ( w / 2 ), y + h)); - pol.append(QPoint( x + 0 , y + 0)); - - QPainterPath path; - path.moveTo(x, y); - path.addPolygon(pol); - - painter->drawPath(path); -} -void Event::drawCustomEventGraphicEllipse(QPainter *painter, const QRect &rect) -{ - QPoint ellipsePos = rect.center(); - ellipsePos.rx()++; - ellipsePos.ry()++; - - painter->drawEllipse(ellipsePos, rect.width() / 2, rect.height() / 2); -} - -void Event::drawCustomEventGraphicRect(QPainter *painter, const QRect &rect) -{ - painter->drawRect(rect); -} - - -void Event::drawCustomEventGraphicRoundedRect(QPainter *painter, const QRect &rect) -{ - painter->drawRoundedRect(rect, 50, 50, Qt::RelativeSize); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/Event.h b/Lts/Common/LtsTimelineClassic/Event.h deleted file mode 100755 index 45001518f..000000000 --- a/Lts/Common/LtsTimelineClassic/Event.h +++ /dev/null @@ -1,155 +0,0 @@ - - - -#pragma once - -#include - -#include -#include -#include -#include - -class QPainter; -class QRect; - -namespace LtsSession -{ -class LtsSession; -} - -namespace LtsTimelineClassic -{ - - - -class LTSTIMELINECLASSIC_EXPORT Event -{ -public: - enum ButtonType - { - ActiveEvent, - EventBlock, - }; - - - typedef void (*EventPaintFunc)(QPainter *, const QRect &); - static void drawCustomEventGraphicTriangle(QPainter *painter, const QRect &rect); - static void drawCustomEventGraphicInvertTriangle(QPainter *painter, const QRect &rect); - static void drawCustomEventGraphicEllipse(QPainter *painter, const QRect &rect); - static void drawCustomEventGraphicRect(QPainter *painter, const QRect &rect); - static void drawCustomEventGraphicRoundedRect(QPainter *painter, const QRect &rect); - static void drawMultipleEventGraphic(QPainter *painter, const QRect &rect); - - - Event() = default; - Event(int uniqueId, int typeId, qint64 startTime, qint64 endTime, - int playerId, int partyId, int equipmentId, const QStringList &path, - const EventPaintFunc &paintFunc, const QColor &paintColor, - const QJsonObject &detailData, int subEventId = -1, - const LtsUtils::QPoint3D &pos = LtsUtils::QPoint3D(), - LtsSession::LtsSession *session = nullptr); - - - - - QStringList getActuator() const { return m_path; } - - int typeId() const { return m_typeId; } - int uniqueId() const { return m_uniqueId; } - - int getSubEventID() const; - - QColor getColor() const { return m_eventColor; } - - EventPaintFunc getPaintFunc() const { return m_paintFunc; } - - - - qint64 startTime() const { return m_startTime; } - qint64 endTime() const { return m_endTime; } - - - - - - - - - - const QJsonObject &detailData() const { return m_detailData; } - void setDetailData(const QJsonObject &detailData) { m_detailData = detailData; } - - void setEndT(qint64 newEnd); - - - - qint64 duration(bool ignoreLiveEvents = false) const; - - int playerId() const { return m_playerId; } - void setPlayerId(int playerId) { m_playerId = playerId; } - - int partyId() const { return m_partyId; } - void setPartyId(int teamId) { m_partyId = teamId; } - - int equipmentId() const { return m_equipmentId; } - void setEquipmentId(int eqId) { m_equipmentId = eqId; } - - QString getShortActuator() const; - - bool positionValid() const { return !std::isnan(m_position.x()) && !std::isnan(m_position.y()); } - const LtsUtils::QPoint3D &position() const { return m_position; } - QPointF pos2d() const { return QPointF(m_position.x(), m_position.y()); } - void setPosition(const LtsUtils::QPoint3D &pos) { m_position = pos; } - - - - - void hide(); - - - - void show(); - - - - bool isVisible() const { return m_visible; } - - - - void setVisible(bool visibility = true); - - void setDeleted() { m_deleted = true; } - - bool isDeleted() const { return m_deleted; } - - -protected: - QStringList m_path; - QJsonObject m_detailData; - - LtsUtils::QPoint3D m_position; - EventPaintFunc m_paintFunc = nullptr; - LtsSession::LtsSession *m_session = nullptr; - QColor m_eventColor; - qint64 m_startTime = -1; - qint64 m_endTime = -1; - mutable qint64 m_duration = -1; - - - int m_uniqueId = -1; - int m_typeId = -1; - int m_subEventId = -1; - int m_playerId = -1; - int m_partyId = -1; - int m_equipmentId = -1; - bool m_deleted = false; - bool m_visible = false; - -private: - - - static bool checkDuration(); -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/EventDelegate.cpp b/Lts/Common/LtsTimelineClassic/EventDelegate.cpp deleted file mode 100755 index 11929c6a2..000000000 --- a/Lts/Common/LtsTimelineClassic/EventDelegate.cpp +++ /dev/null @@ -1,62 +0,0 @@ - - - -#include "EventDelegate.h" -#include "PassiveEventContainer.h" - -#include - -#include - - -namespace LtsTimelineClassic -{ - -EventDelegate::EventDelegate(QWidget *parent) - : QStyledItemDelegate(parent) - , m_liveTime(0) -{ - -} - -QSize EventDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - (void)option; - (void)index; - - return QSize(50, 20); -} - -void EventDelegate::setKnownRanges(const QVector &knownRanges) -{ - m_knownRanges = knownRanges; -} - -void EventDelegate::setLiveTime(qint64 liveTime) -{ - m_liveTime = liveTime; -} - -void EventDelegate::slotAddLine(int posX, qint64 time) -{ - m_lineMap.insert(posX, time); -} - -void EventDelegate::slotResetLineList() -{ - m_lineMap.clear(); -} - -void EventDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (index.data().canConvert()) - { - PassiveEventContainer *passiveEventContainer = qvariant_cast(index.data()); - - passiveEventContainer->paint(painter, option, m_lineMap, m_knownRanges); - } - else - QStyledItemDelegate::paint(painter, option, index); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/EventDelegate.h b/Lts/Common/LtsTimelineClassic/EventDelegate.h deleted file mode 100755 index a6ab695c6..000000000 --- a/Lts/Common/LtsTimelineClassic/EventDelegate.h +++ /dev/null @@ -1,55 +0,0 @@ - - - -#pragma once - -#include "AbstractTimelineModel.h" -#include "Range.h" - -#include - - -namespace LtsTimelineClassic -{ - - - -class EventDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - - - EventDelegate(QWidget *parent = nullptr); - void setKnownRanges(const QVector &knownRanges); - - - - QSize sizeHint(const QStyleOptionViewItem &option = QStyleOptionViewItem(), const QModelIndex &index = QModelIndex()) const; - - void setLiveTime(qint64 liveTime); -private: - - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - - - - - - LineMap m_lineMap; - QVector m_knownRanges; - qint64 m_liveTime; - -public slots: - - - void slotResetLineList(); - - - - void slotAddLine(int posX, qint64 time); -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/EventGraphic.cpp b/Lts/Common/LtsTimelineClassic/EventGraphic.cpp deleted file mode 100755 index 36217f1ce..000000000 --- a/Lts/Common/LtsTimelineClassic/EventGraphic.cpp +++ /dev/null @@ -1,175 +0,0 @@ - - - - -#include "EventGraphic.h" - -#include -#include -#include -#include -#include -#include - - -#include - - -namespace LtsTimelineClassic -{ - -EventGraphic::EventGraphic(Event *event) - : m_lowest(0) - , m_duration(-1) - , m_isMultipleEvent(false) -{ - m_eventList += event; -} - - -void EventGraphic::addEvent(Event *event) -{ - if (event == nullptr) - return; - - m_eventList += event; - m_isMultipleEvent = true; - m_lowest = 0; - m_duration = -1; -} - - -void EventGraphic::addEventList(const QVector &eventList) -{ - if (eventList.isEmpty()) - return; - - m_eventList += eventList; - m_isMultipleEvent = true; - m_lowest = 0; - m_duration = -1; -} - - -qint64 EventGraphic::getDuration() -{ - if (m_duration != -1) - return m_duration; - - qint64 highestDuration = 0; - qint64 lowest = lowestTime(); - - for (Event *event : qAsConst(m_eventList)) - { - - - - - - - qint64 duration = (event->startTime() - lowest) + event->duration(); - - if (duration > highestDuration) - highestDuration = duration; - } - - if (highestDuration < 0) - { - qWarning() << "duration < 0 !!" << highestDuration; - Q_ASSERT(false); - return 0; - } - - m_duration = highestDuration; - return m_duration; -} - - -qint64 EventGraphic::lowestTime() const -{ - if (m_lowest != 0) - return m_lowest; - - auto i = std::min_element(m_eventList.begin(), m_eventList.end(), [](const Event *a, const Event *b) - { - return a->startTime() < b->startTime(); - }); - - if (i == m_eventList.end()) - return 0; - - m_lowest = (*i)->startTime(); - return m_lowest; -} - - -bool EventGraphic::isMultipleEvent() const -{ - return m_isMultipleEvent; -} - - -void EventGraphic::drawEventGraphic(QPainter *painter, const QRect &rect) const -{ - painter->save(); - - painter->setPen(QPen(QBrush(QGuiApplication::palette().text()), 1.0)); - - if (isMultipleEvent()) - Event::drawMultipleEventGraphic(painter, rect); - else - { - Event *e = m_eventList.first(); - - if (e == nullptr) - { - painter->restore(); - return; - } - - painter->setBrush(QBrush(e->getColor())); - - if (e->getPaintFunc() == nullptr) - { - painter->restore(); - return; - } - - e->getPaintFunc()(painter, rect); - } - - painter->restore(); -} - - -void EventGraphic::drawDurationBackground(QPainter *painter, const QRect &rect) const -{ - painter->save(); - if (!isMultipleEvent()) - { - Event *e = m_eventList.first(); - - if (e == nullptr) - { - painter->restore(); - return; - } - - painter->setBrush(QBrush(e->getColor())); - painter->setPen(QPen(QBrush(e->getColor()), 0)); - } - else - { - painter->setBrush(QBrush(QColor("white"))); - } - - painter->drawRect(rect.adjusted(0, 0, -2, 0)); - - painter->setPen(QPen(QBrush(QGuiApplication::palette().text()), 1.0)); - - painter->drawLine(rect.topLeft(), rect.topRight()); - painter->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(1, 1)); - painter->restore(); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/EventGraphic.h b/Lts/Common/LtsTimelineClassic/EventGraphic.h deleted file mode 100755 index 969ce88c7..000000000 --- a/Lts/Common/LtsTimelineClassic/EventGraphic.h +++ /dev/null @@ -1,48 +0,0 @@ - - - - -#pragma once - -#include "EventList.h" -#include "Event.h" -#include - -namespace LtsTimelineClassic -{ - -class LTSTIMELINECLASSIC_EXPORT EventGraphic -{ -public: - - enum Action - { - ACTION_LEFT_CLICKED, - ACTION_RIGHT_CLICKED, - ACTION_TOOLTIP, - }; - - explicit EventGraphic(Event *event); - - bool isMultipleEvent() const; - - const QVector eventList() const { return m_eventList; } - void addEventList(const QVector &eventList); - void addEvent(Event *event); - qint64 lowestTime() const; - qint64 getDuration(); - - void drawEventGraphic(QPainter *painter, const QRect &rect) const; - void drawDurationBackground(QPainter *painter, const QRect &rect) const; - - -private: - EventList m_eventList; - - mutable qint64 m_lowest; - mutable qint64 m_duration; - bool m_isMultipleEvent; - -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/EventList.h b/Lts/Common/LtsTimelineClassic/EventList.h deleted file mode 100755 index bb76db606..000000000 --- a/Lts/Common/LtsTimelineClassic/EventList.h +++ /dev/null @@ -1,17 +0,0 @@ - - - -#pragma once - -#include - -namespace LtsTimelineClassic -{ - -class Event; -using EventList = QVector; - -class EventGraphic; -using EventGraphicList = QVector; - -} diff --git a/Lts/Common/LtsTimelineClassic/EventTools.cpp b/Lts/Common/LtsTimelineClassic/EventTools.cpp deleted file mode 100755 index 61fd7415a..000000000 --- a/Lts/Common/LtsTimelineClassic/EventTools.cpp +++ /dev/null @@ -1,420 +0,0 @@ - - - -#include "EventTools.h" -#include "EventGraphic.h" -#include "Event.h" - -#include -#include -#include -#include - - -namespace LtsTimelineClassic -{ - -#define logger NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.EventTools") -#ifdef LTS_DEBUG - -#endif -#ifdef VERBOSE - #define dbg_print(x) qWarning() << #x << x -#else - #define dbg_print(x) (void)x; -#endif - -qint64 EventTools::s_minAdjusted = 0; -qint64 EventTools::s_maxAdjusted = 1; -qint64 EventTools::s_minTimeline = 0; -qint64 EventTools::s_maxTimeline = 1; -qint64 EventTools::s_sbPos = 0; -qint64 EventTools::s_zoomFactor = 1; -int EventTools::s_paintMin = 0; -int EventTools::s_paintDiff = 0; -bool EventTools::s_isValid = false; -qint64 EventTools::s_diff = 0; -int EventTools::s_sectionHeight = 0; -int EventTools::s_test = 0; -qint64 EventTools::s_blockSizeTime = 0; -bool EventTools::s_inUse = false; - - - - -void EventTools::setValues(const qint64 min, const qint64 max, const qint64 sbPos, const qint64 zoomFactor) -{ - if (s_minTimeline == min && s_maxTimeline == max && s_sbPos == sbPos && s_zoomFactor == zoomFactor) - return; - - s_sbPos = sbPos; - s_zoomFactor = zoomFactor; - - - - - s_isValid = false; - - s_minAdjusted = s_sbPos; - - double d_max = s_minAdjusted; - d_max += s_zoomFactor; - - s_maxAdjusted = d_max; - s_diff = s_maxAdjusted - s_minAdjusted; - - s_minTimeline = min; - s_maxTimeline = max - EventTools::s_diff + (EventTools::getBlockSizeTime() * 2); - - recalcBlockSizeTime(); - - if (min > 0 && max > 1) - { - s_isValid = true; - - - } - else - { - - } - -#ifdef VERBOSE - qWarning() << "\r\n\r\nEventTools::setValues: " << "\r\n" - << "s_sbPos" << s_sbPos << "\r\n" - << "s_zoomFactor" << s_zoomFactor << "\r\n" - << "s_minTimeline" << s_minTimeline << "\r\n" - << "s_maxTimeline" << s_maxTimeline << "\r\n" - << "s_isValid" << s_isValid << "\r\n" - << "s_minAdjusted" << s_minAdjusted << "\r\n" - << "s_maxAdjusted" << (long)s_maxAdjusted << "\r\n" - << "s_diff" << s_diff << "\r\n" - ; -#endif -} - - -void EventTools::setSection(const int x, const int length) -{ - s_paintMin = x; - s_paintDiff = length; - -} - - -bool EventTools::isValid() -{ -#ifdef VERBOSE - if (!s_isValid) - qWarning() << "<<<< INVALID EVENT TOOLS!!"; - - qWarning() << "\r\nEventTools::isValid: " << "\r\n" - << "s_isValid" << s_isValid << "\r\n" - ; -#endif - - return s_isValid; -} - -qint64 EventTools::getAdjustedMin() -{ - if (!isValid()) - return 0; - - return s_minAdjusted; -} - -qint64 EventTools::getAdjustedMax() -{ - if (!isValid()) - return 1; - - return s_maxAdjusted; -} - -qint64 EventTools::getTimelineMin() -{ - if (!isValid()) - return 0; - - return s_minTimeline; -} - -qint64 EventTools::getScrollbarPosition() -{ - return s_sbPos; -} - -qint64 EventTools::getTimelineMax() -{ - if (!isValid()) - return 1; - - return s_maxTimeline; -} - -int EventTools::getEventPosition(qint64 eventTime) -{ - qint64 min = s_minAdjusted; - - qint64 diff = s_diff; - - - - - if (diff == 0) - { -#ifdef VERBOSE - qWarning() << "\n\ngetEventPosition: diff == 0\n"; -#endif - return 0; - } - - double pDiff = s_paintDiff; - double pMin = s_paintMin; - - double x1 = eventTime; - x1 -= min; - x1 /= diff; - - double x2 = x1; - x2 *= pDiff; - x2 += pMin; - -#ifdef VERBOSE - qWarning() << "\r\n\r\nEventTools::getEventPosition: " << "\r\n" - << "min" << min << "\r\n" - << "diff" << diff << "\r\n" - << "pDiff" << pDiff << "\r\n" - << "pMin" << pMin << "\r\n" - << (long)x1 << "[x1]" << "= (" << eventTime << "[eventTime]" << "-" << min << "[min]" << ") /" << diff << "[diff]" << "\r\n" - << (long)x2 << "[x2]" << "= (" << (long)x1 << "[x1]" << "*" << pDiff << "[pDiff]" << ") +" << pMin << "[pMin]" << "\r\n" - ; - - dbg_print(min); -#endif - - return qRound(x2); -} - -qint64 EventTools::getEventTime(double eventPosition) -{ - if (!isValid()) - { - - return 0; - } - - if (eventPosition > 999999) - { - - WARNING_LOG(logger) << "getEventTime: pos > 999999 => returning 0" ; - return 0; - } - - double min = s_minAdjusted; - double diff = s_diff; - double pDiff = s_paintDiff; - - if (pDiff < 1) - { - - WARNING_LOG(logger) << "getEventTime: pDiff < 1" ; - return 0; - } - - double pMin = s_paintMin; - - double x1 = eventPosition; - x1 -= pMin; - x1 /= pDiff; - - double x2 = x1; - x2 *= diff; - x2 += min; - - return qRound64(x2); -} - - -void EventTools::drawMarkLines(QPainter *painter, const EventList &eventList, int viewHeight) -{ - if (!isValid()) - return; - - - - for (int k = 0; k < eventList.count(); k++) - { - Event *e = eventList.at(k); - int pos = getEventPosition(e->startTime()); - - if (pos < (s_paintMin) || pos > (s_paintMin + s_paintDiff)) - continue; - - painter->drawLine(pos, 0, pos, viewHeight); - } -} - - -void EventTools::recalcBlockSizeTime() -{ - s_blockSizeTime = _getBlockSizeTime(); -} - - -qint64 EventTools::_getBlockSizeTime() -{ - - int startPos = getEventPosition(s_minAdjusted); - int endPos = getEventPosition(s_maxAdjusted); - - int posDiff = endPos - startPos; - - if (posDiff < 1) - { - - return 0; - } - - qint64 diff = s_maxAdjusted - s_minAdjusted; - - double pixelWidthInSeconds = diff; - pixelWidthInSeconds /= posDiff; - - int eventGraphicsWidth = eventWidth(); - qint64 result = eventGraphicsWidth; result *= pixelWidthInSeconds; - - #ifdef VERBOSE - qWarning() << "\r\n\r\nEventTools::recalcBlockSizeTime: " << "\r\n" - << "minAdj" << s_minAdjusted << "\r\n" - << "maxAdj" << s_maxAdjusted << "\r\n" - << "endPos" << endPos << "\r\n" - << "diff" << diff << "\r\n" - << "pixelWidthInSeconds" << pixelWidthInSeconds << "\r\n" - << "eventGraphicsWidth" << eventGraphicsWidth << "\r\n" - << "result" << result << "\r\n" - << "(duration > result) ?" << (duration > result) - ; - #endif - - return result; -} - - -qint64 EventTools::getBlockSizeTime(qint64 duration) -{ - if (duration < s_blockSizeTime) - return s_blockSizeTime; - else - return duration; -} - - -void EventTools::drawEvents(QPainter *painter, const EventGraphicList &eventGraphicList, const int verticalPosition ) -{ - painter->save(); - - - painter->setClipping(true); - painter->setClipRect(QRect(s_paintMin, verticalPosition, s_paintDiff, s_sectionHeight)); - - - for (int i = 0; i < eventGraphicList.count(); i++) - { - EventGraphic *eg = eventGraphicList.at(i); - - if (!eg) - { - - - continue; - } - - qint64 eventStart = eg->lowestTime(); - - if (eventStart == 0) - continue; - - - -#ifdef VERBOSE - qWarning() << "\r\n\r\nEventTools::drawEvents" << "\r\n" - << "eventStart" << eventStart << "\r\n"; -#endif - - qint64 eventDuration = eg->getDuration(); - - int newEventPosition = getEventPosition(eventStart); - - int newEventEndPosition = 0; - int finalEventEndPos = 0; - - int eventGraphicsWidth = eventWidth(); - - if (eventDuration > 1) - { - qint64 eventPosition = eventStart; - eventPosition += eventDuration; - newEventEndPosition = getEventPosition(eventPosition); - finalEventEndPos = GetEndPos(newEventEndPosition, eventGraphicsWidth); - } - else - { - newEventEndPosition = newEventPosition; - } - - - if (newEventPosition + (newEventEndPosition - newEventPosition) < ((-eventGraphicsWidth) + s_paintMin) || - newEventPosition > (s_paintMin + s_paintDiff)) - { -#ifdef VERBOSE - qWarning() << "\n\ndrawEvents: boundary check result was negative\n" << - " newEventPosition=" << newEventPosition << "\n" << - " newEventEndPosition=" << newEventPosition << "\n" << - " eventGraphicsWidth=" << eventGraphicsWidth << "\n" << - " s_paintMin=" << s_paintMin << "\n" << - " s_paintDiff=" << s_paintDiff << "\n"; -#endif - continue; - } - - - - int finalEventPos = newEventPosition; - - QRect eventGraphicRect(finalEventPos, verticalPosition + 2, eventGraphicsWidth, s_sectionHeight - 6); - - - - eg->drawEventGraphic(painter, eventGraphicRect); - -#ifdef VERBOSE - qWarning() << "drawEvents > actually draw an event at " - "x=" << eventGraphicRect.x() << " " - "y=" << eventGraphicRect.y() << " " - "eventStart=" << eventStart ; -#endif - - if (eventDuration > 1 && - (finalEventEndPos - 1) > finalEventPos) - { - QRect eventGraphicEndRect(finalEventEndPos, verticalPosition + 2, eventGraphicsWidth, s_sectionHeight - 6); - eg->drawEventGraphic(painter, eventGraphicEndRect); - - - - QRect eventWholeRect(newEventPosition + (eventGraphicsWidth / 2) - - , verticalPosition + 2 - , (newEventEndPosition - newEventPosition) - (eventGraphicsWidth) - , s_sectionHeight - 6); - - eg->drawDurationBackground(painter, eventWholeRect); - } - } - - painter->restore(); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/EventTools.h b/Lts/Common/LtsTimelineClassic/EventTools.h deleted file mode 100755 index 0919c108b..000000000 --- a/Lts/Common/LtsTimelineClassic/EventTools.h +++ /dev/null @@ -1,152 +0,0 @@ - - - -#pragma once - -#include "EventList.h" - -#include -#include - -#include - - -class QPainter; - - -namespace LtsTimelineClassic -{ - - - -class LTSTIMELINECLASSIC_EXPORT EventTools -{ - static const int s_EventTopPadding = 2; - static const int s_EventHeightPadding = -6; - static const int s_ButtonTopPadding = s_EventTopPadding + 2; - static const int s_ButtonHeightPadding = s_EventHeightPadding + 7; - -public: - - static int s_test; - - - static qint64 fromMsecsToSeconds(qint64 msecs) - { - return (msecs / 1000); - } - - static qint64 fromSecondsToMsecs(qint64 minutes) - { - return (minutes * 1000); - } - - - static qint64 getBlockSizeTime(qint64 duration = 0); - - - - static void setValues(const qint64 min, const qint64 max, const qint64 sbPos, const qint64 zoomFactor); - - - - - - - static void setSection(const int x, const int length); - - - - static bool isValid(); - - - - static bool isRelative(); - - - - static int getEventPosition(qint64 eventTime); - - - - static qint64 getAdjustedMin(); - - - - static qint64 getAdjustedMax(); - - - - static qint64 getTimelineMin(); - - - - static qint64 getTimelineMax(); - - static qint64 getScrollbarPosition(); - - static void recalcBlockSizeTime(); - - - - static qint64 getEventTime(double eventPosition); - - - - static void drawEvents(QPainter *painter, const EventGraphicList &eventGraphicList, const int verticalPosition); - - - - static void drawMarkLines(QPainter *painter, const EventList &eventList, int viewHeight); - - - - - - - - - - - - static inline int eventWidth() - { - return s_sectionHeight / 2; - } - - - - static inline int GetEndPos(int position, int graphicsWidth) - { - - return position - graphicsWidth - 1; - } - - static inline float zoomFactor() { return s_zoomFactor; } - - static inline void setSectionHeight(int sectionHeight) { s_sectionHeight = sectionHeight; } - static bool s_inUse; - -private: - static qint64 _getBlockSizeTime(); - - - - - static qint64 s_minAdjusted; - static qint64 s_maxAdjusted; - static qint64 s_minTimeline; - static qint64 s_maxTimeline; - static qint64 s_sbPos; - static qint64 s_zoomFactor; - static int s_paintMin; - static int s_paintDiff; - static bool s_isValid; - static int s_sectionHeight; - static qint64 s_blockSizeTime; - -public: - static qint64 s_diff; -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/Item.cpp b/Lts/Common/LtsTimelineClassic/Item.cpp deleted file mode 100755 index 8cb17e29f..000000000 --- a/Lts/Common/LtsTimelineClassic/Item.cpp +++ /dev/null @@ -1,388 +0,0 @@ - - - -#include - -#include "Item.h" -#include "EventTools.h" -#include "EventGraphic.h" - - -#ifdef LTS_DEBUG - -#endif - - -namespace LtsTimelineClassic -{ - -Item::Item(const QString &name, const QString &identifier) - : m_name(name) - , m_identifier(identifier) - , m_children(nullptr) - , m_parent(nullptr) - , m_pevcon(nullptr) - , m_eventListChanged(true) -{ - -} - - -Item::~Item() -{ - delete m_pevcon; - - if (m_children) - { - qDeleteAll(*m_children); - delete m_children; - } - - qDeleteAll(m_eventList); -} - - -PassiveEventContainer *Item::getPevcon() -{ - if (!m_pevcon) - m_pevcon = new PassiveEventContainer(m_name == QStringLiteral("Markers")); - - if (m_eventListChanged) - { - m_eventListChanged = false; - m_pevcon->setEventList(getEventGraphics()); - } - - return m_pevcon; -} - - -const QString Item::path() const -{ - if (m_parent == nullptr) - return m_name; - - return QStringLiteral("%1/%2").arg(m_parent->path(), m_name); -} - - -const QString &Item::getName() const -{ - return m_name; -} - - -Item *Item::getParent() const -{ - return m_parent; -} - - -ItemList Item::getFullItemList(bool recursive) const -{ - ItemList il; - - if (m_children == nullptr) - return il; - - il += *m_children; - - if (!recursive) - return il; - - auto end = m_children->constEnd(); - for (auto i = m_children->constBegin(); i != end; ++i) - { - il += (*i)->getFullItemList(recursive); - } - - return il; -} - - - - - - - - - - - - - - - - - -const EventGraphicList Item::getCurrentEGLFromPevcon() const -{ - if (m_pevcon == nullptr) - return EventGraphicList(); - return m_pevcon->getEventGraphicList(); -} - - -const EventGraphicList Item::getEventGraphics() const -{ - EventGraphicList egl; - EventList el = getFullEventList(true, false); - - std::sort(el.begin(), el.end(), [](const Event *a, const Event *b) - { - return a->startTime() < b->startTime(); - }); - - for (auto i1 = el.begin(); i1 != el.end(); ++i1) - { - Event *e1 = (*i1); - - if (e1->isDeleted()) - continue; - - qint64 egStart = e1->startTime(); - qint64 egEnd = egStart + EventTools::getBlockSizeTime(e1->duration()); - qint64 blockSizeTime = EventTools::getBlockSizeTime(); - bool egChanged = false; - - EventGraphic *eg = new EventGraphic(e1); - egl += eg; - - auto i2 = i1 + 1; - for (; i2 != el.end(); ++i2) - { - if (egChanged) - { - egEnd = egStart + eg->getDuration(); - - if (egEnd - egStart < blockSizeTime) - egEnd = egStart + blockSizeTime; - - egChanged = false; - } - - Event *e2 = (*i2); - - if (e2->isDeleted()) - continue; - - qint64 start2 = e2->startTime(); - - if (start2 >= egStart && egEnd >= start2) - { - eg->addEvent(e2); - egChanged = true; - } - else - { - - --i2; - break; - } - } - - if (i2 == el.end()) - break; - i1 = i2; - - } - - return egl; -} - - -EventList Item::getFullEventList(bool recursive, bool getHiddenEvents) const -{ - EventList el; - - if (getHiddenEvents) - { - el = m_eventList; - } - else - { - el.reserve(m_eventList.count()); - - for (Event *e : m_eventList) - { - if (e == nullptr || !e->isVisible()) - continue; - - el.append(e); - } - } - - if (!m_children || !recursive) - return el; - - for (const Item *item : qAsConst(*m_children)) - el += item->getFullEventList(recursive, getHiddenEvents); - - return el; -} - - -void Item::setParent(Item *parent) -{ - m_parent = parent; -} - - -void Item::addEvent(Event *event) -{ - m_eventList.append(event); - setEventListChanged(); -} - - -void Item::setEventListChanged() -{ - m_eventListChanged = true; - - if (m_parent != nullptr) - { - - m_parent->setEventListChanged(); - } -} - - -bool Item::removeEvent(int eventId) -{ - for (auto i = m_eventList.begin(); i != m_eventList.end(); ++i) - { - Event *event = *i; - - if (event->uniqueId() == eventId) - { - m_eventList.erase(i); - setEventListChanged(); - - return true; - } - } - - return false; -} - - -void Item::addItem(Item *item) -{ - if (!m_children) - m_children = new ItemList(); - - item->setParent(this); - m_children->append(item); -} - - -bool Item::hasVisibleEvents() const -{ - - if (!m_eventList.isEmpty() && - std::find_if(m_eventList.constBegin(), m_eventList.constEnd(), [](Event *e) { return e->isVisible(); }) != m_eventList.constEnd()) - return true; - - - if (m_children == nullptr) - return false; - - - - for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) - { - Item *item = *i; - - if (item->hasVisibleEvents()) - return true; - } - - - return false; -} - - -Item *Item::child(int row) const -{ - if (m_children == nullptr) - return nullptr; - - int idx = 0; - - - for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) - { - Item *item = *i; - - if (item->hasVisibleEvents()) - { - if (row == idx) - return item; - else - idx++; - } - } - - return nullptr; -} - - -int Item::childCount() const -{ - if (m_children) - return m_children->count(); - - return 0; -} - - -int Item::visibleChildCount() const -{ - if (m_children == nullptr) - return 0; - - int result = 0; - - for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) - { - Item *item = *i; - - if (item->hasVisibleEvents()) - result++; - } - - return result; -} - - -ItemList *Item::getChildren() -{ - return m_children; -} - - -int Item::indexOfParent() const -{ - if (m_parent == nullptr || m_parent->getChildren() == nullptr) - return -1; - - int idx = 0; - - ItemList *parentItemList = m_parent->getChildren(); - - - for (auto i = parentItemList->constBegin(); i != parentItemList->constEnd(); ++i) - { - Item *item = *i; - - if (item == this) - return idx; - - idx++; - } - - Q_ASSERT(false); - return -1; -} - -} diff --git a/Lts/Common/LtsTimelineClassic/Item.h b/Lts/Common/LtsTimelineClassic/Item.h deleted file mode 100755 index 346351095..000000000 --- a/Lts/Common/LtsTimelineClassic/Item.h +++ /dev/null @@ -1,128 +0,0 @@ - - - -#pragma once - -#include -#include "EventList.h" -#include "PassiveEventContainer.h" - -namespace LtsTimelineClassic -{ - -class Item; -using ItemList = QList; - - - -class LTSTIMELINECLASSIC_EXPORT Item -{ - - -public: - - Item(const Item &) = delete; - Item &operator=(const Item &) = delete; - const EventGraphicList getEventGraphics() const; - - - - explicit Item(const QString &name, const QString &identifier = QString()); - - - - ~Item(); - - - - void addItem(Item *item); - - bool removeEvent(int eventId); - - void setEventListChanged(); - - const QString path() const; - - - - - - - - PassiveEventContainer *getPevcon(); - - const EventGraphicList getCurrentEGLFromPevcon() const; - - - - - - - void setParent(Item *parent); - - - - const QString &getName() const; - - void setName(const QString &name) { m_name = name; } - - const QString &getIdentifier() const { return m_identifier; } - - - - ItemList *getChildren(); - - - - - - - - Item *getParent() const; - - - - void addEvent(Event *event); - - - - EventList getFullEventList(bool recursive = true, bool getHiddenEvents = false) const; - - - - ItemList getFullItemList(bool recursive = true) const; - - - - Item *child(int row) const; - - - - int indexOfParent() const; - - - - - - - int childCount() const; - - int visibleChildCount() const; - - bool hasVisibleEvents() const; - - - -private: - QString m_name; - QString m_identifier; - ItemList *m_children; - Item *m_parent; - PassiveEventContainer *m_pevcon; - EventList m_eventList; - - - bool m_eventListChanged; -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/LineMap.h b/Lts/Common/LtsTimelineClassic/LineMap.h deleted file mode 100755 index c97b47cd4..000000000 --- a/Lts/Common/LtsTimelineClassic/LineMap.h +++ /dev/null @@ -1,13 +0,0 @@ - - - -#pragma once - -#include - -namespace LtsTimelineClassic -{ - -using LineMap = QMap; - -} diff --git a/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp b/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp deleted file mode 100755 index 428e0033c..000000000 --- a/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp +++ /dev/null @@ -1,133 +0,0 @@ - - - -#include "PassiveEventContainer.h" -#include "EventGraphic.h" -#include "EventTools.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - - -namespace LtsTimelineClassic -{ - -PassiveEventContainer::PassiveEventContainer(bool ignoreKnownRanges, QObject *parent) - : QObject(parent) - , m_ignoreKnownRanges(ignoreKnownRanges) -{ - -} - - -PassiveEventContainer::~PassiveEventContainer() -{ - qDeleteAll(m_eventGraphicList); -} - - -EventGraphicList PassiveEventContainer::getEventGraphicList() const -{ - return m_eventGraphicList; -} - - -void PassiveEventContainer::setEventList(const EventGraphicList &eventGraphicList) -{ - qDeleteAll(m_eventGraphicList); - - m_eventGraphicList = eventGraphicList; -} - - -void PassiveEventContainer::paint(QPainter *painter, const QStyleOptionViewItem &option, const LineMap &lineMap, - const QVector &knownRanges) const -{ - EventTools::s_test++; - - bool alternate = option.features.testFlag(QStyleOptionViewItem::ViewItemFeature::Alternate); - const QColor bg = QColor(alternate ? QRgb(0xF6D8CE) : QRgb(0xFBF2EF)); - - QColor cachedBg(Qt::white); - if (alternate) - cachedBg = cachedBg.darker(110); - - - painter->fillRect(option.rect, QBrush(bg)); - - int posX1 = option.rect.x(); - int posX2 = option.rect.right(); - int posY1 = option.rect.y(); - int posY2 = option.rect.bottom(); - int h = posY2 - posY1 + 1; - - painter->save(); - painter->setClipping(true); - painter->setClipRect(option.rect.adjusted(0, 0, 1, 1)); - - int eventWidth = EventTools::eventWidth(); - - - if (m_ignoreKnownRanges) - { - painter->fillRect(QRect(posX1, posY1, posX2 - posX1, h), cachedBg); - } - else - { - for (const auto &range : knownRanges) - { - int rangeStart = EventTools::getEventPosition(range.start); - int rangeEnd = EventTools::getEventPosition(range.end); - - - if (rangeEnd >= posX1 && rangeStart <= posX2) - painter->fillRect(QRect(rangeStart, posY1, rangeEnd - rangeStart + eventWidth + 1, h), cachedBg); - } - } - - - - - - - - - - - painter->restore(); - - - - - - for (auto i = lineMap.constBegin(); i != lineMap.constEnd(); ++i) - { - int posX = i.key(); - - painter->setPen(QPen(QBrush(QGuiApplication::palette().shadow()), 1.0)); - - if (posX > option.rect.x() && - posX < (option.rect.x() + option.rect.width())) - painter->drawLine(posX, posY1, posX, posY2); - } - - - - - - - - - EventTools::drawEvents(painter, m_eventGraphicList, option.rect.y()); - -} - -} diff --git a/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h b/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h deleted file mode 100755 index 46dcf1fb8..000000000 --- a/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h +++ /dev/null @@ -1,53 +0,0 @@ - - - -#pragma once - -#include "EventList.h" -#include "LineMap.h" -#include "Range.h" - -#include - - -class QPainter; -class QStyleOptionViewItem; - - -namespace LtsTimelineClassic -{ - - - -class PassiveEventContainer: public QObject -{ - Q_OBJECT - -public: - - - explicit PassiveEventContainer(bool ignoreKnownRanges, QObject *parent = nullptr); - - ~PassiveEventContainer(); - - - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const LineMap &lineMap, const QVector &knownRanges) const; - - - - EventGraphicList getEventGraphicList() const; - - - - void setEventList(const EventGraphicList &eventGraphicList); - -private: - EventGraphicList m_eventGraphicList; - bool m_ignoreKnownRanges; - -}; - -} - -Q_DECLARE_METATYPE(LtsTimelineClassic::PassiveEventContainer *) diff --git a/Lts/Common/LtsTimelineClassic/Range.h b/Lts/Common/LtsTimelineClassic/Range.h deleted file mode 100755 index 8dab924a9..000000000 --- a/Lts/Common/LtsTimelineClassic/Range.h +++ /dev/null @@ -1,18 +0,0 @@ - - - -#pragma once - -#include - -namespace LtsTimelineClassic -{ -struct Range -{ - qint64 start = 0; - qint64 end = 0; - Range() = default; - Range(qint64 s, qint64 e) : start(s), end(e) {} -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp b/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp deleted file mode 100755 index 50069ed82..000000000 --- a/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp +++ /dev/null @@ -1,40 +0,0 @@ - - - - -#include "TestTimelineModel.h" - -#include "EventGraphic.h" - -#include -#include - - -namespace LtsTimelineClassic -{ - -TestTimelineModel::TestTimelineModel() - : AbstractTimelineModel() -{ - -} - -void TestTimelineModel::addTestEvent(Event *event) -{ - AbstractTimelineModel::addEvent(event); -} - -void TestTimelineModel::slotEventAction(EventGraphic *eg, int actionType) -{ - (void)actionType; - - QString content = tr("Event count: %1").arg(QString::number(eg->eventList().count())); - - for (int i = 0; i < eg->eventList().count(); i++) - { - content += QLatin1String("\r\nTime: "); - content += QString::number(eg->eventList().at(i)->startTime()); - } -} - -} diff --git a/Lts/Common/LtsTimelineClassic/TestTimelineModel.h b/Lts/Common/LtsTimelineClassic/TestTimelineModel.h deleted file mode 100755 index 98ad77c24..000000000 --- a/Lts/Common/LtsTimelineClassic/TestTimelineModel.h +++ /dev/null @@ -1,25 +0,0 @@ - - - -#pragma once - -#include "AbstractTimelineModel.h" - - -namespace LtsTimelineClassic -{ - -class LTSTIMELINECLASSIC_EXPORT TestTimelineModel : public AbstractTimelineModel -{ - Q_OBJECT - -public: - TestTimelineModel(); - void addTestEvent(Event *event); - - -private slots: - void slotEventAction(EventGraphic *, int); -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp b/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp deleted file mode 100755 index 5cb11e167..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp +++ /dev/null @@ -1,149 +0,0 @@ - - - -#include "TimelineWidget.h" -#include "TimelineHeaderView.h" - -#include -#include -#include -#include -#include -#include - - -namespace LtsTimelineClassic -{ - -TimelineHeaderView::TimelineHeaderView(QWidget *parent) - : QHeaderView(Qt::Horizontal, parent) - , m_posMarkTime(0) - , m_paintingRange(rect()) - , m_liveTime(0) -{ - setSectionsClickable(true); - - setStretchLastSection(true); - - connect(this, &TimelineHeaderView::sectionResized, - this, &TimelineHeaderView::slotSectionResized); - -} - -#define GET_LOGGER NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.HeaderView") - -void TimelineHeaderView::setPosMarkTime(qint64 time) -{ - m_posMarkTime = time; - refreshData(); -} - -void TimelineHeaderView::setLiveTime(qint64 time) -{ - m_liveTime = time; - refreshData(); -} - -void TimelineHeaderView::setPosMarkPos() -{ - - - if (m_posMarkTime > 0) - { - int posMarkPos = EventTools::getEventPosition(m_posMarkTime); - emit signalSetPosMarkPos(posMarkPos); - refreshData(); - } -} - -void TimelineHeaderView::refreshData() -{ - if (EventTools::getTimelineMin() > 0 && EventTools::getTimelineMax() > 1) - { - updateSection(1); - updateSection(0); - } -} - -void TimelineHeaderView::slotSectionResized(int logicalIndex, int oldSize, int newSize) -{ - (void)oldSize; - - if (logicalIndex == 1) - m_paintingRange = QRect(0, 0, newSize, height()); - - refreshData(); -} - -void TimelineHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const -{ - painter->save(); - QHeaderView::paintSection(painter, rect, logicalIndex); - painter->restore(); - - - if (logicalIndex == 1) - { - int h = (rect.height() / 3); - int y = rect.height() - h; - int w = h; - - - - painter->save(); - painter->setClipping(true); - - if (m_posMarkTime != 0) - { - double posMark = EventTools::getEventPosition(m_posMarkTime); - int x = + posMark; - - if (x >= sectionPosition(1)) - { - painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Inactive, QPalette::Highlight))); - painter->setBrush(QGuiApplication::palette().highlight()); - - QPolygon pol; - pol.append(QPoint(x + (-w / 2), y + 0)); - pol.append(QPoint(x + 0, y + h)); - pol.append(QPoint(x + (w / 2), y + 0)); - pol.append(QPoint(x + (-w / 2), y + 0)); - - QPainterPath path; - path.moveTo(x, y); - path.addPolygon(pol); - - painter->drawPath(path); - } - } - - - if (m_liveTime != 0) - { - int posLive = EventTools::getEventPosition(m_liveTime); - int x = posLive; - - if (x >= sectionPosition(1)) - { - painter->setPen(QPen(QColor("green"))); - painter->setBrush(QBrush(QColor("green"))); - - QPolygon pol; - pol.append(QPoint(x + (-w / 2), y + 0)); - pol.append(QPoint(x + 0, y + h)); - pol.append(QPoint(x + (w / 2), y + 0)); - pol.append(QPoint(x + (-w / 2), y + 0)); - - QPainterPath path; - path.moveTo(x, y); - path.addPolygon(pol); - - painter->drawPath(path); - } - } - - painter->restore(); - } -} - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h b/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h deleted file mode 100755 index 35267a358..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h +++ /dev/null @@ -1,63 +0,0 @@ - - - -#pragma once - -#include "EventTools.h" - -#include - - -class QPainter; - -namespace LtsTimelineClassic -{ - - - -class TimelineHeaderView : public QHeaderView -{ - Q_OBJECT - -public: - explicit TimelineHeaderView(QWidget *parent = nullptr); - - - - void refreshData(); - void setLiveTime(qint64 time); - -private: - qint64 m_posMarkTime; - QRect m_paintingRange; - - qint64 m_liveTime; - -protected: - - - void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; - -signals: - - - void signalSetPosMarkPos(int pos); - -public slots: - - - - void slotSectionResized(int logicalIndex, int oldSize, int newSize); - - - - void setPosMarkTime(qint64 time); - -private: - - - - void setPosMarkPos(); -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp b/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp deleted file mode 100755 index 4247b90e9..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp +++ /dev/null @@ -1,519 +0,0 @@ - - - -#include "TimelineWidget.h" -#include "TimelineIndicator.h" -#include "EventTools.h" - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - - -#ifdef LTS_DEBUG - -#endif -#ifdef VERBOSE - #define dbg_print(x) qWarning() << #x << x -#else - #define dbg_print(x) (void)x; -#endif - -namespace LtsTimelineClassic -{ - -TimelineIndicator::TimelineIndicator(QWidget *parent) - : QWidget(parent) - , m_zoomFactorPoints(0) - , m_pbZoom(new QPushButton(this)) - , m_pbUnzoom(new QPushButton(this)) - , m_cbZoomLevel(new QComboBox(this)) - , m_currentTextHeight(0) - , m_currentTextWidth(0) - , m_sectionPos(0) - , m_sectionSize(0) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.Indicator")) -{ - m_timeArray.reserve(11 + 11 + 7 + 10); - - m_timeArray << - - 1 << 2 << 3 << 4 << 5 << 6 << 12 << 15 << 20 << 30 << 60 << - - - 1 * 60 << 2 * 60 << 3 * 60 << 4 * 60 << 5 * 60 << 6 * 60 << 12 * 60 << 15 * 60 << 20 * 60 << 30 * 60 << 60 * 60 << - - - 1 * 60 * 60 << 2 * 60 * 60 << 4 * 60 * 60 << 6 * 60 * 60 << 8 * 60 * 60 << 12 * 60 * 60 << 24 * 60 * 60 << - - - 1 * 60 * 60 << 2 * 60 * 60 << 4 * 60 * 60 << 6 * 60 * 60 << 8 * 60 * 60 << 10 * 60 * 60 << 14 * 60 * 60 << 18 * 60 * 60 << 22 * 60 * 60 << 26 * 60 * 60 << 30 * 60 * 60; - - QHBoxLayout *centralLayout = new QHBoxLayout(); - centralLayout->setSpacing(0); - - setLayout(centralLayout); - - m_pbZoom->setText(QLatin1String("+")); - m_pbZoom->setFixedSize(s_pbSize, s_pbSize); - - m_pbUnzoom->setText(QLatin1String("-")); - m_pbUnzoom->setFixedSize(s_pbSize, s_pbSize); - - - m_cbZoomLevel->addItem(tr("10 min"), 600); - m_cbZoomLevel->addItem(tr("30 min"), 1800); - m_cbZoomLevel->addItem(tr("1 h"), 3600); - m_cbZoomLevel->addItem(tr("2 h"), 7200); - m_cbZoomLevel->addItem(tr("4 h"), 14400); - m_cbZoomLevel->addItem(tr("8 h"), 28800); - - m_cbZoomLevel->setCurrentIndex(0); - - m_cbZoomLevel->setFixedHeight(s_pbSize); - m_zoomFactorPoints = m_cbZoomLevel->currentData().toLongLong(); - - connect(m_cbZoomLevel, &QComboBox::currentTextChanged, - this, &TimelineIndicator::afterZoom); - connect(m_pbZoom, &QPushButton::clicked, - this, &TimelineIndicator::slotZoom); - connect(m_pbUnzoom, &QPushButton::clicked, - this, &TimelineIndicator::slotUnzoom); - connect(this, &TimelineIndicator::signalAddLine, - this, &TimelineIndicator::slotAddLine); - connect(this, &TimelineIndicator::signalResetLineList, - this, &TimelineIndicator::slotResetLineList); - - centralLayout->addWidget(m_pbZoom, 0, Qt::AlignLeft | Qt::AlignTop); - centralLayout->addWidget(m_pbUnzoom, 0, Qt::AlignLeft | Qt::AlignTop); - centralLayout->addWidget(m_cbZoomLevel, 1, Qt::AlignLeft | Qt::AlignTop); - - - - - - QFontMetrics fm(QApplication::font()); - m_currentTextHeight = fm.height(); - -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - m_currentTextWidth = fm.width(DTG::toString(QDateTime::currentDateTimeUtc(), DTG::Short)); -#else - m_currentTextWidth = fm.horizontalAdvance(DTG::toString(QDateTime::currentDateTimeUtc(), DTG::Short)); -#endif - - dbg_print(m_currentTextWidth); - - -} - -TimelineIndicator::~TimelineIndicator() -{ - -} - -void TimelineIndicator::afterZoom() -{ - m_zoomFactorPoints = m_cbZoomLevel->currentData().toLongLong() * 1000; - emit signalSetZoomFactor(m_zoomFactorPoints); - update(); -} - - -void TimelineIndicator::slotZoom() -{ - int pos = m_cbZoomLevel->currentIndex(); - - if (pos == 0) - return; - - m_cbZoomLevel->setCurrentIndex(pos - 1); - emit signalZoomChanged(); -} - -void TimelineIndicator::slotUnzoom() -{ - int count = m_cbZoomLevel->count(); - int pos = m_cbZoomLevel->currentIndex(); - - if (pos >= count - 1) - return; - - m_cbZoomLevel->setCurrentIndex(pos + 1); - emit signalZoomChanged(); -} - -enum highestUnit -{ - HU_INVALID, - - HU_DAYS, - HU_HOURS, - HU_MINUTES, - HU_SECONDS -}; - -void TimelineIndicator::refreshData() -{ -#ifdef VERBOSE - qWarning() << ""; -#endif - - - - emit signalResetLineList(); - -#ifdef VERBOSE - qWarning() << "section x" << m_sectionPos << "length" << m_sectionSize; -#endif - - - - - - - - double abstand = m_sectionPos + m_currentTextWidth + s_TimeTextDistanceTolerancePixels; - dbg_print(abstand); - - - - qint64 eventTimeOfAbstand = EventTools::getEventTime(abstand); - dbg_print(eventTimeOfAbstand); - qint64 adjustedMin = EventTools::getAdjustedMin(); - dbg_print(adjustedMin); - qint64 textWidthTime = (eventTimeOfAbstand - adjustedMin); - - if (textWidthTime < 0) - { - qWarning() << "TimelineIndicator: paintEvent: textWidthTime invalid! (" << textWidthTime << ")"; - return; - } - - qint64 eventTime = EventTools::getEventTime(abstand); - dbg_print(eventTime); - - - QDateTime textWidthDt = QDateTime::fromMSecsSinceEpoch(textWidthTime); - - - - int days = textWidthDt.date().day() - 1; - int hours = textWidthDt.time().hour() - 1; - int minutes = textWidthDt.time().minute(); - int seconds = textWidthDt.time().second() - 1; - - highestUnit unit = HU_INVALID; - - if (days == 0) - if (hours == 0) - if (minutes == 0) - if (seconds == 0) - { - #ifdef VERBOSE - qWarning() << "refreshData: no lower time unit available" ; - #endif - return; - } - else - { - - unit = HU_SECONDS; - } - else - { - - unit = HU_MINUTES; - } - else - { - - unit = HU_HOURS; - } - else - { - - unit = HU_DAYS; - } - - if (unit == HU_INVALID) - { -#ifdef VERBOSE - qWarning() << "unit == HU_INVALID"; -#endif - return; - } - - int higherValue = 0; - for (int i = 0; i < m_timeArray.size(); i++) - { - qint64 time = (m_timeArray[i] * 1000); - - if (time >= textWidthTime) - { - higherValue = time; - - break; - } - else - { - continue; - } - - - -#ifdef VERBOSE - qWarning() << "refreshData: no fitting highest unit found !" ; -#endif - return; - } - - - if (higherValue < 1) - { - - return; - } - - - - - - QDateTime minDt = QDateTime::fromMSecsSinceEpoch(adjustedMin); - dbg_print(minDt); - dbg_print(unit); - - switch (unit) - { - case HU_SECONDS: - { - minDt = minDt.addSecs(-(minDt.time().second())); -#ifdef VERBOSE - qCritical() << "HU_SECONDS"; -#endif - - break; - } - - case HU_MINUTES: - { - minDt = minDt.addSecs(-(minDt.time().second())); - minDt = minDt.addSecs(-(minDt.time().minute() * 60)); -#ifdef VERBOSE - qCritical() << "HU_MINUTES"; -#endif - - break; - } - - case HU_HOURS: - { - minDt = minDt.addSecs(-(minDt.time().second())); - minDt = minDt.addSecs(-(minDt.time().minute() * 60)); - minDt = minDt.addSecs(-(minDt.time().hour() * 60 * 60)); -#ifdef VERBOSE - qCritical() << "HU_HOURS"; -#endif - - break; - } - - case HU_DAYS: - { - minDt = minDt.addSecs(-(minDt.time().second())); - minDt = minDt.addSecs(-(minDt.time().minute() * 60)); - minDt = minDt.addSecs(-(minDt.time().hour() * 60 * 60)); - minDt = minDt.addDays(-(minDt.date().day())); -#ifdef VERBOSE - qCritical() << "HU_DAYS"; -#endif - - break; - } - - default: - { -#ifdef VERBOSE - qWarning() << "unit / scaling is off da charz !"; -#endif - return; - } - } - - dbg_print(minDt); - - - minDt = minDt.addMSecs(-higherValue); - - dbg_print(minDt); - - - qint64 maxTimelineView = EventTools::getAdjustedMax() + (higherValue ); - qint64 additional = textWidthTime / 2; - maxTimelineView += additional; - - qint64 minTimeline = minDt.toMSecsSinceEpoch(); - - dbg_print(minTimeline); - dbg_print(maxTimelineView); - dbg_print(higherValue); - - qint64 steps = (maxTimelineView - minTimeline) / higherValue; - if (steps < 0) - steps = steps * (-1); - - if (steps > 60000 || steps < 1) - { - ERROR_LOG(m_logger) << "More than 60000 ("< 60000) - break; - - currentStep++; - - int pos = EventTools::getEventPosition(indicatorTime); - dbg_print(indicatorTime); - - if (pos + (m_currentTextWidth / 4) + 1 < m_sectionPos) - { -#ifdef VERBOSE - qCritical() << "Indicator: pos" << pos << "invisible #1"; -#endif - continue; - } - else - if (pos - (m_currentTextWidth ) > (m_sectionPos + m_sectionSize)) - { -#ifdef VERBOSE - qCritical() << "Indicator: pos" << pos << "invisible #2"; -#endif - continue; - } - else - { -#ifdef VERBOSE - qCritical() << "Indicator: OKAY!! pos" << pos << "visible"; -#endif - } - - - - emit signalAddLine(pos, indicatorTime); - } - - dbg_print(currentStep); - - QFontMetrics fm(QApplication::font()); - m_currentTextHeight = fm.height(); - - update(); -} - -void TimelineIndicator::paintEvent(QPaintEvent *) -{ - - - QPainter painter(this); - painter.save(); - painter.setClipping(true); - painter.setClipRect(QRect(x() - 8 + m_sectionPos, 0, m_sectionSize, height())); - -#ifdef VERBOSE - int textY = (height() - m_currentTextHeight - 5); -#endif - - int h = height(); - - for (auto i = m_lineMap.constBegin(); i != m_lineMap.constEnd(); ++i) - { - - - int pos = i.key(); - - - painter.setPen(QPen(QColor("black"), 1.0)); - painter.drawLine(pos, m_currentTextHeight, pos, h); - - int centeredPos = pos - (m_currentTextWidth / 2); - qint64 time = (*i); - - - - - painter.drawText(centeredPos, (m_currentTextHeight / 2) + 4, - DTG::toStringMSec(time, DTG::Short)); - -#ifdef VERBOSE - WARNING_LOG(m_logger) << "paintEvent: actual drawing position: x=" << centeredPos << " y=" << textY; -#endif - } - - painter.restore(); - - -} - -void TimelineIndicator::slotSectionResized(int logicalIndex, int oldSize, int newSize) -{ - (void)oldSize; - - - QHeaderView *header = qobject_cast(QObject::sender()); - - if (!header) - return; - - if (logicalIndex == 1) - { - m_sectionPos = header->sectionPosition(1); - m_sectionSize = newSize; - - } - - update(); -} - -void TimelineIndicator::slotAddLine(double posX, qint64 time) -{ -#ifdef VERBOSE - qCritical() << "slotAddLine: posX" << posX << "time" << time; -#endif - m_lineMap.insert(posX, time); -} - -void TimelineIndicator::slotResetLineList() -{ - m_lineMap.clear(); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineIndicator.h b/Lts/Common/LtsTimelineClassic/TimelineIndicator.h deleted file mode 100755 index 710e1d3f1..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineIndicator.h +++ /dev/null @@ -1,111 +0,0 @@ - - - -#pragma once - -#include "EventTools.h" -#include "LineMap.h" -#include -#include -#include - - -namespace NGL -{ -class INGLLogger; - -} -class QPushButton; -class QLabel; -class QComboBox; - - -namespace LtsTimelineClassic -{ - - - -class TimelineIndicator : public QWidget -{ - Q_OBJECT - - - static const int s_TimeTextDistanceTolerancePixels = 10; - static const int s_pbSize = 18; - -public: - explicit TimelineIndicator(QWidget *parent); - ~TimelineIndicator(); - qint64 zoomFactorPoints() const { return m_zoomFactorPoints * 1000; } - - - - void refreshData(); - -private: - QVector m_timeArray; - qint64 m_zoomFactorPoints; - QPushButton *m_pbZoom; - QPushButton *m_pbUnzoom; - QComboBox *m_cbZoomLevel; - int m_currentTextHeight; - int m_currentTextWidth; - LineMap m_lineMap; - int m_sectionPos; - int m_sectionSize; - NGL::INGLLogger &m_logger; - -protected: - - - void paintEvent(QPaintEvent *); - -signals: - - - void signalSetTimelineRange(qint64 min, qint64 max); - - - - void signalSetZoomFactor(qint64 zoomFactor); - - - - void signalResetLineList(); - - - - void signalAddLine(int posX, qint64 time); - - void signalZoomChanged(); - - -private slots: - void afterZoom(); - - -public slots: - - - - void slotSectionResized(int logicalIndex, int oldSize, int newSize); - - - - void slotZoom(); - - - - void slotUnzoom(); - - - - void slotResetLineList(); - - - - void slotAddLine(double posX, qint64 time); - -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineView.cpp b/Lts/Common/LtsTimelineClassic/TimelineView.cpp deleted file mode 100755 index 4e3bae7c6..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineView.cpp +++ /dev/null @@ -1,392 +0,0 @@ - - - -#include "TimelineView.h" -#include "TimelineHeaderView.h" -#include "TimelineWidget.h" -#include "EventDelegate.h" -#include "EventGraphic.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsTimelineClassic -{ - - -TimelineView::TimelineView(QWidget *parent) - : QTreeView(parent) - - , m_model(nullptr) - , m_drawCache(false) -{ - m_posMarkTime = 0; - - - setAlternatingRowColors(true); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setSelectionBehavior(QAbstractItemView::SelectItems); - setSelectionMode(QAbstractItemView::NoSelection); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); -} - - -void TimelineView::setHeader(QHeaderView *header) -{ - if (header) - { - QTreeView::setHeader(header); - header->setMinimumSectionSize(150); - } - else - { - - } -} - - -AbstractTimelineModel *TimelineView::timelineModel() -{ - return m_model; -} - - -void TimelineView::setTimelineModel(AbstractTimelineModel *model) -{ - m_model = model; - setMouseTracking(true); - setModel(m_model); - - EventDelegate *ed = new EventDelegate(this); - - EventTools::setSectionHeight(ed->sizeHint().height()); - - setItemDelegate(ed); -} - - -EventGraphic *TimelineView::getEventOnPosition(QPoint position) -{ - position = viewport()->mapFromGlobal(position); - QModelIndex index = indexAt(position); - - if (index.isValid() && position.x() > header()->sectionSize(0)) - { - Item *item = static_cast(index.internalPointer()); - - if (!item) - return nullptr; - - qint64 curTime = EventTools::getEventTime(position.x()); - EventGraphicList egl = item->getCurrentEGLFromPevcon(); - - qint64 blockSizeTime = EventTools::getBlockSizeTime(); - - for (int i = 0; i < egl.count(); i++) - { - EventGraphic *eg = egl.at(i); - - if (!eg) - continue; - - qint64 eventStartTime = eg->lowestTime(); - - if (eventStartTime == 0) - continue; - - qint64 eventEndTime = eventStartTime; - - qint64 eglDuration = eg->getDuration(); - - if (eglDuration > blockSizeTime) - eventEndTime += eglDuration; - else - eventEndTime += blockSizeTime; - - if (curTime >= eventStartTime && curTime <= eventEndTime) - return eg; - } - } - - return nullptr; -} - - -bool TimelineView::event(QEvent *event) -{ - switch (event->type()) - { - case QEvent::ToolTip: - { - - - QHelpEvent *helpEvent = static_cast(event); - - if (!helpEvent) - return true; - - EventGraphic *eg = getEventOnPosition(helpEvent->globalPos()); - - if (!eg) - return true; - - emit signalEventAction(eg, EventGraphic::ACTION_TOOLTIP); - - return true; - } - - default: - { - break; - } - } - - return QTreeView::event(event); -} - - -void TimelineView::wheelEvent(QWheelEvent *event) -{ - QTreeView::wheelEvent(event); - - if (QGuiApplication::queryKeyboardModifiers() != Qt::ControlModifier) - return; - - int scrolls = event->delta() / 120; - - if (scrolls > 0) - { - for (int i = 0; i < scrolls; i++) - emit signalZoom(); - } - - if (scrolls < 0) - { - for (int i = 0; i > scrolls; i--) - emit signalUnzoom(); - } -} - - -void TimelineView::mouseReleaseEvent(QMouseEvent *event) -{ - QTreeView::mouseReleaseEvent(event); - - QPoint position = event->globalPos(); - EventGraphic *eg = getEventOnPosition(position); - - if (!eg) - return; - - switch (event->button()) - { - case Qt::LeftButton: - { - emit signalEventAction(eg, EventGraphic::ACTION_LEFT_CLICKED); - break; - } - - case Qt::RightButton: - { - emit signalEventAction(eg, EventGraphic::ACTION_RIGHT_CLICKED); - break; - } - - default: - { - break; - } - } -} - - -void TimelineView::slotAddLine(int posX, qint64 time) -{ - - m_lineMap.insert(posX, time); -} - - -void TimelineView::slotResetLineList() -{ - m_lineMap.clear(); -} - - - - - - -void TimelineView::slotSetPosMarkTime(qint64 posMarkTime) -{ - m_posMarkTime = posMarkTime; - - TimelineHeaderView *headerView = qobject_cast(header()); - if (headerView) - headerView->setPosMarkTime(posMarkTime); - - refreshData(); -} - - -void TimelineView::refreshData() -{ - update(); - viewport()->update(); -} - - -void TimelineView::paintEvent(QPaintEvent *event) -{ - if (!m_model) - return; - - QWidget *viewp0rt = viewport(); - - - - QPainter painter(viewp0rt); - - if (!painter.isActive()) - { - - } - - painter.save(); - - int viewPortHeight = viewp0rt->height(); - - QHeaderView *head0r = header(); - - - - int sectionPos = head0r->sectionPosition(1); - int sectionSize = head0r->sectionSize(1); - - int minSection = sectionPos; - int maxSection = minSection + sectionSize; - - - - - QRect paintRect(minSection, 0, - maxSection - minSection, height()); - painter.setClipping(true); - painter.setClipRect(paintRect); - - - - - - - - - - for (auto i = m_lineMap.constBegin(); i != m_lineMap.constEnd(); ++i) - { - int pos = i.key(); - - painter.setPen(QPen(QBrush(QGuiApplication::palette().shadow()), 1.0)); - - if (pos > minSection && - pos < maxSection) - painter.drawLine(pos, 0, pos, viewPortHeight); - else - { -#ifdef VERBOSE - qCritical() << "out of view => pos" << pos << "min" << minSection << "max" << maxSection; -#endif - } - } - - - - painter.restore(); - QTreeView::paintEvent(event); - painter.save(); - - - - - - - - - - if (m_drawCache) - { - - painter.setPen(QPen(QBrush("gray"), 1)); - painter.setBrush(QBrush("gray")); - painter.drawRect(minSection, 0, maxSection, height()); - - - - painter.setPen(QPen(QBrush("white"), 1)); - painter.setBrush(QBrush("white")); - int h = height(); - - for (const auto &range : qAsConst(m_knownRanges)) - { - int start = EventTools::getEventPosition(range.start); - - if (start < minSection) - start = minSection; - - int end = EventTools::getEventPosition(range.end); - - if (end > maxSection) - end = maxSection; - - painter.drawRect(start, 0, end - start, h); - } - } - - - - - - - - - - - - ItemList il = m_model->getRootItem()->getFullItemList(true); - for (int i = 0; i < il.count(); i++) - { - if (il.at(i)->getName() == tr("Markers")) - EventTools::drawMarkLines(&painter, il.at(i)->getFullEventList(true), height()); - } - - - - - if (m_posMarkTime > 0) - { - int posMarkPosition = EventTools::getEventPosition(m_posMarkTime); - - if (posMarkPosition > sectionPos && - posMarkPosition < sectionSize + sectionPos) - { - painter.setPen(QPen(QGuiApplication::palette().highlight(), 1.0)); - int xpos = posMarkPosition; - painter.drawLine(xpos, 0, xpos, height()); - } - } - - painter.restore(); - - -} - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineView.h b/Lts/Common/LtsTimelineClassic/TimelineView.h deleted file mode 100755 index caae12374..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineView.h +++ /dev/null @@ -1,107 +0,0 @@ - - - -#pragma once - -#include "AbstractTimelineModel.h" -#include "TimelineIndicator.h" -#include "TimelineHeaderView.h" -#include "LineMap.h" -#include "Range.h" - -#include - - -namespace LtsTimelineClassic -{ - - -class TimelineView : public QTreeView -{ - Q_OBJECT - -public: - - - explicit TimelineView(QWidget *parent); - - EventGraphic *getEventOnPosition(QPoint position); - - - - void setTimelineModel(AbstractTimelineModel *model); - - - - AbstractTimelineModel *timelineModel(); - - - - void refreshData(); - - - - void setHeader(QHeaderView *header); - - - void setKnownRanges(const QVector &newRanges) - { - m_knownRanges = newRanges; - } - - void setDrawCache(bool show) - { - m_drawCache = show; - } - - -private: - qint64 m_posMarkTime; - AbstractTimelineModel *m_model; - LineMap m_lineMap; - QVector m_knownRanges; - bool m_drawCache; - - -protected: - bool event(QEvent *event) override; - void mouseReleaseEvent(QMouseEvent *) override; - void wheelEvent(QWheelEvent *event) override; - - - - - - - - - virtual void paintEvent(QPaintEvent *event) override; - - -public slots: - - - void slotResetLineList(); - - - - void slotAddLine(int posX, qint64 time); - - - - - - - - void slotSetPosMarkTime(qint64 posMarkTime); - - -signals: - - void signalEventAction(EventGraphic *eg, int); - void signalZoom(); - void signalUnzoom(); - -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp b/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp deleted file mode 100755 index be7d69e34..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp +++ /dev/null @@ -1,324 +0,0 @@ - - - -#include "TimelineWidget.h" -#include "EventDelegate.h" -#include "ui_TimelineWidgetUi.h" -#include "AbstractTimelineModel.h" -#include "EventGraphic.h" - -#include - -#include -#include -#include -#include - -#include - - -namespace LtsTimelineClassic -{ - -TimelineWidget::TimelineWidget(QWidget *parent) - : QWidget(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.Widget")) - , m_timelineUi(new Ui::Timeline) - , m_min(0) - , m_max(1) - , m_sbPos(0) - , m_zoomFactor(1) - , m_headerView(new TimelineHeaderView) - , m_model(nullptr) -{ - m_timelineUi->setupUi(this); - m_timelineUi->view->setHeader(m_headerView); - m_headerView->setMinimumSectionSize(150); - - m_timelineUi->scrollbar->setMouseTracking(true); - connect(m_timelineUi->scrollbar, &QScrollBar::sliderReleased, this, &TimelineWidget::scrollBarFilter2); - connect(m_timelineUi->scrollbar, &QScrollBar::actionTriggered, this, &TimelineWidget::scrollBarFilter1); - - connect(m_headerView, &QHeaderView::sectionResized, m_timelineUi->indicator, &TimelineIndicator::slotSectionResized); - connect(m_headerView, &QHeaderView::sectionResized, this, &TimelineWidget::slotSectionResized); - connect(m_timelineUi->view, &TimelineView::signalZoom, m_timelineUi->indicator, &TimelineIndicator::slotZoom); - connect(m_timelineUi->view, &TimelineView::signalUnzoom, m_timelineUi->indicator, &TimelineIndicator::slotUnzoom); - - connect(m_timelineUi->view, &TimelineView::signalZoom, this, &TimelineWidget::signalZoomChanged); - connect(m_timelineUi->view, &TimelineView::signalUnzoom, this, &TimelineWidget::signalZoomChanged); - - connect(m_timelineUi->indicator, &TimelineIndicator::signalZoomChanged, this, &TimelineWidget::signalZoomChanged); - connect(m_timelineUi->indicator, &TimelineIndicator::signalSetZoomFactor, this, &TimelineWidget::slotSetZoomFactor); - - connect(m_timelineUi->view, &TimelineView::signalEventAction, - this, &TimelineWidget::signalEventAction); - - connect(m_timelineUi->cbDrawCache, &QCheckBox::toggled, - this, [this](bool toggled) - { - m_timelineUi->view->setDrawCache(toggled); - update(); - }); - - connect(m_timelineUi->cbArm, &QCheckBox::toggled, - this, &TimelineWidget::signalArm); - connect(m_timelineUi->updateTimeline, &QPushButton::clicked, - this, &TimelineWidget::slotUpdateTimeline); -} - - -TimelineWidget::~TimelineWidget() -{ - delete m_timelineUi; -} - - -void TimelineWidget::setLiveTime(qint64 time) -{ - m_headerView->setLiveTime(time); - - EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); - if (ed == nullptr) - { - qWarning() << "TimelineWidget::setLiveTime: ed == nullptr"; - return; - } - - ed->setLiveTime(time); -} - - -void TimelineWidget::hideDebugWidget() -{ - m_timelineUi->debugWidget->hide(); - m_timelineUi->lbMinMax->hide(); -} - -void TimelineWidget::knownRangesChanged(const QVector &newRanges) -{ - EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); - if (ed == nullptr) - { - WARNING_LOG(m_logger) << "ed == nullptr"; - return; - } - - m_timelineUi->view->setKnownRanges(newRanges); - ed->setKnownRanges(newRanges); - m_timelineUi->view->refreshData(); -} - - -void TimelineWidget::setMaxPos() -{ - m_timelineUi->scrollbar->setValue(m_timelineUi->scrollbar->maximum()); -} - - -int TimelineWidget::getMaxPos() const -{ - return m_timelineUi->scrollbar->maximum(); -} - - -int TimelineWidget::getPos() const -{ - return m_timelineUi->scrollbar->value(); -} - - -void TimelineWidget::setPos(qint64 pos) -{ - m_timelineUi->scrollbar->setValue(pos / 1000); -} - - -void TimelineWidget::setPositionMarkerTime(const qint64 time) -{ - m_timelineUi->view->slotSetPosMarkTime(time); -} - - -void TimelineWidget::slotUpdateTimeline() -{ - qint64 min = m_model->lowestPositionedItem(); - qint64 max = m_model->highestPositionedItem(); - - if (m_timelineUi->cbMinMax->isChecked()) - { - QString minmax(QStringLiteral("%1 (%2) - %3 (%4)") - .arg(QDateTime::fromMSecsSinceEpoch(min).toString()) - .arg(min) - .arg(QDateTime::fromMSecsSinceEpoch(max).toString()) - .arg(max)); - m_timelineUi->lbMinMax->setText(minmax); - } - - setTimelineRange(min, max); - emit signalRangeChanged(); -} - - -void TimelineWidget::setTimelineModel(AbstractTimelineModel *model) -{ - m_model = model; - - connect(m_model, &AbstractTimelineModel::dataChanged, - this, &TimelineWidget::slotRefreshData ); - - - - m_timelineUi->view->setTimelineModel(m_model); - - - connect(m_timelineUi->indicator, &TimelineIndicator::signalAddLine, - m_timelineUi->view, &TimelineView::slotAddLine); - connect(m_timelineUi->indicator, &TimelineIndicator::signalResetLineList, - m_timelineUi->view, &TimelineView::slotResetLineList); - - EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); - if (ed != nullptr) - { - connect(m_timelineUi->indicator, &TimelineIndicator::signalAddLine, - ed, &EventDelegate::slotAddLine); - connect(m_timelineUi->indicator, &TimelineIndicator::signalResetLineList, - ed, &EventDelegate::slotResetLineList); - } - - m_timelineUi->view->expandToDepth(1); - - m_headerView->resizeSection(0, m_headerView->minimumSectionSize()); - m_headerView->resizeSection(1, m_headerView->minimumSectionSize()); - - slotSetZoomFactor(m_timelineUi->indicator->zoomFactorPoints()); -} - - -qint64 TimelineWidget::getRangeMinimum() const -{ - return EventTools::getAdjustedMin(); -} - - -qint64 TimelineWidget::getRangeMaximum() const -{ - return EventTools::getAdjustedMax(); -} - - -void TimelineWidget::setTimelineRange(qint64 min, qint64 max) -{ - m_min = min; - m_max = max; - - if (m_min > 0 && m_max > 2) - { - m_sbPos = EventTools::fromSecondsToMsecs(m_timelineUi->scrollbar->sliderPosition()); - - if (m_max <= m_min) - { - if (m_min > (10 * 60)) - m_min -= (10 * 60); - } - else - { - qint64 diff = m_max - m_min; - if (diff < (10 * 60)) - if (m_min > (10 * 60)) - m_min -= (10 * 60); - } - - - - - - - - EventTools::setValues(m_min, m_max, m_sbPos, m_zoomFactor); - - - - - m_timelineUi->scrollbar->setRange(EventTools::fromMsecsToSeconds(EventTools::getTimelineMin()), EventTools::fromMsecsToSeconds(EventTools::getTimelineMax())); - - if (m_sbPos < m_min) - { - m_sbPos = m_min; - - m_timelineUi->scrollbar->setSliderPosition(EventTools::fromMsecsToSeconds(m_sbPos)); - } - - - - int pagestep = EventTools::fromMsecsToSeconds(EventTools::s_diff); - if (pagestep < 0) - pagestep = 1; - - - m_timelineUi->scrollbar->setPageStep(pagestep); - - - - - m_timelineUi->bsz->setText(QString::number(EventTools::getBlockSizeTime())); - } - - - refreshData(); -} - - -void TimelineWidget::slotRefreshData() -{ - refreshData(); -} - - -void TimelineWidget::refreshData() -{ - m_headerView->refreshData(); - m_timelineUi->indicator->refreshData(); - m_timelineUi->view->refreshData(); -} - - -void TimelineWidget::slotSetZoomFactor(qint64 zoomFactor) -{ - m_zoomFactor = zoomFactor; - slotUpdateTimeline(); -} - - -void TimelineWidget::scrollBarFilter1(int ) -{ - scrollBarFilter2(); -} - - -void TimelineWidget::scrollBarFilter2() -{ - slotUpdateTimeline(); -} - - -void TimelineWidget::slotSectionResized(int logicalIndex, int oldSize, int newSize) -{ - (void)oldSize; - - if (logicalIndex == 1) - { - int viewScrollbarWidth = m_timelineUi->view->verticalScrollBar()->width(); - m_timelineUi->scrollbar->setMinimumHeight(viewScrollbarWidth); - m_timelineUi->scrollbar->setMaximumHeight(viewScrollbarWidth); - - m_timelineUi->scrollbar->setMaximumWidth(newSize); - m_timelineUi->scrollbar->setMinimumWidth(newSize); - - EventTools::setSection(m_headerView->sectionPosition(1), - newSize ); - } - - slotUpdateTimeline(); -} - -} diff --git a/Lts/Common/LtsTimelineClassic/TimelineWidget.h b/Lts/Common/LtsTimelineClassic/TimelineWidget.h deleted file mode 100755 index 3c527d9b9..000000000 --- a/Lts/Common/LtsTimelineClassic/TimelineWidget.h +++ /dev/null @@ -1,123 +0,0 @@ - - - -#pragma once - -#include - -#include "Range.h" -#include - -#include - - -namespace Ui -{ -class Timeline; -} - - -namespace LtsTimelineClassic -{ -class AbstractTimelineModel; -class EventGraphic; -class TimelineHeaderView; - - - -class LTSTIMELINECLASSIC_EXPORT TimelineWidget : public QWidget -{ - Q_OBJECT - -public: - - - explicit TimelineWidget(QWidget *parent = nullptr); - - ~TimelineWidget(); - - - - void setPositionMarkerTime(const qint64 time); - - qint64 getRangeMinimum() const; - qint64 getRangeMaximum() const; - - void setPos(qint64 pos); - void setMaxPos(); - int getPos() const; - int getMaxPos() const; - - void knownRangesChanged(const QVector &newRanges); - void hideDebugWidget(); - void setLiveTime(qint64 time); - - -private: - - - void setTimelineRange(qint64 min, qint64 max); - - void refreshData(); - - NGL::INGLLogger &m_logger; - Ui::Timeline *m_timelineUi; - - - qint64 m_min; - qint64 m_max; - qint64 m_sbPos; - qint64 m_zoomFactor; - - TimelineHeaderView *m_headerView; - AbstractTimelineModel *m_model; - - -signals: - - - void signalLoadNewEvents(qint64 from, qint64 to); - - - - - - void signalEventAction(EventGraphic *eg, int); - void signalRangeChanged(); - void signalArm(bool arm); - void signalZoomChanged(); - - -public slots: - - - - void scrollBarFilter1(int x); - - - - void scrollBarFilter2(); - - - - void slotSetZoomFactor(qint64 zoomFactor); - - - - void slotSectionResized(int logicalIndex, int oldSize, int newSize); - - - - void setTimelineModel(AbstractTimelineModel *model); - - - - - void slotUpdateTimeline(); - - - void slotRefreshData(); - -}; - -} diff --git a/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui b/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui deleted file mode 100755 index f3fb7bacb..000000000 --- a/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui +++ /dev/null @@ -1,167 +0,0 @@ - - - Timeline - - - - 0 - 0 - 628 - 393 - - - - Timeline - - - - 0 - - - - - - - - - - [draw cache] - - - - - - - [arm] - - - - - - - [min/max] - - - - - - - [update timeline] - - - - - - - Block size time: - - - - - - - TextLabel - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - 0 - 200 - - - - - - - - - 0 - 20 - - - - - 16777215 - 16777215 - - - - Qt::LeftToRight - - - 0 - - - 0 - - - Qt::Horizontal - - - false - - - - - - - - 100 - 30 - - - - - 16777215 - 30 - - - - - - - - - - - - - - - - LtsTimelineClassic::TimelineView - QWidget -
LtsTimelineClassic/TimelineView.h
- 1 -
- - LtsTimelineClassic::TimelineIndicator - QWidget -
LtsTimelineClassic/TimelineIndicator.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props b/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props deleted file mode 100755 index 64c4f6833..000000000 --- a/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props +++ /dev/null @@ -1,66 +0,0 @@ -log4j.reset=true -# exclusivly for timelinetest build - - -log4j.rootLogger=ERROR, file -#log4j.rootLogger=WARN, stdout - -log4j.logger.Timeline.EventInfoWindow=DEBUG -log4j.logger.Timeline.EventTools=DEBUG -log4j.logger.Timeline=WARN - - -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.MaxFileSize=1MB -log4j.appender.file.MaxBackupIndex=9 -log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_timeline.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n - -# Appnd_Console -########################################################### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n - -########################################################### -########################################################### -########################################################### -# 1) Available Levels in LOG4CXX Version 0.10.0: -# TRACE, -# DEBUG, -# INFO, -# WARN, -# ERROR -# FATAL -# -# d = date/time -# p = priority (Level) -# c = category/channel -# m = message -# F = file -# L = line -# n = platform dependent line separator character or characters. -# .... -# -# Examples for console: -# a) -# %d %p %c #%m# (%.50F:%L)%n -# date/time -# priority -# category/channel -# #message# -# (Maximal 50Character des Filename von rechts:Line) -# line separator -# -# b) -# %d (%.15F:%L) %-5p %c |#| %m%n -# date/time -# (Maximal 15Character des Filename von rechts:Line) -# priority, Right pad with spaces if the priority name is less than 5 characters long. -# category/channel -# message -# line separator -# -# Examples for files: - diff --git a/Lts/Common/LtsTimelineClassic/main.cpp b/Lts/Common/LtsTimelineClassic/main.cpp deleted file mode 100755 index 203b50ce5..000000000 --- a/Lts/Common/LtsTimelineClassic/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ - - - -#include "TimelineWidget.h" -#include "TestTimelineModel.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - - -LtsTimelineClassic::TestTimelineModel *ttm; - - -void add(qint64 time_offset) -{ - qint64 time = 1505292189000; - time += time_offset; - - ttm->addTestEvent(new LtsTimelineClassic::Event(0, (int)ProjectDataManagement::EventType_def::Hit, time, 0, 0, 0, 0, - QStringList() << QStringLiteral("Test"), - LtsTimelineClassic::Event::drawCustomEventGraphicEllipse, QColor("green"), QString())); -} - - -int main(int argc, char *argv[]) -{ - qputenv("TDS_DEVEL", "yes, of course"); - qWarning() << "LtsTimelineClassicTest"; - NGLApplication app(argc, argv); - app.createLogger("Timeline", "log4cxx_timeline.props"); - - STARTUP_LOG(app.baseLogger()); - - int ret = 0; - -#if 1 - QWidget *widget = new QWidget(); - widget->setMinimumWidth(500); - - QVBoxLayout *layout = new QVBoxLayout(); - widget->setLayout(layout); - - LtsTimelineClassic::TimelineWidget *timelineWidget = new LtsTimelineClassic::TimelineWidget(); - layout->addWidget(timelineWidget); - - QHBoxLayout *btnLayout = new QHBoxLayout(); - QWidget *btnLayoutWidget = new QWidget(); - btnLayoutWidget->setLayout(btnLayout); - layout->addWidget(btnLayoutWidget); - - btnLayout->addWidget(new QPushButton(QStringLiteral("Filter"))); - btnLayout->addWidget(new QPushButton(QStringLiteral("CSV Export"))); - btnLayout->addStretch(1); - - widget->show(); - - ttm = new LtsTimelineClassic::TestTimelineModel(); - timelineWidget->setTimelineModel(ttm); - - - - - - add(0); - add(100 * 60 * 1000); - add(3000 * 60 * 1000); - - - - ret = app.exec(); - SHUTDOWN_LOG(app.baseLogger()); -#endif - - return ret; -} diff --git a/Lts/Common/LtsUtils/CMakeLists.txt b/Lts/Common/LtsUtils/CMakeLists.txt deleted file mode 100755 index 7051cc9a5..000000000 --- a/Lts/Common/LtsUtils/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-09-19 -# - - -ADD_SUBDIRECTORY(LtsUtilsCore) -ADD_SUBDIRECTORY(LtsUtilsGui) -ADD_SUBDIRECTORY(LtsUtilsSettings) diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsCore/CMakeLists.txt deleted file mode 100755 index 666d70a6c..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -SET(SRCS_hdr_only - ${CMAKE_CURRENT_SOURCE_DIR}/data_modification_enum.h - ${CMAKE_CURRENT_SOURCE_DIR}/dds_bitarray.h - ${CMAKE_CURRENT_SOURCE_DIR}/dds_vector.h - ${CMAKE_CURRENT_SOURCE_DIR}/connection.h - ${CMAKE_CURRENT_SOURCE_DIR}/json_object.h - ${CMAKE_CURRENT_SOURCE_DIR}/outdated_position.h - ${CMAKE_CURRENT_SOURCE_DIR}/qbytearray_byteaccess.h - ${CMAKE_CURRENT_SOURCE_DIR}/qpoint3d.h - ${CMAKE_CURRENT_SOURCE_DIR}/await.h -) -ADD_LIBRARY(LtsUtilsCoreHeaderOnly INTERFACE) -TARGET_INCLUDE_DIRECTORIES(LtsUtilsCoreHeaderOnly - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ -) -TARGET_SOURCES(LtsUtilsCoreHeaderOnly INTERFACE ${SRCS_hdr_only}) - -SET(SRCS - pgarray.cpp - qplane3d.cpp - qpoint3d.cpp - scoped_connection.cpp - sfm_bytemessage_converter.cpp - promise.cpp - lts_translatable_identifier.cpp - lts_portrange.cpp - #variant_operators.h -) - -NGL_ADD_LIBRARY2(ALL LtsUtilsCore SHARED ${SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsCore - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsUtilsCore - PUBLIC - Qt5::Core - LtsUtilsCoreHeaderOnly -) - -# -# --- Tests -------------------------------------------------------------- -# -# IF (TARGET ddscore) // does not work, not yet included -IF((BUILD_WS OR BUILD_SV) AND BUILD_TESTING) - ADD_EXECUTABLE(utils_test - UnitTests/utils_test.cpp - UnitTests/utils_test.h - promise_examples.h - promise_examples.cpp - ) - TARGET_LINK_LIBRARIES(utils_test - LtsUtilsCore - Qt5::Core - Qt5::Test - ddscore - ) - ADD_TEST(NAME utils_test COMMAND $) -ENDIF() diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.cpp deleted file mode 100755 index 5cfd50391..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.cpp +++ /dev/null @@ -1,1202 +0,0 @@ - - - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "utils_test.h" -#include "pgarray.h" -#include "qpoint3d.h" -#include "connection.h" -#include "json_object.h" -#include "sfm_bytemessage_converter.h" -#include "promise.h" -#include "promise_examples.h" -#include "qbytearray_byteaccess.h" -#include "dds_vector.h" - -#define SUPPORT_QBITARRAY -#include "dds_bitarray.h" -#include - -#include -#include -#include -#include - - -using namespace LtsUtils; -namespace QTest -{ -template <> -char *toString(const QVector &vec) -{ - QStringList sl; - sl.reserve(vec.size()); - for (int i : vec) - sl += QString::number(i); - return qstrdup(sl.join(QLatin1Char(',')).toLatin1().constData()); -} -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) -template -inline bool qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected, - const char *file, int line) -{ - return compare_helper(t1 == t2, "Compared values are not the same", - toString(t1), toString(t2), actual, expected, file, line); -} -#endif -} - - - - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - LtsUtilsTest tc; - return QTest::qExec(&tc, argc, argv); -} - - -void LtsUtilsTest::test_promise() -{ - QVector results; - - results.append(1); - - - LtsUtils::Promise p([&results](LtsUtils::ResultFunction resolve, LtsUtils::ResultFunction ) - { - results.append(4); - qDebug() << "LtsUtils Promise: Promise executed! Resolving now"; - - resolve(QStringLiteral("hello world!")); - }); - - results.append(2); - - bool resolved = false; - p.resolved([&resolved, &results](const QVariant &value) - { - results.append(5); - qDebug() << "LtsUtils Promise: Promise was resolved with value" << value; - resolved = true; - }); - - p.rejected([](const QVariant &val) - { - qCritical() << "rejected with val" << val; - QFAIL("Promise unexpectedly rejected"); - }); - - results.append(3); - - QTRY_VERIFY2(resolved, "Promise was not resolved"); - QCOMPARE((QVector() << 1 << 2 << 3 << 4 << 5), results); -} - - -void LtsUtilsTest::test_promise_example1() -{ - qDebug("LtsUtils: test_promise_example1"); - - LtsUtils::PromiseExample1 p; - - QTRY_VERIFY2(p.isSuccess(), "Promise was not resolved"); -} - - -void LtsUtilsTest::test_promise_example2() -{ - qDebug("LtsUtils: test_promise_example2"); - - LtsUtils::PromiseExample2 p; - - QTRY_VERIFY2(p.isSuccess(), "Promise didn't reject as expected"); -} - - -void LtsUtilsTest::test_promise_example3() -{ - qDebug("LtsUtils: test_promise_example3"); - - LtsUtils::PromiseExample3 p; - - qDebug() << "waiting for expected reject to happen, and wait 2 seconds to see if the timeout triggers even though it shouldn't, because the promise was already rejected"; - - QTRY_VERIFY2(p.isSuccess(), "Promise was not resolved"); -} - - -void LtsUtilsTest::test_toIntArray() -{ - qDebug("LtsUtils: test_toIntArray"); - - QString str; - std::vector vec; - - str = QLatin1String("{0,1,2,3,4,999999999,-2147483647,2147483647}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::IntArray::fromPgString(str)(); - QVERIFY2(vec.size() == 8, "Wrong size of QVector! (expected 8)"); - - QVERIFY2(vec[0] == 0, "Wrong value on position 0"); - QVERIFY2(vec[1] == 1, "Wrong value on position 1"); - QVERIFY2(vec[2] == 2, "Wrong value on position 2"); - QVERIFY2(vec[3] == 3, "Wrong value on position 3"); - QVERIFY2(vec[4] == 4, "Wrong value on position 4"); - QVERIFY2(vec[5] == 999999999, "Wrong value on position 5"); - QVERIFY2(vec[6] == -2147483647, "Wrong vtest_fromIntArray2Dalue on position 6"); - QVERIFY2(vec[7] == 2147483647, "Wrong value on position 7"); - - str = QLatin1String("1,2,3,"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::IntArray::fromPgString(str)(); - QVERIFY2(vec.size() == 3, "Wrong size of QVector! (expected 3)"); - - QVERIFY2(vec[0] == 1, "Wrong value on position 0"); - QVERIFY2(vec[1] == 2, "Wrong value on position 1"); - QVERIFY2(vec[2] == 3, "Wrong value on position 2"); - - str = QLatin1String("{a,2,3}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::IntArray::fromPgString(str)(); - QVERIFY2(vec.size() == 2, "Wrong size of QVector! (expected 2)"); - - QVERIFY2(vec[0] == 2, "Wrong value on position 0"); - QVERIFY2(vec[1] == 3, "Wrong value on position 1"); - - str = QLatin1String("{abc}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::IntArray::fromPgString(str)(); - QVERIFY2(vec.size() == 0, "Wrong size of QVector! (expected 0)"); -} - - -void LtsUtilsTest::test_fromIntArray() -{ - qDebug("LtsUtils: test_fromIntArray"); - - QString str = LtsUtils::IntArray({ 0, 1, 2, 3, 999999999, -2147483647, 2147483647 }).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{0,1,2,3,999999999,-2147483647,2147483647}")); - - str = LtsUtils::IntArray(std::vector()).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{}")); -} - - -void LtsUtilsTest::test_toBigintArray() -{ - qDebug("LtsUtils: test_toBigintArray"); - - QString str; - std::vector vec; - - str = QLatin1String("{0,1,2147483647,-2147483647,999999999999}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::BigintArray::fromPgString(str)(); - QVERIFY2(vec.size() == 5, "Wrong size of QVector! (expected 5)"); - - QVERIFY2(vec[0] == 0, "Wrong value on position 0"); - QVERIFY2(vec[1] == 1, "Wrong value on position 1"); - QVERIFY2(vec[2] == 2147483647, "Wrong value on position 2"); - QVERIFY2(vec[3] == -2147483647, "Wrong value on position 3"); - QVERIFY2(vec[4] == 999999999999, "Wrong value on position 4"); - - str = QLatin1String("0,-2147483647,999999999999,"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::BigintArray::fromPgString(str)(); - QVERIFY2(vec.size() == 3, "Wrong size of QVector! (expected 3)"); - - QVERIFY2(vec[0] == 0, "Wrong value on position 0"); - QVERIFY2(vec[1] == -2147483647, "Wrong value on position 1"); - QVERIFY2(vec[2] == 999999999999, "Wrong value on position 2"); - - str = QLatin1String("{a,2,3}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::BigintArray::fromPgString(str)(); - QVERIFY2(vec.size() == 2, "Wrong size of QVector! (expected 2)"); - - QVERIFY2(vec[0] == 2, "Wrong value on position 0"); - QVERIFY2(vec[1] == 3, "Wrong value on position 1"); - - str = QLatin1String("{abc}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::BigintArray::fromPgString(str)(); - QVERIFY2(vec.size() == 0, "Wrong size of QVector! (expected 0)"); -} - - -void LtsUtilsTest::test_fromBigintArray() -{ - qDebug("LtsUtils: test_fromBigintArray"); - - QString str = LtsUtils::BigintArray({ 0, -2147483647, 2147483647, 999999999999, 123456789}).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{0,-2147483647,2147483647,999999999999,123456789}")); - - str = LtsUtils::BigintArray(std::vector()).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{}")); -} - - -void LtsUtilsTest::test_toIntArray2D() -{ - qDebug("LtsUtils: test_toIntArray2D"); - - QString str; - std::map vec; - - str = QLatin1String("{{0,2},{1,3},{2,999999999},{3,-2147483647},{4,2147483647}}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::IntArray2D::fromPgString(str)(); - QVERIFY2(vec.size() == 5, "Wrong size of QVector! (expected 5)"); - - QVERIFY2(vec[0] == 2, "Wrong value on position 0"); - QVERIFY2(vec[1] == 3, "Wrong value on position 1"); - QVERIFY2(vec[2] == 999999999, "Wrong value on position 2"); - QVERIFY2(vec[3] == -2147483647, "Wrong value on position 3"); - QVERIFY2(vec[4] == 2147483647, "Wrong value on position 4"); -} - - -void LtsUtilsTest::test_fromIntArray2D() -{ - qDebug("LtsUtils: test_fromIntArray2D"); - - QString str = LtsUtils::IntArray2D({{0,2},{1,3},{2,999999999},{3,-2147483647},{4,2147483647},{5,123456}}).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{{0,2},{1,3},{2,999999999},{3,-2147483647},{4,2147483647},{5,123456}}")); - - str = LtsUtils::IntArray2D(std::map()).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{}")); -} - - -void LtsUtilsTest::test_toBigintArray2D() -{ - qDebug("LtsUtils: test_toBigintArray2D"); - - QString str; - std::map vec; - - str = QLatin1String("{{0,2},{1,3},{2,999999999999},{3,-2147483647},{4,2147483647}}"); - qDebug() << "LtsUtils: test " << str; - vec = LtsUtils::BigintArray2D::fromPgString(str)(); - QVERIFY2(vec.size() == 5, "Wrong size of QVector! (expected 5)"); - - QVERIFY2(vec[0] == 2, "Wrong value on position 0"); - QVERIFY2(vec[1] == 3, "Wrong value on position 1"); - QVERIFY2(vec[2] == 999999999999, "Wrong value on position 2"); - QVERIFY2(vec[3] == -2147483647, "Wrong value on position 3"); - QVERIFY2(vec[4] == 2147483647, "Wrong value on position 4"); -} - - -void LtsUtilsTest::test_fromBigintArray2D() -{ - qDebug("LtsUtils: test_fromBigintArray2D"); - - QString str = LtsUtils::BigintArray2D({{0,-123456789},{1,-2147483647},{2,999999999999},{3,123456789},{4,2147483647}}).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{{0,-123456789},{1,-2147483647},{2,999999999999},{3,123456789},{4,2147483647}}")); - - str = LtsUtils::BigintArray2D(std::map()).toPgString(); - - qDebug() << "LtsUtils: Result " << str; - QCOMPARE(str, QLatin1String("{}")); -} - - -void LtsUtilsTest::test_QPoint3D() -{ - qDebug("LtsUtils: test_QPoint3D"); - - LtsUtils::QPoint3D pos(0,0,0); - - qDebug() << "LstUtils: position (X,Y,Z) = (" << pos.x() << "," << pos.y() << "," << pos.z() << ")"; - - QVERIFY2(pos.x() == 0, "Wrong position (X)"); - QVERIFY2(pos.y() == 0, "Wrong position (Y)"); - QVERIFY2(pos.z() == 0, "Wrong position (Z)"); - - pos.setX(92000); - pos.setY(77400); - pos.setZ(0); - - qDebug() << "LstUtils: position (X,Y,Z) = (" << pos.x() << "," << pos.y() << "," << pos.z() << ")"; - - QVERIFY2(pos.x() == 92000, "Wrong position (X)"); - QVERIFY2(pos.y() == 77400, "Wrong position (Y)"); - QVERIFY2(pos.z() == 0, "Wrong position (Z)"); -} - - - -void LtsUtilsTest::test_bitArray() -{ - qDebug("LtsUtils: test_bitArray"); - - { - QVERIFY2(LtsUtils::BitArray::convert>(QBitArray(8)).size() == 1, " wrong size of underlying data"); - QVERIFY2(LtsUtils::BitArray::convert>(QBitArray(9)).size() == 2, " wrong size of underlying data"); - QVERIFY2(LtsUtils::BitArray::convert>(QBitArray(16)).size() == 2, " wrong size of underlying data"); - QVERIFY2(LtsUtils::BitArray::convert>(QBitArray(17)).size() == 3, " wrong size of underlying data"); - } - - - - { - QBitArray ba(10); - { - int i = 0; - ba[i++] = true; - ba[i++] = false; - ba[i++] = true; - ba[i++] = false; - ba[i++] = true; - - ba[i++] = false; - ba[i++] = true; - ba[i++] = false; - ba[i++] = true; - ba[i++] = false; - } - - const std::vector bArray = LtsUtils::BitArray::convert>(ba); - - - for(int position=0; position<20;++position) - LtsUtils::BitArray::testBit(bArray, position); - - - - { - QBitArray retrievedBd = LtsUtils::BitArray::convert(bArray, ba.size()); - QVERIFY2(ba == retrievedBd, " forward/backward failed"); - } - - - int i = 0; - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - } - - - std::vector bArrayCopy; - { - QBitArray ba(10); - { - int i = 0; - ba[i++] = true; - ba[i++] = true; - ba[i++] = true; - ba[i++] = true; - ba[i++] = true; - - ba[i++] = false; - ba[i++] = false; - ba[i++] = false; - ba[i++] = false; - ba[i++] = false; - } - - const std::vector bArray = LtsUtils::BitArray::convert>(ba); - - - { - QBitArray retrievedBd = LtsUtils::BitArray::convert(bArray, ba.size()); - QVERIFY2(ba == retrievedBd, " forward/backward failed"); - } - - int i = 0; - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == true, " testBit failed"); - - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArray, i++) == false, " testBit failed"); - - - bArrayCopy = bArray; - } - - { - - - - LtsUtils::BitArray::clearBit(bArrayCopy, 0); - LtsUtils::BitArray::clearBit(bArrayCopy, 1); - LtsUtils::BitArray::clearBit(bArrayCopy, 2); - LtsUtils::BitArray::clearBit(bArrayCopy, 3); - LtsUtils::BitArray::clearBit(bArrayCopy, 4); - QVERIFY2(bArrayCopy[0] == 0, " clearBit failed"); - - - LtsUtils::BitArray::setBit(bArrayCopy, 1); - LtsUtils::BitArray::setBit(bArrayCopy, 2); - LtsUtils::BitArray::setBit(bArrayCopy, 3); - LtsUtils::BitArray::setBit(bArrayCopy, 4); - LtsUtils::BitArray::setBit(bArrayCopy, 5); - - int i = 0; - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == true, " testBit failed"); - - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == true, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == false, " testBit failed"); - QVERIFY2(LtsUtils::BitArray::testBit(bArrayCopy, i++) == false, " testBit failed"); - - - { - QBitArray ba = LtsUtils::BitArray::convert(bArrayCopy, 10); - const std::vector retrievedVec = LtsUtils::BitArray::convert>(ba); - QVERIFY2(bArrayCopy == retrievedVec, " forward/backward failed"); - } - - } -} - - - -void LtsUtilsTest::test_convert() -{ - constexpr int containerLen = 50; - - - QByteArray ba(containerLen, Qt::Uninitialized); - for (int i = 0; i < containerLen; ++i) - ba[i] = i + 1; - const auto result = LtsUtils::convert>(ba); - const auto resultBA = LtsUtils::toQByteArray(result); - QCOMPARE(result.size(), containerLen); - QCOMPARE(resultBA.size(), containerLen); - for (int i = 0; i < containerLen; ++i) - QCOMPARE(resultBA.at(i), i + 1); - - - QVector> outer; - outer.reserve(containerLen); - for (int i = 0; i < containerLen; ++i) - outer.push_back(result); - const auto resultVec = LtsUtils::convert>>(outer); - const auto resultQVec = LtsUtils::convert>>(resultVec); - QCOMPARE(resultVec.size(), containerLen); - QCOMPARE(resultQVec.size(), containerLen); - for (const auto &inner : resultQVec) - { - for (int i = 0; i < containerLen; ++i) - QCOMPARE(inner.at(i), i + 1); - } - - - QVERIFY(!LtsUtils::isInVector(result, 0)); - QVERIFY(LtsUtils::isInVector(result, 1)); - QVERIFY(LtsUtils::isInVector(result, containerLen)); - QVERIFY(!LtsUtils::isInVector(result, containerLen + 1)); -} - - - -class Emitter : public QObject -{ - Q_OBJECT - -signals: - void signal1(); - void signal2(int i); -}; - -class Spy : public QObject -{ - Q_OBJECT - -public slots: - void slot1() { ++m_slot1Count; } - -public: - int m_slot1Count{0}; -}; - -void LtsUtilsTest::test_connection() -{ - Emitter emitter; - Spy spy; - std::unique_ptr conn(LtsUtils::createConnection(&emitter, &Emitter::signal1, &spy, &Spy::slot1)); - emit emitter.signal1(); - QCOMPARE(spy.m_slot1Count, 0); - - conn->connect(); - emit emitter.signal1(); - QCOMPARE(spy.m_slot1Count, 1); - - conn->disconnect(); - emit emitter.signal1(); - QCOMPARE(spy.m_slot1Count, 1); -} - -void LtsUtilsTest::test_queuedConnection() -{ - Emitter emitter; - Spy spy; - std::unique_ptr conn(LtsUtils::createConnection(&emitter, &Emitter::signal2, &spy, &Spy::slot1)); - emit emitter.signal2(42); - QCOMPARE(spy.m_slot1Count, 0); - - conn->connect(Qt::QueuedConnection); - emit emitter.signal2(42); - QCOMPARE(spy.m_slot1Count, 0); - - QTRY_COMPARE(spy.m_slot1Count, 1); -} - -void LtsUtilsTest::test_signalSignalConnection() -{ - Emitter emitter; - Spy spy; - std::unique_ptr signalSignalConn(LtsUtils::createConnection(&emitter, &Emitter::signal2, &emitter, &Emitter::signal1)); - std::unique_ptr signalSlotConn(LtsUtils::createConnection(&emitter, &Emitter::signal1, &spy, &Spy::slot1)); - - signalSignalConn->connect(); - signalSlotConn->connect(); - - emit emitter.signal2(42); - QCOMPARE(spy.m_slot1Count, 1); -} - -void LtsUtilsTest::test_danglingConnection1() -{ - Spy spy; - std::unique_ptr conn; - - { - Emitter emitter; - conn.reset(LtsUtils::createConnection(&emitter, &Emitter::signal2, &spy, &Spy::slot1)); - } - - conn->connect(); - conn->disconnect(); -} - -void LtsUtilsTest::test_danglingConnection2() -{ - Emitter emitter; - std::unique_ptr conn; - - { - Spy spy; - conn.reset(LtsUtils::createConnection(&emitter, &Emitter::signal2, &spy, &Spy::slot1)); - } - - conn->connect(); - conn->disconnect(); -} - -void LtsUtilsTest::test_danglingConnection3() -{ - Emitter emitter; - std::unique_ptr spy(new Spy); - std::unique_ptr conn; - - conn.reset(LtsUtils::createConnection(&emitter, &Emitter::signal2, spy.get(), &Spy::slot1)); - - conn->connect(); - spy.reset(); - conn->disconnect(); -} - -void LtsUtilsTest::test_jsonObject_fromJson() -{ - { - std::string json = R"({})"; - const auto obj = JsonObject::fromJson(json); - QVERIFY(obj.isValid()); - QVERIFY(obj.isEmpty()); - } - - { - std::string json = R"({"a": 1, "b": true})"; - const auto obj = JsonObject::fromJson(json); - QVERIFY(obj.isValid()); - QVERIFY(!obj.isEmpty()); - } - - { - std::string json = R"({"a": 1, "b": })"; - const auto obj = JsonObject::fromJson(json); - QVERIFY(!obj.isValid()); - QVERIFY(obj.isEmpty()); - } - - { - std::string json = "[1, 2, 3, 4]"; - const auto obj = JsonObject::fromJson(json); - QVERIFY(!obj.isValid()); - QVERIFY(obj.isEmpty()); - } -} - -void LtsUtilsTest::test_jsonObject_contains() -{ - std::string json = R"({ "int": 42, "double": 3.14, "string": "hello json", "bool": true, "null": null, - "object": {}, - "int array": [1, 2, 3], - "string array": ["hello", "json"], - "mixed array": ["hello", 42], - "int map": {"i": 1, "j": 2, "k": 3}, - "mixed map": {"i": 1, "j": 2, "k": "3"} - })"; - JsonObject obj = JsonObject::fromJson(json); - - QVERIFY(obj.contains("int")); - QVERIFY(obj.contains("int")); - QVERIFY(!obj.contains("int")); - QVERIFY(!obj.contains("int")); - QVERIFY(!obj.contains("int")); - QVERIFY(!obj.contains("int")); - QVERIFY(!obj.contains("int")); - QVERIFY(!obj.contains("int")); - - QVERIFY(!obj.contains("double")); - QVERIFY(obj.contains("double")); - QVERIFY(!obj.contains("double")); - QVERIFY(!obj.contains("double")); - QVERIFY(!obj.contains("double")); - QVERIFY(!obj.contains("double")); - QVERIFY(!obj.contains("double")); - QVERIFY(!obj.contains("double")); - - QVERIFY(!obj.contains("string")); - QVERIFY(!obj.contains("string")); - QVERIFY(!obj.contains("string")); - QVERIFY(obj.contains("string")); - QVERIFY(obj.contains("string")); - QVERIFY(!obj.contains("string")); - QVERIFY(!obj.contains("string")); - QVERIFY(!obj.contains("string")); - - QVERIFY(!obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - QVERIFY(obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - QVERIFY(!obj.contains("bool")); - - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(!obj.contains("null")); - QVERIFY(obj.contains("null")); - - QVERIFY(!obj.contains("object")); - QVERIFY(!obj.contains("object")); - QVERIFY(!obj.contains("object")); - QVERIFY(!obj.contains("object")); - QVERIFY(!obj.contains("object")); - QVERIFY(obj.contains("object")); - QVERIFY(obj.contains("object")); - QVERIFY(!obj.contains("object")); - QVERIFY(!obj.contains("object")); - - QVERIFY(!obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - QVERIFY(obj.contains("int array")); - QVERIFY(obj.contains("int array")); - QVERIFY(!obj.contains("int array")); - - QVERIFY(obj.contains("mixed array")); - QVERIFY(obj.contains("mixed array")); - - - QVERIFY(obj.contains>("int array")); - QVERIFY(!obj.contains>("int array")); - QVERIFY(obj.contains>("int array")); - QVERIFY(!obj.contains>("int array")); - - QVERIFY((obj.contains>("int map"))); - QVERIFY(!(obj.contains>("mixed map"))); - QVERIFY((obj.contains>("int map"))); - QVERIFY(!(obj.contains>("mixed map"))); -} - -void LtsUtilsTest::test_jsonObject_value() -{ - std::string json = R"({ "int": 42, "double": 3.14, "string": "hello json", "bool": true, "null": null, - "object": {"a": 1, "b": 2, "c": 3}, - "int array": [1, 2, 3], - "string array": ["hello", "json"], - "mixed array": ["hello", 42], - "int map": {"i": 1, "j": 2, "k": 3} - })"; - JsonObject obj = JsonObject::fromJson(json); - - QCOMPARE(obj.value("int"), 42); - QCOMPARE(obj.value("double"), 3.14); - QCOMPARE(obj.value("string").c_str(), "hello json"); - QCOMPARE(obj.value("string"), QStringLiteral("hello json")); - QCOMPARE(obj.value("bool"), true); - - QJsonObject expectedObject{ {QStringLiteral("a"), 1}, {QStringLiteral("b"), 2}, {QStringLiteral("c"), 3} }; - QCOMPARE(obj.value("object"), expectedObject); - QCOMPARE(obj.value("object"), JsonObject(expectedObject)); - - QJsonArray expectedMixedArray{QStringLiteral("hello"), 42}; - QCOMPARE(obj.value("mixed array"), expectedMixedArray); - QCOMPARE(obj.value("mixed array"), JsonArray(expectedMixedArray)); - - QVector expectedIntArray{1, 2, 3}; - QCOMPARE(obj.value>("int array"), expectedIntArray); - - std::vector expectedStdIntArray{1, 2, 3}; - QCOMPARE(obj.value>("int array"), expectedStdIntArray); - - QVector expectedStringArray{QStringLiteral("hello"), QStringLiteral("json")}; - QCOMPARE(obj.value>("string array"), expectedStringArray); - - std::vector expectedStdStringArray{"hello", "json"}; - QCOMPARE(obj.value>("string array"), expectedStdStringArray); - - QMap expectedIntMap{ {QStringLiteral("i"), 1}, {QStringLiteral("j"), 2}, {QStringLiteral("k"), 3} }; - QCOMPARE((obj.value>("int map")), expectedIntMap); - - std::map expectedStdIntMap{ {"i", 1}, {"j", 2}, {"k", 3} }; - QCOMPARE((obj.value>("int map")), expectedStdIntMap); - - QVERIFY(obj.isNull("null")); -} - -void LtsUtilsTest::test_jsonObject_valueWithError() -{ - std::string json = R"({ "int": 42, "double": 3.14, "string": "hello json", "bool": true, "null": null, - "object": {"a": 1, "b": 2, "c": 3}, - "int array": [1, 2, 3], - "string array": ["hello", "json"], - "mixed array": ["hello", 42], - "mixed map": {"i": 1, "j": 2, "k": "3"} - })"; - JsonObject obj = JsonObject::fromJson(json); - - QVERIFY_EXCEPTION_THROWN(obj.value("unknown key"), JsonKeyError); - - QVERIFY_EXCEPTION_THROWN(obj.value("double"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("string"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("int"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("int"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("int"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("int"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value("object"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN(obj.value>("mixed array"), JsonTypeError); - QVERIFY_EXCEPTION_THROWN((obj.value>("mixed map")), JsonTypeError); - QVERIFY_EXCEPTION_THROWN((obj.value>("mixed map")), JsonTypeError); -} - -void LtsUtilsTest::test_jsonObject_valueOr() -{ - QJsonObject obj_{ {QStringLiteral("int"), 42} }; - JsonObject obj(obj_); - - QCOMPARE(obj.valueOr("int", -1), 42); - - - QCOMPARE(obj.valueOr("string", -1), -1); - - - QVERIFY_EXCEPTION_THROWN(obj.valueOr("int", QString()), JsonTypeError); -} - -void LtsUtilsTest::test_jsonObject_integers() -{ - std::string json = R"({ - "int16_min": -32768, - "int16_max": 32767, - "int16_min-1": -32769, - "int16_max+1": 32768, - - "int32_min": -2147483648, - "int32_max": 2147483647, - "int32_min-1": -2147483649, - "int32_max+1": 2147483648, - - "int64_min": -9007199254740991, - "int64_max": 9007199254740991, - "int64_min-1": -9007199254740992, - "int64_max+1": 9007199254740992, - - "uint_min": 0, - "uint16_max": 65535, - "uint_min-1": -1, - "uint16_max+1": 65536, - - "uint32_max": 4294967295, - "uint32_max+1": 4294967296, - - "uint64_max": 9007199254740991, - "uint64_max+1": 9007199254740992 - })"; - JsonObject obj = JsonObject::fromJson(json); - QVERIFY(obj.contains("int16_min")); - QVERIFY(obj.contains("int16_max")); - QVERIFY(!obj.contains("int16_min-1")); - QVERIFY(!obj.contains("int16_max+1")); - QVERIFY(obj.contains("int16_min-1")); - QVERIFY(obj.contains("int16_max+1")); - - QVERIFY(obj.contains("int32_min")); - QVERIFY(obj.contains("int32_max")); - QVERIFY(!obj.contains("int32_min-1")); - QVERIFY(!obj.contains("int32_max+1")); - QVERIFY(obj.contains("int32_min-1")); - QVERIFY(obj.contains("int32_max+1")); - - QVERIFY(obj.contains("int64_min")); - QVERIFY(obj.contains("int64_max")); - QVERIFY(!obj.contains("int64_min-1")); - QVERIFY(!obj.contains("int64_max+1")); - - - QVERIFY(obj.contains("uint_min")); - QVERIFY(obj.contains("uint16_max")); - QVERIFY(!obj.contains("uint_min-1")); - QVERIFY(!obj.contains("uint16_max+1")); - QVERIFY(obj.contains("uint16_max+1")); - - QVERIFY(obj.contains("uint_min")); - QVERIFY(obj.contains("uint32_max")); - QVERIFY(!obj.contains("uint_min-1")); - QVERIFY(!obj.contains("uint32_max+1")); - QVERIFY(obj.contains("uint32_max+1")); - - QVERIFY(obj.contains("uint_min")); - QVERIFY(obj.contains("uint64_max")); - QVERIFY(!obj.contains("uint_min-1")); - QVERIFY(!obj.contains("uint64_max+1")); -} - -void LtsUtilsTest::test_jsonObject_nullValues() -{ - std::string jsonNullMap = R"( {"a": null, "b": null, "c": null} )"; - JsonObject obj = JsonObject::fromJson(jsonNullMap); - QVERIFY(obj.isMap()); -} - -void LtsUtilsTest::test_jsonArray_fromJson() -{ - { - std::string json = "[]"; - const auto arr = JsonArray::fromJson(json); - QVERIFY(arr.isValid()); - QVERIFY(arr.isEmpty()); - } - - { - std::string json = R"([1, 2, true, false, null])"; - const auto arr = JsonArray::fromJson(json); - QVERIFY(arr.isValid()); - QVERIFY(!arr.isEmpty()); - } - - { - std::string json = R"({"a": 1, "b": })"; - const auto arr = JsonArray::fromJson(json); - QVERIFY(!arr.isValid()); - QVERIFY(arr.isEmpty()); - } - - { - std::string json = R"({"a": 1, "b": true})"; - const auto arr = JsonArray::fromJson(json); - QVERIFY(!arr.isValid()); - QVERIFY(arr.isEmpty()); - } -} - -void LtsUtilsTest::test_jsonArray() -{ - std::string jsonIntArray = R"( [0, 1, 2, 3] )"; - JsonArray array = JsonArray::fromJson(jsonIntArray); - QCOMPARE(array.size(), 4); - - QVERIFY(array.contains(0)); - QVERIFY(!array.contains(0)); - QCOMPARE(array.at(0), 0); - - QVERIFY_EXCEPTION_THROWN(array.at(0), JsonTypeError); - - QVERIFY_EXCEPTION_THROWN(array.contains(-1), JsonRangeError); - QVERIFY_EXCEPTION_THROWN(array.contains(4), JsonRangeError); - QVERIFY_EXCEPTION_THROWN(array.at(-1), JsonRangeError); - QVERIFY_EXCEPTION_THROWN(array.at(4), JsonRangeError); - - QVERIFY(array.isVector()); - QVERIFY(!array.isVector()); - QCOMPARE(array.toVector(), QVector({0, 1, 2, 3})); - QCOMPARE(array.toStdVector(), std::vector({0, 1, 2, 3})); -} - -void LtsUtilsTest::test_jsonArray_nullArray() -{ - std::string jsonNullArray = R"( [null, null, null, null] )"; - JsonArray array = JsonArray::fromJson(jsonNullArray); - QVERIFY(array.isNull(0)); - QVERIFY(array.isVector()); -} - -void LtsUtilsTest::test_byteStreamFromSfmMessage() -{ - SfmMessage msg; - - msg.setId(16); - msg.setRtr(true); - const quint8 payloadArray [] = {0x11, 0x02, 0x03, 0x04, 0x50}; - msg.setPayload(QByteArray(reinterpret_cast(payloadArray), sizeof(payloadArray))); - - const quint8 expectedResultArray [] = {0x02, 0x80, 0x00, 0x10, 0x05, - 0x80, 0x91, 0x80, 0x82, 0x80, - 0x83, 0x04, 0x50, 0x00, 0x03}; - QByteArray expectedResult = QByteArray(reinterpret_cast(expectedResultArray), sizeof(expectedResultArray)); - - QByteArray byteMessage = byteStreamFromSfmMessage (msg); - - QCOMPARE(byteMessage, expectedResult); -} - -void LtsUtilsTest::test_byteStreamFromSfmMessage2() -{ - SfmMessage msg = LtsUtils::byteStreamToSfmMessage(QByteArray::fromHex("0280004f003003")); - QCOMPARE(msg.rtr(), true); - QVERIFY2(msg.id() == 0x4f, "Value not expected"); - QVERIFY2((msg.id() | (msg.rtr() << 15)) == 0x804f, "Value not expected"); -} - -void LtsUtilsTest::test_byteStreamToSfmMessage() -{ - SfmMessage msgComplete; - msgComplete.setId(16); - msgComplete.setRtr(true); - - const quint8 payloadArray [] = {0x11, 0x02, 0x03, 0x04, 0x50, 0x00}; - msgComplete.setPayload(QByteArray(reinterpret_cast(payloadArray), sizeof(payloadArray))); - - const quint8 dataArray [] = {0x02, 0x80, 0x00, 0x10, 0x06, - 0x80, 0x91, 0x80, 0x82, 0x80, - 0x83, 0x04, 0x50, 0x00, 0xFF, 0x03}; - QByteArray dataComplete = QByteArray(reinterpret_cast(dataArray), sizeof(dataArray)); - - SfmMessage result; - try - { - result = byteStreamToSfmMessage(dataComplete); - } - catch (const ConversionError &ex) - { - qDebug() << ex.what(); - } - QCOMPARE(result, msgComplete); - - - QVERIFY_EXCEPTION_THROWN(byteStreamToSfmMessage(QByteArray()), ConversionError); - - - QByteArray framingError = dataComplete; - framingError.chop(1); - QVERIFY_EXCEPTION_THROWN(byteStreamToSfmMessage(framingError), ConversionError); - - - QByteArray checksumError = dataComplete; - checksumError[14] = 0x01; - QVERIFY_EXCEPTION_THROWN(byteStreamToSfmMessage(checksumError), ConversionError); - - - QByteArray lengthError = dataComplete; - QVERIFY_EXCEPTION_THROWN(byteStreamToSfmMessage(lengthError.remove(13,1)), ConversionError); -} - -void LtsUtilsTest::byteAccess() -{ - QByteArray ba; - LtsUtils::set1Byte(0x67, ba); - LtsUtils::set1Byte(0xcb, ba); - LtsUtils::set2Byte(0x1234, ba); - LtsUtils::set2Byte(0x9876, ba); - LtsUtils::set3Byte(0x6263fb, ba); - LtsUtils::set3Byte(0x80e87b, ba); - LtsUtils::set4Byte(0x66b9dd15, ba); - LtsUtils::set4Byte(0xa254a715, ba); - LtsUtils::set8Byte(0x1234567812345678, ba); - LtsUtils::set8Byte(0x8234567812345678, ba); - - { - int64_t i = 0; - size_t pos = 0; - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, 103); - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, -53); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, 4660); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, -26506); - LtsUtils::get3Byte(ba, pos, i); - QCOMPARE(i, 6448123); - LtsUtils::get3Byte(ba, pos, i); - QCOMPARE(i, -8329093); - LtsUtils::get4Byte(ba, pos, i); - QCOMPARE(i, 1723456789); - LtsUtils::get4Byte(ba, pos, i); - QCOMPARE(i, -1571510507); - LtsUtils::get8Byte(ba, pos, i); - QCOMPARE(i, 0x1234567812345678); - LtsUtils::get8Byte(ba, pos, i); - QCOMPARE(i, 0x8234567812345678); - - uint64_t u = 0; - pos = 0; - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 103); - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 203); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 4660); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 39030); - LtsUtils::get3Byte(ba, pos, u); - QCOMPARE(u, 6448123); - LtsUtils::get3Byte(ba, pos, u); - QCOMPARE(u, 8448123); - LtsUtils::get4Byte(ba, pos, u); - QCOMPARE(u, 1723456789); - LtsUtils::get4Byte(ba, pos, u); - QCOMPARE(u, 2723456789); - LtsUtils::get8Byte(ba, pos, u); - QCOMPARE(u, 0x1234567812345678); - LtsUtils::get8Byte(ba, pos, u); - QCOMPARE(u, 0x8234567812345678); - } - { - int32_t i = 0; - size_t pos = 0; - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, 103); - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, -53); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, 4660); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, -26506); - LtsUtils::get3Byte(ba, pos, i); - QCOMPARE(i, 6448123); - LtsUtils::get3Byte(ba, pos, i); - QCOMPARE(i, -8329093); - LtsUtils::get4Byte(ba, pos, i); - QCOMPARE(i, 1723456789); - LtsUtils::get4Byte(ba, pos, i); - QCOMPARE(i, -1571510507); - - uint32_t u = 0; - pos = 0; - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 103); - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 203); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 4660); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 39030); - LtsUtils::get3Byte(ba, pos, u); - QCOMPARE(u, 6448123); - LtsUtils::get3Byte(ba, pos, u); - QCOMPARE(u, 8448123); - LtsUtils::get4Byte(ba, pos, u); - QCOMPARE(u, 1723456789); - LtsUtils::get4Byte(ba, pos, u); - QCOMPARE(u, 2723456789); - } - { - int16_t i = 0; - size_t pos = 0; - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, 103); - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, -53); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, 4660); - LtsUtils::get2Byte(ba, pos, i); - QCOMPARE(i, -26506); - - uint16_t u = 0; - pos = 0; - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 103); - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 203); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 4660); - LtsUtils::get2Byte(ba, pos, u); - QCOMPARE(u, 39030); - } - { - int8_t i = 0; - size_t pos = 0; - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, 103); - LtsUtils::get1Byte(ba, pos, i); - QCOMPARE(i, -53); - - uint8_t u = 0; - pos = 0; - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 103); - LtsUtils::get1Byte(ba, pos, u); - QCOMPARE(u, 203); - } -} - - -#include "utils_test.moc" diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.h b/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.h deleted file mode 100755 index e6a37453b..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/UnitTests/utils_test.h +++ /dev/null @@ -1,62 +0,0 @@ - - -#pragma once - -#include - -class LtsUtilsTest : public QObject -{ - Q_OBJECT -private Q_SLOTS: - - void test_toIntArray(); - void test_fromIntArray(); - void test_toBigintArray(); - void test_fromBigintArray(); - void test_toIntArray2D(); - void test_fromIntArray2D(); - void test_toBigintArray2D(); - void test_fromBigintArray2D(); - - void test_QPoint3D(); - - - void test_bitArray(); - - - void test_convert(); - - - void test_connection(); - void test_queuedConnection(); - void test_signalSignalConnection(); - void test_danglingConnection1(); - void test_danglingConnection2(); - void test_danglingConnection3(); - - - void test_jsonObject_fromJson(); - void test_jsonObject_contains(); - void test_jsonObject_value(); - void test_jsonObject_valueWithError(); - void test_jsonObject_valueOr(); - void test_jsonObject_integers(); - void test_jsonObject_nullValues(); - void test_jsonArray_fromJson(); - void test_jsonArray(); - void test_jsonArray_nullArray(); - - - void test_byteStreamFromSfmMessage(); - void test_byteStreamFromSfmMessage2(); - void test_byteStreamToSfmMessage(); - - - void test_promise(); - void test_promise_example1(); - void test_promise_example2(); - void test_promise_example3(); - - - void byteAccess(); -}; diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/await.h b/Lts/Common/LtsUtils/LtsUtilsCore/await.h deleted file mode 100755 index d48cbe915..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/await.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - - - - -#include -#include -#include - -#include - - -namespace LtsUtils -{ -template -void addConnect(QEventLoop &x, bool &success, const typename QtPrivate::FunctionPointer::Object *sender, Func signal, bool isSuccess) -{ - if (isSuccess) - { - QObject::connect(sender, signal, &x, [&success]() - { - - success = true; - }); - } - - QObject::connect(sender, signal, &x, &QEventLoop::quit, Qt::QueuedConnection); -} - - -template -void addConnect(QEventLoop &x, bool &success, const typename QtPrivate::FunctionPointer::Object *sender, Func signal, bool isSuccess, Connect... connects) -{ - addConnect(x, success, sender, signal, isSuccess); - addConnect(x, success, connects...); -} - - -template -void await(Connect... connects) noexcept(false) -{ - QEventLoop x; - bool success = false; - - addConnect(x, success, connects...); - - - x.exec(); - - - if (!success) - { - - throw std::runtime_error("Failed."); - } -} - - -template -void awaitTimeout(const int timeoutMs, Connect... connects) noexcept(false) -{ - std::unique_ptr t(new QTimer); - bool timeout = false; - - if (timeoutMs > -1) - { - t->setInterval(timeoutMs); - t->setSingleShot(true); - QObject::connect(t.get(), &QTimer::timeout, t.get(), [&timeout](){ fprintf(stderr, "awaitTimeout: timed out!\n"); timeout = true; }); - t->start(); - } - - await(t.get(), &QTimer::timeout, false, connects...); - - - if (timeout) - { - - throw std::runtime_error("Timeout."); - } -} - - - - - - - - - - - - - - - - - - -template -void awaitWithCancelationSignal(const typename QtPrivate::FunctionPointer::Object *sender1, Func1 signal1, - const typename QtPrivate::FunctionPointer::Object *sender2, Func2 signal2, - const int timeoutMs = 0) noexcept(false) -{ - if (timeoutMs > 0) - { - awaitTimeout(timeoutMs, - sender1, signal1, true, - sender2, signal2, false); - return; - } - - await(sender1, signal1, true, - sender2, signal2, false); -} -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/connection.h b/Lts/Common/LtsUtils/LtsUtilsCore/connection.h deleted file mode 100644 index 5c14f79c5..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/connection.h +++ /dev/null @@ -1,99 +0,0 @@ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace LtsUtils { - -class IConnection { -public: - virtual ~IConnection() = default; - virtual void connect(Qt::ConnectionType = Qt::AutoConnection) = 0; - virtual void disconnect() = 0; -}; - -class DummyConnection : public IConnection { -public: - void connect(Qt::ConnectionType = Qt::AutoConnection) override {} - void disconnect() override {} -}; - -namespace detail { - -template struct ExtractClass; - -template struct ExtractClass { - using type = T; -}; - -} // namespace detail - -template class Connection : public IConnection { - static_assert(std::is_member_function_pointer_v, - "Must be a pointer to method"); - static_assert(std::is_member_function_pointer_v, - "Must be a pointer to method"); - - using Emitter = typename detail::ExtractClass::type; - using Target = typename detail::ExtractClass::type; - - static_assert(std::is_base_of_v, - "Must be derived from QObject"); - static_assert(std::is_base_of_v, - "Must be derived from QObject"); - -public: - Connection(Emitter *emitter, Signal signal, Target *target, Slot slot) - : m_emitter(emitter), m_signal(signal), m_target(target), m_slot(slot) { - Q_ASSERT(emitter); - Q_ASSERT(signal); - Q_ASSERT(target); - Q_ASSERT(slot); - } - - void - connect(Qt::ConnectionType connectionType = Qt::AutoConnection) override { - Q_ASSERT(!m_connected); - - m_connected = true; - - if (!m_emitter || !m_target) - return; - - m_conn = QObject::connect(m_emitter.data(), m_signal, m_target.data(), - m_slot, connectionType); - Q_ASSERT(m_conn); - } - - void disconnect() override { - Q_ASSERT(m_connected); - - if (m_conn) - QObject::disconnect(m_conn); - - m_connected = false; - } - -private: - bool m_connected{false}; - QPointer m_emitter; - Signal m_signal; - QPointer m_target; - Slot m_slot; - QMetaObject::Connection m_conn; -}; - -template -Connection * -createConnection(typename detail::ExtractClass::type *emitter, - Signal signal, - typename detail::ExtractClass::type *target, Slot slot) { - return new Connection(emitter, signal, target, slot); -} - -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/data_modification_enum.h b/Lts/Common/LtsUtils/LtsUtilsCore/data_modification_enum.h deleted file mode 100755 index c2a316a43..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/data_modification_enum.h +++ /dev/null @@ -1,30 +0,0 @@ - - - -#pragma once - -#include - -enum class DataModification -{ - eAdded, - eModified, - eRemoved -}; - -inline std::ostream &operator<<(std::ostream &str, DataModification mod) -{ - switch(mod) - { - case DataModification::eAdded: - str << "DataModification::eAdded"; - break; - case DataModification::eModified: - str << "DataModification::eModified"; - break; - case DataModification::eRemoved: - str << "DataModification::eRemoved"; - break; - } - return str; -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/dds_bitarray.h b/Lts/Common/LtsUtils/LtsUtilsCore/dds_bitarray.h deleted file mode 100755 index a26a6c73a..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/dds_bitarray.h +++ /dev/null @@ -1,98 +0,0 @@ - - - -#pragma once - -#ifdef SUPPORT_QBITARRAY -# include -#endif - -namespace LtsUtils -{ - -namespace BitArray -{ -constexpr inline int bitNumber(int position) -{ - return 7 - (position % 8); - -} - -template -inline bool testBit(const T &bitField, int position, bool *oob = nullptr) -{ - const size_t byteNum = position / 8; - if (byteNum >= bitField.size()) - { - if (oob) - *oob = true; - return false; - } - if (oob) - *oob = false; - - const typename T::value_type mask = 1 << bitNumber(position); - return ((bitField.at(byteNum) & mask) == mask); -} - -template -inline bool setBit(T &bitField, int position, bool set = true) -{ - const size_t byteNum = position / 8; - if (byteNum >= bitField.size()) - return false; - - const typename T::value_type mask = 1 << bitNumber(position); - if (set) - bitField[byteNum] |= mask; - else - bitField[byteNum] &= ~mask; - - return true; -} - -template -inline bool clearBit(T &bitField, int position) -{ - return setBit(bitField, position, false); -} - - -#ifdef SUPPORT_QBITARRAY -template -inline T convert(const QBitArray &bitArray) -{ - T ret; - ret.resize((bitArray.size() + 7) / 8, 0); - - for (int i = 0; i < bitArray.size(); ++i) - { - const size_t byteNum = i / 8; - const typename T::value_type mask = (1 << bitNumber(i)); - if (bitArray[i]) - ret[byteNum] |= mask; - else - ret[byteNum] &= ~mask; - } - return ret; -} - -template -inline QBitArray convert(T &bitField, int realSize) -{ - QBitArray ba(realSize); - for (int32_t i = 0; i < realSize; ++i) - { - const size_t byteNum = i / 8; - const typename T::value_type mask = (1 << bitNumber(i)); - bool bit = ((bitField[byteNum] & mask) == mask); - ba[i] = bit; - } - return ba; -} - -#endif - -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector.h b/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector.h deleted file mode 100644 index 232831401..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector.h +++ /dev/null @@ -1,35 +0,0 @@ - - -#pragma once - -#include -#include - -namespace LtsUtils { - -template inline void convert(const S &in, T &out) { - static_assert(!std::is_same_v, - "Try to convert a type to the same type - is useless"); - out.reserve(static_cast(in.size())); - std::copy(in.begin(), in.end(), std::back_inserter(out)); -} - -template inline T convert(const S &in) { - static_assert(!std::is_same_v, - "Try to convert a type to the same type - is useless"); - T out; - convert(in, out); - return out; -} - -template inline QByteArray toQByteArray(const T &in) { - return convert(in); -} - -template -bool isInVector(const T &vec, const V &valueToSearch) { - const auto end = vec.end(); - return std::find(vec.begin(), end, valueToSearch) != end; -} - -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector_to_pixmap.h b/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector_to_pixmap.h deleted file mode 100755 index f94966b45..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/dds_vector_to_pixmap.h +++ /dev/null @@ -1,24 +0,0 @@ - - - -#pragma once - -#include -#include -#include - -class QByteArray; -class QPixmap; -class QImage; - -namespace LtsUtils -{ - -LTSUTILSCORE_EXPORT QPixmap getPixmap(const std::vector &data); -LTSUTILSCORE_EXPORT QPixmap getPixmap(const QByteArray &data); -LTSUTILSCORE_EXPORT QImage getImage(const std::vector &data); -LTSUTILSCORE_EXPORT QImage getImage(const QByteArray &data); -LTSUTILSCORE_EXPORT std::vector fromPixmap(const QPixmap &pixmap, bool *bIsPng = nullptr); -LTSUTILSCORE_EXPORT std::vector fromImage(const QImage &image, bool *bIsPng = nullptr); - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/json.h b/Lts/Common/LtsUtils/LtsUtilsCore/json.h deleted file mode 100755 index c91ae03cc..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/json.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/json_object.h b/Lts/Common/LtsUtils/LtsUtilsCore/json_object.h deleted file mode 100644 index ec73d43f1..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/json_object.h +++ /dev/null @@ -1,519 +0,0 @@ - - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsUtils { - -class JsonParseError : public std::runtime_error { -public: - JsonParseError(const QJsonParseError &err) - : std::runtime_error( - "json parse error: " + err.errorString().toStdString() + - " - at offset " + std::to_string(err.offset)) {} -}; - -class JsonError : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -class JsonKeyError : public JsonError { -public: - using JsonError::JsonError; -}; - -class JsonRangeError : public JsonError { -public: - using JsonError::JsonError; -}; - -class JsonTypeError : public JsonError { -public: - using JsonError::JsonError; -}; - -class JsonObject { -public: - class Key; - - JsonObject() = default; - explicit JsonObject(const QJsonObject &json) - : m_json(json), m_jsonIsSet(true) {} - - explicit JsonObject(QJsonObject &&json) - : m_json(std::move(json)), m_jsonIsSet(true) {} - - static inline JsonObject fromJson(const char *json, int len = -1); - static inline JsonObject fromJson(const QByteArray &json) { - return fromJson(json.data(), json.size()); - } - static inline JsonObject fromJson(const std::string &json) { - return fromJson(json.c_str(), json.size()); - } - - static inline JsonObject fromJsonSafe(const char *json, - int len = -1) noexcept(false); - static inline JsonObject - fromJsonSafe(const QByteArray &json) noexcept(false) { - return fromJsonSafe(json.data(), json.size()); - } - static inline JsonObject - fromJsonSafe(const std::string &json) noexcept(false) { - return fromJsonSafe(json.c_str(), json.size()); - } - - template [[nodiscard]] bool contains(const Key &key) const; - - [[nodiscard]] bool isNull(const Key &key) const { - return contains(key); - } - - [[nodiscard]] bool isEmpty() const { return m_json.isEmpty(); } - [[nodiscard]] bool isValid() const { return m_jsonIsSet; } - - template T value(const Key &key) const noexcept(false); - - template - T valueOr(const Key &key, const T &defaultValue) const noexcept(false); - - template [[nodiscard]] bool isMap() const; - - template QMap toMap() const noexcept(false); - - template std::map toStdMap() const; - - bool operator==(const JsonObject &other) const { - return m_json == other.m_json; - } - bool operator!=(const JsonObject &other) const { return !(*this == other); } - - class Key { - public: - Key(QString s) : m_s(std::move(s)) {} - - Key(const std::string &s) : m_s(QString::fromStdString(s)) {} - - Key(const char *s) : m_s(QString::fromUtf8(s)) {} - - operator const QString &() const { return m_s; } - - private: - const QString m_s; - }; - - operator const QJsonObject &() const { return m_json; } - [[nodiscard]] const QJsonObject &object() const { return m_json; } - -private: - [[nodiscard]] bool containsAny(const QString &key) const { - return m_json.contains(key); - } - - QJsonObject m_json; - bool m_jsonIsSet = false; -}; - -JsonObject JsonObject::fromJson(const char *json, int len) { - if (len < 0) - len = strlen(json); - QJsonParseError error; - const auto doc = - QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); - if (error.error != QJsonParseError::NoError) - return {}; - - if (!doc.isObject()) - return {}; - - return JsonObject(doc.object()); -} - -JsonObject JsonObject::fromJsonSafe(const char *json, int len) noexcept(false) { - if (len < 0) - len = strlen(json); - QJsonParseError error; - const auto doc = - QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); - if (error.error != QJsonParseError::NoError) - throw JsonParseError(error); - - if (!doc.isObject()) - throw JsonTypeError("JSON root is not an object"); - - return JsonObject(doc.object()); -} - -class JsonArray { -public: - JsonArray() = default; - explicit JsonArray(const QJsonArray &json) - : m_json(json), m_jsonIsSet(true) {} - - explicit JsonArray(QJsonArray &&json) - : m_json(std::move(json)), m_jsonIsSet(true) {} - - static inline JsonArray fromJson(const char *json, int len = -1); - static inline JsonArray fromJson(const QByteArray &json) { - return fromJson(json.data(), json.size()); - } - static inline JsonArray fromJson(const std::string &json) { - return fromJson(json.c_str(), json.size()); - } - - static inline JsonArray fromJsonSafe(const char *json, - int len = -1) noexcept(false); - static inline JsonArray fromJsonSafe(const QByteArray &json) noexcept(false) { - return fromJson(json.data(), json.size()); - } - static inline JsonArray - fromJsonSafe(const std::string &json) noexcept(false) { - return fromJson(json.c_str(), json.size()); - } - - [[nodiscard]] int size() const { return m_json.size(); } - [[nodiscard]] bool isEmpty() const { return m_json.isEmpty(); } - [[nodiscard]] bool isValid() const { return m_jsonIsSet; } - - template - [[nodiscard]] bool contains(int index) const noexcept(false); - - [[nodiscard]] bool isNull(int index) const noexcept(false) { - return contains(index); - } - - template T at(int index) const noexcept(false); - - template [[nodiscard]] bool isVector() const; - - template QVector toVector() const noexcept(false); - - template std::vector toStdVector() const; - - bool operator==(const JsonArray &other) const { - return m_json == other.m_json; - } - bool operator!=(const JsonArray &other) const { return !(*this == other); } - - const QJsonArray &operator()() const { return m_json; } - -private: - QJsonArray m_json; - bool m_jsonIsSet = false; -}; - -JsonArray JsonArray::fromJson(const char *json, int len) { - if (len < 0) - len = strlen(json); - QJsonParseError error; - QJsonDocument doc = - QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); - if (error.error != QJsonParseError::NoError) - return {}; - - if (!doc.isArray()) - return {}; - - return JsonArray(doc.array()); -} - -JsonArray JsonArray::fromJsonSafe(const char *json, int len) noexcept(false) { - if (len < 0) - len = strlen(json); - QJsonParseError error; - const auto doc = - QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); - if (error.error != QJsonParseError::NoError) - throw JsonParseError(error); - - if (!doc.isArray()) - throw JsonTypeError("JSON root is not an array"); - - return JsonArray(doc.array()); -} - -template struct JsonValue; - -template struct JsonLimits { - static constexpr Int lowest() { return std::numeric_limits::lowest(); } - static constexpr Int max() { return std::numeric_limits::max(); } -}; - -template <> struct JsonLimits { - static constexpr int64_t lowest() { - return -(static_cast(1) << 53) + 1; - } - static constexpr int64_t max() { return (static_cast(1) << 53) - 1; } -}; - -template <> struct JsonLimits { - static constexpr uint64_t lowest() { return 0; } - static constexpr uint64_t max() { return JsonLimits::max(); } -}; - -template struct JsonIntValue { - static bool matchesType(const QJsonValue &value) { - if (!value.isDouble()) - return false; - double val = value.toDouble(); - - if (val < JsonLimits::lowest()) - return false; - if (val > JsonLimits::max()) - return false; - - double wholeInt = static_cast(val); - return val == wholeInt; - } - - static Int value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type Int"); - return static_cast(value.toDouble()); - } -}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue : JsonIntValue {}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isDouble(); } - - static double value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type Double"); - return value.toDouble(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isBool(); } - - static bool value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type Bool"); - return value.toBool(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isString(); } - - static QString value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type String"); - return value.toString(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isString(); } - - static std::string value(const QJsonValue &value) { - return JsonValue::value(value).toStdString(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isObject(); } - - static QJsonObject value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type Object"); - return value.toObject(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isObject(); } - static JsonObject value(const QJsonValue &value) { - return JsonObject(JsonValue::value(value)); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isNull(); } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isArray(); } - - static QJsonArray value(const QJsonValue &value) noexcept(false) { - if (!matchesType(value)) - throw JsonTypeError("JSON value not of type Array"); - return value.toArray(); - } -}; - -template <> struct JsonValue { - static bool matchesType(const QJsonValue &value) { return value.isArray(); } - static JsonArray value(const QJsonValue &value) { - return JsonArray(JsonValue::value(value)); - } -}; - -template struct JsonValue> { - static bool matchesType(const QJsonValue &value) { - if (!value.isArray()) - return false; - - const JsonArray array(value.toArray()); - return array.isVector(); - } - - static QVector value(const QJsonValue &value) { - const JsonArray array(JsonValue::value(value)); - return array.toVector(); - } -}; - -template struct JsonValue> { - static bool matchesType(const QJsonValue &value) { - return JsonValue>::matchesType(value); - } - - static std::vector value(const QJsonValue &value) { - const JsonArray array(JsonValue::value(value)); - return array.toStdVector(); - } -}; - -template struct JsonValue> { - static bool matchesType(const QJsonValue &value) { - if (!value.isObject()) - return false; - - const JsonObject obj(JsonValue::value(value)); - return obj.isMap(); - } - - static QMap value(const QJsonValue &value) { - const JsonObject obj(JsonValue::value(value)); - return obj.toMap(); - } -}; - -template struct JsonValue> { - static bool matchesType(const QJsonValue &value) { - return JsonValue>::matchesType(value); - } - - static std::map value(const QJsonValue &value) { - const JsonObject obj(JsonValue::value(value)); - return obj.toStdMap(); - } -}; - -template bool JsonObject::contains(const Key &key) const { - if (!m_json.contains(key)) - return false; - return JsonValue::matchesType(m_json.value(key)); -} - -template T JsonObject::value(const Key &key) const noexcept(false) { - if (!containsAny(key)) - throw JsonKeyError("no element with key: " + - static_cast(key).toStdString()); - try { - return JsonValue::value(m_json.value(key)); - } catch (const JsonTypeError &typeError) { - throw JsonTypeError(std::string(typeError.what()) + " - key: " + - static_cast(key).toStdString()); - } -} - -template -T JsonObject::valueOr(const Key &key, const T &defaultValue) const { - try { - return value(key); - } - - catch (const JsonKeyError &) { - return defaultValue; - } -} - -template bool JsonObject::isMap() const { - const QStringList keys = m_json.keys(); - for (const auto &key : keys) - if (!JsonValue::matchesType(m_json.value(key))) - return false; - - return true; -} - -template QMap JsonObject::toMap() const { - QMap result; - for (auto current = m_json.begin(); current != m_json.end(); ++current) - result.insert(current.key(), JsonValue::value(current.value())); - return result; -} - -template std::map JsonObject::toStdMap() const { - std::map result; - for (auto current = m_json.begin(); current != m_json.end(); ++current) - result.insert( - {current.key().toStdString(), JsonValue::value(current.value())}); - return result; -} - -template bool JsonArray::contains(int index) const noexcept(false) { - if (index < 0 || index >= m_json.size()) - throw JsonRangeError("index out of range: " + std::to_string(index)); - return JsonValue::matchesType(m_json.at(index)); -} - -template T JsonArray::at(int index) const noexcept(false) { - if (index < 0 || index >= m_json.size()) - throw JsonRangeError("index out of range: " + std::to_string(index)); - return JsonValue::value(m_json.at(index)); -} - -template bool JsonArray::isVector() const { - for (const QJsonValue &value : m_json) - if (!JsonValue::matchesType(value)) - return false; - return true; -} - -template QVector JsonArray::toVector() const { - QVector result; - result.reserve(m_json.size()); - for (const QJsonValue &value : m_json) - result.push_back(JsonValue::value(value)); - return result; -} - -template std::vector JsonArray::toStdVector() const { - std::vector result; - result.reserve(m_json.size()); - for (const QJsonValue &value : m_json) - result.push_back(JsonValue::value(value)); - return result; -} - -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.cpp deleted file mode 100755 index 5bd5cb61d..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "lts_portrange.h" - -namespace LtsUtils -{ - -LtsUtils::LtsPortRange fromQPoint(const QPoint &point) -{ - if (point.x() < 1025 || point.y() > 65535 || point.x() > point.y()) - return {}; - - return {static_cast(point.x()), static_cast(point.y())}; -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.h b/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.h deleted file mode 100755 index 43d8f364a..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/lts_portrange.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace LtsUtils -{ - typedef QPair LtsPortRange; - - LTSUTILSCORE_EXPORT LtsPortRange fromQPoint(const QPoint &point); -} - - diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.cpp deleted file mode 100755 index 239a14cc4..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.cpp +++ /dev/null @@ -1,25 +0,0 @@ - - - -#include "lts_translatable_identifier.h" - -#include - - -namespace LtsUtils -{ -LtsTranslatableIdentifier::LtsTranslatableIdentifier(const QString &identifier, const QObject *context, - const char *translatableText) - : m_identifier(identifier) - , m_translationContext(context->metaObject()->className()) - , m_translatableText(translatableText) -{} - - -const QString LtsTranslatableIdentifier::translatedText(const char *disambiguation, int n) const -{ - if (m_identifier.isEmpty() || m_translationContext == nullptr || m_translatableText == nullptr) - return {}; - return QCoreApplication::translate(m_translationContext, m_translatableText, disambiguation, n); -} -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.h b/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.h deleted file mode 100755 index 84a4a3bec..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/lts_translatable_identifier.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - - - - -#include - -#include -#include - - -namespace LtsUtils -{ -class LTSUTILSCORE_EXPORT LtsTranslatableIdentifier -{ -public: - LtsTranslatableIdentifier() - : m_translationContext(nullptr) - , m_translatableText("") - {} - - LtsTranslatableIdentifier(const QString &identifier, const QObject *context, const char *translatableText); - const QString &identifier() const { return m_identifier; } - const QString translatedText(const char *disambiguation = nullptr, int n = -1) const; - const QString _context() const { return QString::fromUtf8(m_translationContext); } - -private: - QString m_identifier; - const char *m_translationContext; - - const char *m_translatableText; -}; -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/outdated_position.h b/Lts/Common/LtsUtils/LtsUtilsCore/outdated_position.h deleted file mode 100755 index 20f04b9f4..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/outdated_position.h +++ /dev/null @@ -1,15 +0,0 @@ - - - -#pragma once - -#include - -namespace LtsUtils -{ -inline bool isPositionTimeOutdated(qint64 curTime, qint64 posTime) -{ - - return curTime > posTime + 11 * 60 * 1000; -} -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.cpp deleted file mode 100755 index f99ded658..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.cpp +++ /dev/null @@ -1,169 +0,0 @@ - - - -#include "pgarray.h" -#include -#include - -#include -#include - -namespace LtsUtils -{ - -template -static inline QString convertToPgString(const std::vector &data) -{ - if (data.size() == 0) - return QLatin1String("{}"); - QString ret(QLatin1Char('{')); - for (T i : data) - ret += QString::number(i) + QLatin1Char(','); - ret.chop(1); - ret += QLatin1Char('}'); - return ret; -} - -template<> -LTSUTILSCORE_EXPORT QString IntArray::toPgString() const -{ - return convertToPgString(m_data); -} - -template<> -LTSUTILSCORE_EXPORT QString BigintArray::toPgString() const -{ - return convertToPgString(m_data); -} - -template -static inline T convertFromPgString(const QString &str, const std::function &convertFunc) -{ - typename T::ContainerType result; - - int idxStart = str.indexOf(QLatin1Char('{')); - int idxEnd = str.lastIndexOf(QLatin1Char('}')); - if (idxEnd < idxStart) - return T(result); - if (idxStart < 0 && idxEnd < 0) - { - - idxStart = -1; - idxEnd = str.length() + 1; - } - - const QStringRef strRef = str.midRef(idxStart + 1, idxEnd - idxStart - 1); - const QVector vec = strRef.split(QLatin1Char(','), QString::SkipEmptyParts); - result.reserve(vec.size()); - for (const QStringRef &s : vec) - { - bool ok = false; - const typename T::Type i = convertFunc(s, &ok); - if (ok) - result.push_back(i); - } - - return T(result); -} - -template<> -LTSUTILSCORE_EXPORT IntArray IntArray::fromPgString(const QString &str) -{ - return convertFromPgString(str, - [](const QStringRef & val, bool * bOk) { return val.toInt(bOk);}); -} - -template<> -LTSUTILSCORE_EXPORT BigintArray BigintArray::fromPgString(const QString &str) -{ - return convertFromPgString(str, - [](const QStringRef & val, bool * bOk) { return val.toLongLong(bOk);}); -} - - - - - - -template -static inline QString convertToPgString(const std::map &data) -{ - if (data.size() == 0) - return QLatin1String("{}"); - QString ret(QLatin1Char('{')); - for (const auto &it : data) - { - ret += QLatin1Char('{') + QString::number(it.first) + - QLatin1Char(',') + QString::number(it.second) + QLatin1String("},"); - } - ret.chop(1); - ret += QLatin1Char('}'); - return ret; -} - -template<> -LTSUTILSCORE_EXPORT QString IntArray2D::toPgString() const -{ - return convertToPgString(m_data); -} - -template<> -LTSUTILSCORE_EXPORT QString BigintArray2D::toPgString() const -{ - return convertToPgString(m_data); -} - - -template -static inline T convertFromPgString2D(const QString &str, const std::function &convertFunc) -{ - typename T::ContainerType ret; - int idxStart = str.indexOf(QLatin1Char('{')); - int idxEnd = str.lastIndexOf(QLatin1Char('}')); - if (idxEnd < idxStart) - return T(ret); - if (idxStart < 0 && idxEnd < 0) - { - - idxStart = -1; - idxEnd = str.length() + 1; - } - - const QStringRef strRef = str.midRef(idxStart + 1, idxEnd - idxStart - 1); - idxStart = strRef.indexOf(QLatin1Char('{')); - while (idxStart >= 0) - { - idxEnd = strRef.indexOf(QLatin1Char('}'), idxStart); - if (idxEnd < 0) - break; - - const QStringRef keyValueStr = strRef.mid(idxStart + 1, idxEnd - idxStart - 1); - const QVector keyValuePair = keyValueStr.split(QLatin1Char(','), QString::SkipEmptyParts); - if (keyValuePair.size() != 2) - break; - const typename T::Type key = convertFunc(keyValuePair.at(0).trimmed()); - const typename T::Type value = convertFunc(keyValuePair.at(1).trimmed()); - ret.insert(std::make_pair(key, value)); - - - idxStart = strRef.indexOf(QLatin1Char('{'), idxEnd + 1); - } - - return T(ret); -} - -template<> -LTSUTILSCORE_EXPORT IntArray2D IntArray2D::fromPgString(const QString &str) -{ - return convertFromPgString2D(str, - [](const QStringRef & val) { return val.toInt();}); -} - -template<> -LTSUTILSCORE_EXPORT BigintArray2D BigintArray2D::fromPgString(const QString &str) -{ - return convertFromPgString2D(str, - [](const QStringRef & val) { return val.toLongLong();}); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.h b/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.h deleted file mode 100644 index d40eb48a1..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/pgarray.h +++ /dev/null @@ -1,34 +0,0 @@ - - -#pragma once -#include -#include -#include -#include - -namespace LtsUtils { - -template class PgArray { -public: - using Type = T; - using ContainerType = C; - - PgArray() = default; - Q_DECL_CONSTEXPR explicit PgArray(const C &data) : m_data(data) {} - - Q_DECL_CONSTEXPR C operator()() const { return m_data; } - - [[nodiscard]] QString toPgString() const; - static PgArray fromPgString(const QString &str); - -private: - C m_data; -}; - -using IntArray = PgArray>; -using BigintArray = PgArray>; - -using IntArray2D = PgArray>; -using BigintArray2D = PgArray>; - -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/promise.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/promise.cpp deleted file mode 100755 index 0fc9bfd5c..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/promise.cpp +++ /dev/null @@ -1,103 +0,0 @@ - - - -#include "promise.h" - -#include - -namespace LtsUtils -{ - -Promise::Promise(const std::function &function, QObject *parent) - : QObject(parent) - , m_functionAlreadySet(false) - , m_timeout(new QTimer(this)) - , m_resultSet(false) - , m_timeoutSet(false) -{ - m_timeout->setSingleShot(true); - connect(m_timeout, &QTimer::timeout, - this, &Promise::slotTimeout); - setFunction(function); -} - - -void Promise::setFunction(const std::function &function) -{ - if (!function || m_functionAlreadySet) - return; - - m_functionAlreadySet = true; - - if (m_timeoutSet) - m_timeout->start(); - - - QTimer::singleShot(0, this, [this, function]() - { - - function([this](const QVariant &value) - { - resolve(value); - }, [this](const QVariant &value) - { - - reject(value); - }); - }); -} - - -void Promise::setTimeout(int interval) -{ - m_timeout->stop(); - m_timeout->setInterval(interval); - m_timeoutSet = (interval != 0); - - if (interval != 0 && m_functionAlreadySet) - m_timeout->start(); -} - - -void Promise::slotTimeout() -{ - reject(QStringLiteral("timeout")); -} - - -void Promise::resolve(const QVariant &value) -{ - if (m_resultSet) - return; - m_resultSet = true; - - m_timeout->stop(); - m_resolveFn(value); -} - - -void Promise::reject(const QVariant &value) -{ - if (m_resultSet) - return; - m_resultSet = true; - - m_timeout->stop(); - m_rejectFn(value); -} - - -Promise &Promise::resolved(ResultFunction resolveFn) -{ - m_resolveFn = resolveFn; - return *this; -} - - -Promise &Promise::rejected(ResultFunction rejectFn) -{ - m_rejectFn = rejectFn; - return *this; -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/promise.h b/Lts/Common/LtsUtils/LtsUtilsCore/promise.h deleted file mode 100755 index 7bb4e2c2c..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/promise.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - - - - -#include - -#include -#include - -#include - - -class QTimer; - - -namespace LtsUtils -{ -typedef std::function RawResultFunction; -typedef const RawResultFunction &ResultFunction; - - - - -class LTSUTILSCORE_EXPORT Promise : public QObject -{ - Q_OBJECT - -public: - - - Promise(const std::function &function, QObject *parent = nullptr); - - - - void setFunction(const std::function &function); - - - - void resolve(const QVariant &value = QVariant()); - - - - void reject(const QVariant &value = QVariant()); - - - - void setTimeout(int interval); - - - - Promise &resolved(ResultFunction resolveFn); - - - - Promise &rejected(ResultFunction rejectFn); - - -private: - RawResultFunction m_resolveFn; - RawResultFunction m_rejectFn; - bool m_functionAlreadySet; - QTimer *m_timeout; - bool m_resultSet; - bool m_timeoutSet; - - -private slots: - void slotTimeout(); -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.cpp deleted file mode 100755 index 4fa512d34..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "promise_examples.h" -#include "promise.h" - -#include -#include - - -#include - - -namespace LtsUtils -{ - - -void PromiseExample1_Class::doSomethingAsync1() -{ - QTimer::singleShot(200, this, [this]() - { - emit signalDone1(true); - }); -} - - -void PromiseExample1_Class::doSomethingAsync2() -{ - QTimer::singleShot(450, this, [this]() - { - emit signalDone2(true, 1234); - }); -} - - -PromiseExample1::PromiseExample1(QObject *parent) - : QObject(parent) - , m_success(false) -{ - - - - auto func = [this](ResultFunction resolve - , ResultFunction reject ) - { - - PromiseExample1_Class *c = new PromiseExample1_Class(this); - - - connect(c, &PromiseExample1_Class::signalDone1, - this, [this, c, resolve, reject](bool didThisWork) - { - - if (!didThisWork) - { - - reject(QStringLiteral("First Action did not succeed")); - return; - } - else - { - - - - connect(c, &PromiseExample1_Class::signalDone2, - this, [resolve, reject](bool didThisWork, int someNumberThatReturns) - { - if (didThisWork) - resolve(someNumberThatReturns); - else - reject(QStringLiteral("Second Action did not succeed")); - - - }); - - c->doSomethingAsync2(); - } - }); - - c->doSomethingAsync1(); - }; - Promise *p = new Promise(func, this); - - - - - - p->resolved([this](const QVariant &value) - { - - qDebug() << "example1 promise succeeded! 'someNumberThatReturns' was" << value; - - m_success = true; - - }).rejected([](const QVariant &value) - { - - qDebug() << "example1 promise failed.." << value; - }); -} - - - - -PromiseExample2::PromiseExample2(QObject *parent) - : QObject(parent) - , m_success(false) -{ - - - - Promise *p = new Promise([](ResultFunction , ResultFunction ) - { - - }, this); - - p->setTimeout(1000); - - p->resolved([](const QVariant &) - { - QFAIL("This test case's promise should never resolve"); - }); - - p->rejected([this](const QVariant &val) - { - QVERIFY2(val == QStringLiteral("timeout"), "Promise didn't reject with string \"timeout\""); - qDebug() << "Promise rejected as expected"; - m_success = true; - }); -} - - - - -PromiseExample3::PromiseExample3(QObject *parent) - : QObject(parent) - , m_success(true) - , m_alreadyRejected(false) -{ - - - - Promise *p = new Promise([](ResultFunction , ResultFunction reject) - { - reject(QString()); - }, this); - - p->setTimeout(1000); - - p->resolved([](const QVariant &) - { - QFAIL("This test case's promise should never resolve"); - }); - - p->rejected([this](const QVariant &val) - { - qDebug() << "rejected with val" << val; - QVERIFY2(val == QString(), "Promise didn't reject with an expected empty string."); - QVERIFY2(!m_alreadyRejected, "Promise rejected more than once"); - - m_alreadyRejected = true; - qDebug() << "Promise rejected as expected"; - }); -} - - - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.h b/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.h deleted file mode 100644 index 234df6bb5..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/promise_examples.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include - -namespace LtsUtils { - -class PromiseExample1_Class : public QObject { - Q_OBJECT -public: - using QObject::QObject; - - void doSomethingAsync1(); - void doSomethingAsync2(); - -signals: - void signalDone1(bool didThisWork); - void signalDone2(bool didThisWork, int someNumberThatReturns); -}; - -class PromiseExample1 : public QObject { - Q_OBJECT -public: - explicit PromiseExample1(QObject *parent = nullptr); - - [[nodiscard]] bool isSuccess() const { return m_success; } - -private: - bool m_success; -}; - -class PromiseExample2 : public QObject { - Q_OBJECT -public: - explicit PromiseExample2(QObject *parent = nullptr); - - [[nodiscard]] bool isSuccess() const { return m_success; } - -private: - bool m_success; -}; - -class PromiseExample3 : public QObject { - Q_OBJECT -public: - explicit PromiseExample3(QObject *parent = nullptr); - - [[nodiscard]] bool isSuccess() const { return m_success; } - -private: - bool m_success; - bool m_alreadyRejected; -}; - -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/qbytearray_byteaccess.h b/Lts/Common/LtsUtils/LtsUtilsCore/qbytearray_byteaccess.h deleted file mode 100644 index 51956975a..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/qbytearray_byteaccess.h +++ /dev/null @@ -1,241 +0,0 @@ - - -#pragma once - -#include -#include - -namespace LtsUtils { - -template void inline set8Byte(const T in, QByteArray &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - out += (in & 0xFF00000000000000UL) >> 56; - out += (in & 0x00FF000000000000UL) >> 48; - out += (in & 0x0000FF0000000000UL) >> 40; - out += (in & 0x000000FF00000000UL) >> 32; - out += (in & 0x00000000FF000000UL) >> 24; - out += (in & 0x0000000000FF0000UL) >> 16; - out += (in & 0x000000000000FF00UL) >> 8; - out += (in & 0x00000000000000FFUL); -} - -template <> void inline set8Byte(const double in, QByteArray &out) { - union hack { - double d; - int64_t i; - } h; - h.d = in; - set8Byte(h.i, out); -} - -template -bool inline get8Byte(const QByteArray &baData, size_t &pos, T &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - static_assert(sizeof(T) >= 8, - "sizeof(out) is smaller than 8 bytes - will not fit"); - out = T(); - - if (size_t(baData.size()) < pos + 8) - return false; - - const char *data = baData.data() + pos; - - if (std::numeric_limits::is_signed) - out |= static_cast(data[0]) << 56; - else - out |= (data[0] & 0xFFULL) << 56; - out |= (data[1] & 0xFFULL) << 48; - out |= (data[2] & 0xFFULL) << 40; - out |= (data[3] & 0xFFULL) << 32; - out |= (data[4] & 0xFFULL) << 24; - out |= (data[5] & 0xFFULL) << 16; - out |= (data[6] & 0xFFULL) << 8; - out |= (data[7] & 0xFFULL); - - pos += 8; - - return true; -} - -template <> -bool inline get8Byte(const QByteArray &baData, size_t &pos, double &out) { - out = 0.; - union hack { - double d; - int64_t i; - } h; - if (!get8Byte(baData, pos, h.i)) - return false; - out = h.d; - return true; -} - -template bool inline get8Byte(const QByteArray &data, T &out) { - size_t pos = 0; - return get8Byte(data, pos, out); -} - -template void inline set4Byte(const T in, QByteArray &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - out += (in & 0xFF000000) >> 24; - out += (in & 0x00FF0000) >> 16; - out += (in & 0x0000FF00) >> 8; - out += (in & 0x000000FF); -} - -template <> void inline set4Byte(const double in, QByteArray &out) { - int tmp = qRound(in); - return set4Byte(tmp, out); -} - -template -bool inline get4Byte(const QByteArray &baData, size_t &pos, T &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - static_assert(sizeof(T) >= 4, - "sizeof(out) is smaller than 4 bytes - will not fit"); - out = T(); - - if (size_t(baData.size()) < pos + 4) - return false; - - const char *data = baData.data() + pos; - - if (std::numeric_limits::is_signed) - out |= data[0] << 24; - else - out |= (data[0] & 0xFFU) << 24; - out |= (data[1] & 0xFFU) << 16; - out |= (data[2] & 0xFFU) << 8; - out |= (data[3] & 0xFFU); - - pos += 4; - - return true; -} - -template <> -bool inline get4Byte(const QByteArray &baData, size_t &pos, double &out) { - out = 0.; - int tmp; - if (!get4Byte(baData, pos, tmp)) - return false; - out = tmp; - return true; -} - -template bool inline get4Byte(const QByteArray &data, T &out) { - size_t pos = 0; - return get4Byte(data, pos, out); -} - -template void inline set3Byte(const T in, QByteArray &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - out += (in & 0x00FF0000) >> 16; - out += (in & 0x0000FF00) >> 8; - out += (in & 0x000000FF); -} - -template -bool inline get3Byte(const QByteArray &baData, size_t &pos, T &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - static_assert(sizeof(T) >= 3, - "sizeof(out) is smaller than 3 bytes - will not fit"); - out = T(); - - if (size_t(baData.size()) < pos + 3) - return false; - - const char *data = baData.data() + pos; - - if (std::numeric_limits::is_signed) - out |= data[0] << 16; - else - out |= (data[0] & 0xFFU) << 16; - out |= (data[1] & 0xFFU) << 8; - out |= (data[2] & 0xFFU); - - pos += 3; - - return true; -} - -template bool inline get3Byte(const QByteArray &data, T &out) { - size_t pos = 0; - return get3Byte(data, pos, out); -} - -template void inline set2Byte(const T in, QByteArray &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - out += (in & 0x0000FF00) >> 8; - out += (in & 0x000000FF); -} - -template -bool inline get2Byte(const QByteArray &baData, size_t &pos, T &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - static_assert(sizeof(T) >= 2, - "sizeof(out) is smaller than 2 bytes - will not fit"); - out = T(); - - if (size_t(baData.size()) < pos + 2) - return false; - - const char *data = baData.data() + pos; - - if (std::numeric_limits::is_signed) - out |= data[0] << 8; - else - out |= (data[0] & 0xFFU) << 8; - out |= data[1] & 0xFFU; - - pos += 2; - - return true; -} - -template bool inline get2Byte(const QByteArray &data, T &out) { - size_t pos = 0; - return get2Byte(data, pos, out); -} - -template void inline set1Byte(const T in, QByteArray &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - unsigned char c = in; - out += c; -} - -template -bool inline get1Byte(const QByteArray &baData, size_t &pos, T &out) { - static_assert(std::is_integral_v || std::is_enum_v, - "Only integrals allowed for raw byte accessd"); - out = T(); - - if (size_t(baData.size()) < pos + 1) - return false; - - const char *data = baData.data() + pos; - - if (std::numeric_limits::is_signed) - out = (T)(data[0]); - else - out = (T)(data[0] & 0xFF); - - pos += 1; - - return true; -} - -template bool inline get1Byte(const QByteArray &data, T &out) { - size_t pos = 0; - return get1Byte(data, pos, out); -} -} // namespace LtsUtils diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.cpp deleted file mode 100755 index 2e2435096..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "qplane3d.h" - -#include - - -namespace LtsUtils -{ - - - - - -QPlane3D::QPlane3D(const QPoint3D &pos, const QPoint3D &vec1, const QPoint3D &vec2) - : m_pos(pos) - , m_vec1(vec1) - , m_vec2(vec2) -{ - m_normal = QPoint3D::crossProduct(m_vec1, m_vec2); - m_planeC = QPoint3D::dotProduct(m_pos, m_normal); -} - - -QPoint3D QPlane3D::intersection(const QPoint3D &point, const QPoint3D &direction) const -{ - const double dotProduct = QPoint3D::dotProduct(direction, m_normal); - if (qFuzzyIsNull(dotProduct)) - return inPlane(point) ? point : QPoint3D(); - const double distance = distanceTo(point) / std::abs(dotProduct); - return point + distance * direction; -} - -bool QPlane3D::intersects(const QPoint3D &direction) const -{ - return !isParallel(direction); -} - -bool QPlane3D::isParallel(const QPoint3D &direction) const -{ - return qFuzzyIsNull(QPoint3D::dotProduct(m_normal, direction)); -} - -bool QPlane3D::inPlane(const QPoint3D &point) const -{ - if (point == m_pos) - return true; - return qFuzzyIsNull(QPoint3D::dotProduct(m_normal, point - m_pos)); -} - -double QPlane3D::distanceTo(const QPoint3D &point) const -{ - return QPoint3D::dotProduct(point - m_pos, m_normal); -} - - - - - - -QPolygon3D::QPolygon3D(const QVector &vertices) - : QPlane3D(vertices[0], vertices[1], vertices[2]) - , m_vertices(vertices) -{ -} - - -bool QPolygon3D::containsPoint(const QPoint3D &point) const -{ - if (!inPlane(point)) - return false; - - if (contains(point)) - return true; - - double angleSum = 0.; - double cosTheta = 0.; - const int numberOfVertices = m_vertices.size(); - for (int i = 0; i < m_vertices.size(); ++i) - { - const QPoint3D &first = m_vertices[i % numberOfVertices] - point; - const QPoint3D &second = m_vertices[(i + 1) % numberOfVertices] - point; - - cosTheta = QPoint3D::dotProduct(first, second) / (first.length() * second.length()); - angleSum += acos(cosTheta); - } - return qFuzzyIsNull(M_2_PI - angleSum); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.h b/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.h deleted file mode 100755 index f9abd932c..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/qplane3d.h +++ /dev/null @@ -1,59 +0,0 @@ - - - -#pragma once - -#include "qpoint3d.h" - -#include - -#include -#include - - -namespace LtsUtils -{ - -class LTSUTILSCORE_EXPORT QPlane3D -{ - -public: - QPlane3D(const QPoint3D &pos, const QPoint3D &vec1, const QPoint3D &vec2); - - QPoint3D intersection(const QPoint3D &point, const QPoint3D &direction) const; - bool intersects(const QPoint3D &direction) const; - bool isParallel(const QPoint3D &direction) const; - bool inPlane(const QPoint3D &point) const; - double distanceTo(const QPoint3D &point) const; - - const QPoint3D &pos() const { return m_pos; } - const QPoint3D &vec1() const { return m_vec1; } - const QPoint3D &vec2() const { return m_vec2; } - const QPoint3D &normal() const { return m_normal; } - double planeC() const { return m_planeC; } - -protected: - QPoint3D m_pos; - QPoint3D m_vec1; - QPoint3D m_vec2; - QPoint3D m_normal; - double m_planeC; - -}; - - -class LTSUTILSCORE_EXPORT QPolygon3D : public QPlane3D -{ - -public: - QPolygon3D(const QVector &vertices); - - bool contains(const QPoint3D &point) const { return m_vertices.contains(point); } - bool containsPoint(const QPoint3D &point) const; - -private: - QVector m_vertices; - -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.cpp deleted file mode 100755 index dc4fc26c6..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.cpp +++ /dev/null @@ -1,48 +0,0 @@ - - - -#include "qpoint3d.h" -#include - -using namespace LtsUtils; - - -double QPoint3D::distanceToLine(const QPoint3D &point, const QPoint3D &direction) const -{ - if (direction.isNull()) - return (*this - point).length(); - QPoint3D p = point + dotProduct(*this - point, direction) * direction; - return (*this - p).length(); -} - -double QPoint3D::distanceToPlane(const QPoint3D &plane, const QPoint3D &normal) const -{ - return dotProduct(*this - plane, normal); -} - -double QPoint3D::distanceToPlane(const QPoint3D &plane1, const QPoint3D &plane2, const QPoint3D &plane3) const -{ - QPoint3D n = normal(plane2 - plane1, plane3 - plane1); - return dotProduct(*this - plane1, n); -} - -double QPoint3D::distanceToPoint(const QPoint3D &point) const -{ - return (*this - point).length(); -} - - -QDataStream &operator<<(QDataStream &s, const QPoint3D &p) -{ - s << p.x() << p.y() << p.z(); - return s; -} - -QDataStream &operator>>(QDataStream &s, QPoint3D &p) -{ - double x, y, z; - s >> x; p.rx() = x; - s >> y; p.ry() = y; - s >> z; p.rz() = z; - return s; -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.h b/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.h deleted file mode 100755 index e0398a44f..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/qpoint3d.h +++ /dev/null @@ -1,296 +0,0 @@ - - - -#pragma once - -#include -#include - -#include - -#include -#include - - -namespace LtsUtils -{ - -class QPoint3D -{ -public: - enum class Axis { X, Y, Z }; - - Q_DECL_CONSTEXPR QPoint3D() = default; - Q_DECL_CONSTEXPR QPoint3D(double xpos, double ypos, double zpos); - template - explicit QPoint3D(const T &data) - : xp(data.offsetX()) - , yp(data.offsetY()) - , zp(data.offsetZ()) - {} - - inline bool isNull() const; - - Q_DECL_CONSTEXPR inline double x() const; - Q_DECL_CONSTEXPR inline double y() const; - Q_DECL_CONSTEXPR inline double z() const; - Q_DECL_RELAXED_CONSTEXPR inline void setX(double xpos); - Q_DECL_RELAXED_CONSTEXPR inline void setY(double ypos); - Q_DECL_RELAXED_CONSTEXPR inline void setZ(double zpos); - - Q_DECL_RELAXED_CONSTEXPR inline double &rx(); - Q_DECL_RELAXED_CONSTEXPR inline double &ry(); - Q_DECL_RELAXED_CONSTEXPR inline double &rz(); - - Q_DECL_CONSTEXPR inline QPointF toPointF() const; - static QPoint3D fromQPointF(const QPointF &p) - { - return QPoint3D(p.x(), p.y(), .0); - } - - inline double lengthSquared() const { return (xp * xp) + (yp * yp) + (zp * zp); } - inline double length() const { return std::sqrt(lengthSquared()); } - inline void normalize() - { - double len = xp * xp + yp * yp + zp * zp; - if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) - return; - - len = std::sqrt(len); - - xp /= len; - yp /= len; - zp /= len; - } - inline QPoint3D normalized() const - { - double len = xp * xp + yp * yp + zp * zp; - if (qFuzzyIsNull(len - 1.0f)) - return *this; - else if (!qFuzzyIsNull(len)) - return *this / std::sqrt(len); - else - return QPoint3D(*this); - } - - inline void rotate(const double &phi, const Axis &axis) - { - const double sin = std::sin(phi); - const double cos = std::cos(phi); - switch (axis) - { - case Axis::X: - { - const double _yp = (cos * yp) - (sin * zp); - const double _zp = (sin * yp) + (cos * zp); - yp = _yp; - zp = _zp; - break; - } - case Axis::Y: - { - const double _xp = (cos * xp) + (sin * zp); - const double _zp = (-sin * xp) + (cos * zp); - xp = _xp; - zp = _zp; - break; - } - case Axis::Z: - { - const double _xp = (cos * xp) - (sin * yp); - const double _yp = (sin * xp) + (cos * yp); - xp = _xp; - yp = _yp; - break; - } - } - } - inline QPoint3D rotated(const double &phi, const Axis &axis) const - { - QPoint3D p(*this); - p.rotate(phi, axis); - return p; - } - - inline double distanceToLine(const QPoint3D &point, const QPoint3D &direction) const; - inline double distanceToPlane(const QPoint3D &plane, const QPoint3D &normal) const; - inline double distanceToPlane(const QPoint3D &plane1, const QPoint3D &plane2, const QPoint3D &plane3) const; - inline double distanceToPoint(const QPoint3D &point) const; - - friend Q_DECL_CONSTEXPR inline bool operator==(const QPoint3D &p1, const QPoint3D &p2); - friend Q_DECL_CONSTEXPR inline bool operator!=(const QPoint3D &p1, const QPoint3D &p2); - friend Q_DECL_CONSTEXPR inline QPoint3D operator +(const QPoint3D &p1, const QPoint3D &p2) - { - return QPoint3D(p1.xp + p2.xp, p1.yp + p2.yp, p1.zp + p2.zp); - } - - friend Q_DECL_CONSTEXPR inline QPoint3D operator -(const QPoint3D &p1, const QPoint3D &p2) - { - return QPoint3D(p1.xp - p2.xp, p1.yp - p2.yp, p1.zp - p2.zp); - } - - friend Q_DECL_CONSTEXPR inline QPoint3D operator *(const QPoint3D &p1, double factor) - { - return QPoint3D(p1.xp * factor, p1.yp * factor, p1.zp * factor); - } - friend Q_DECL_CONSTEXPR inline QPoint3D operator *(double factor, const QPoint3D &p1) - { - return QPoint3D(p1.xp * factor, p1.yp * factor, p1.zp * factor); - } - - friend Q_DECL_CONSTEXPR inline QPoint3D operator /(const QPoint3D &p1, double factor) - { - return QPoint3D(p1.xp / factor, p1.yp / factor, p1.zp / factor); - } - - Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator +=(const QPoint3D &o) - { - xp += o.xp; - yp += o.yp; - zp += o.zp; - return *this; - } - Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator -=(const QPoint3D &o) - { - xp -= o.xp; - yp -= o.yp; - zp -= o.zp; - return *this; - } - Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator *=(double factor) - { - xp *= factor; - yp *= factor; - zp *= factor; - return *this; - } - Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator /=(double divisor) - { - xp /= divisor; - yp /= divisor; - zp /= divisor; - return *this; - } - - Q_DECL_CONSTEXPR static inline double dotProduct(const QPoint3D& v1, const QPoint3D& v2) - { - return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp; - } - - Q_DECL_CONSTEXPR static inline QPoint3D crossProduct(const QPoint3D& v1, const QPoint3D& v2) - { - return QPoint3D(v1.yp * v2.zp - v1.zp * v2.yp, - v1.zp * v2.xp - v1.xp * v2.zp, - v1.xp * v2.yp - v1.yp * v2.xp); - } - - static inline QPoint3D normal(const QPoint3D &v1, const QPoint3D &v2) - { - return crossProduct(v1, v2).normalized(); - } - static inline QPoint3D normal(const QPoint3D &v1, const QPoint3D &v2, const QPoint3D &v3) - { - return crossProduct((v2 - v1), (v3 - v1)).normalized(); - } - - Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QPoint3D& v1, const QPoint3D& v2); - -private: - double xp = 0; - double yp = 0; - double zp = 0; -}; - - - -LTSUTILSCORE_EXPORT QDataStream &operator<<(QDataStream &s, const QPoint3D &p); -LTSUTILSCORE_EXPORT QDataStream &operator>>(QDataStream &s, QPoint3D &p); - - - - -Q_DECL_CONSTEXPR inline QPoint3D::QPoint3D(double xpos, double ypos, double zpos) : xp(xpos), yp(ypos), zp(zpos) { } - -inline bool QPoint3D::isNull() const -{ - return qIsNull(xp) && qIsNull(yp) && qIsNull(zp); -} - -Q_DECL_CONSTEXPR inline double QPoint3D::x() const -{ - return xp; -} - -Q_DECL_CONSTEXPR inline double QPoint3D::y() const -{ - return yp; -} - -Q_DECL_CONSTEXPR inline double QPoint3D::z() const -{ - return zp; -} - -Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setX(double xpos) -{ - xp = xpos; -} - -Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setY(double ypos) -{ - yp = ypos; -} - -Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setZ(double zpos) -{ - zp = zpos; -} - -Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::rx() -{ - return xp; -} - -Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::ry() -{ - return yp; -} - -Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::rz() -{ - return zp; -} - - -Q_DECL_CONSTEXPR inline QPointF QPoint3D::toPointF() const -{ - return QPointF(xp, yp); -} - - -Q_DECL_CONSTEXPR inline bool operator==(const QPoint3D &p1, const QPoint3D &p2) -{ - return p1.xp == p2.xp && p1.yp == p2.yp && p1.zp == p2.zp; -} - -Q_DECL_CONSTEXPR inline bool operator!=(const QPoint3D &p1, const QPoint3D &p2) -{ - return p1.xp != p2.xp || p1.yp != p2.yp || p1.zp != p2.zp; -} - -Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QPoint3D& v1, const QPoint3D& v2) -{ - return ::qFuzzyCompare(v1.xp, v2.xp) && - ::qFuzzyCompare(v1.yp, v2.yp) && - ::qFuzzyCompare(v1.zp, v2.zp); -} - -inline std::ostream& operator<< (std::ostream& stream, const QPoint3D& pos) -{ - stream << "(" << pos.x() << "," << pos.y() << "," << pos.z() << ")"; - return stream; -} - -} - -Q_DECLARE_TYPEINFO(LtsUtils::QPoint3D, Q_MOVABLE_TYPE); diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.cpp deleted file mode 100755 index a8d543e3d..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.cpp +++ /dev/null @@ -1,22 +0,0 @@ - - - -#include "scoped_connection.h" -#include "connection.h" - -namespace LtsUtils -{ - -ScopedConnection::ScopedConnection(IConnection *connection, Qt::ConnectionType connType) - : m_conn(connection) -{ - Q_ASSERT(connection); - m_conn->connect(connType); -} - -ScopedConnection::~ScopedConnection() -{ - m_conn->disconnect(); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.h b/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.h deleted file mode 100755 index 74e6e98af..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/scoped_connection.h +++ /dev/null @@ -1,26 +0,0 @@ - - - -#pragma once - -#include -#include - -namespace LtsUtils -{ - -class IConnection; - - - -class LTSUTILSCORE_EXPORT ScopedConnection -{ -public: - explicit ScopedConnection(IConnection *connection, Qt::ConnectionType = Qt::AutoConnection); - ~ScopedConnection(); - -private: - IConnection *m_conn; -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.cpp b/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.cpp deleted file mode 100755 index a5876f216..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.cpp +++ /dev/null @@ -1,301 +0,0 @@ - - - -#include "sfm_bytemessage_converter.h" - -namespace LtsUtils -{ - - - -static constexpr unsigned char XON = 0x11; -static constexpr unsigned char XOFF = 0x13; -static constexpr unsigned char STX = 0x02; -static constexpr unsigned char ETX = 0x03; -static constexpr unsigned char SUBST = 0x80; - -static constexpr unsigned char SUB_XON = 0x91; -static constexpr unsigned char SUB_XOFF = 0x93; -static constexpr unsigned char SUB_STX = 0x82; -static constexpr unsigned char SUB_ETX = 0x83; -static constexpr unsigned char SUB_SUBST = 0x00; - -static constexpr int HEADER_SIZE = 3; -static constexpr int CHECKSUM_SIZE = 1; -static constexpr int MINIMUM_BYTE_SIZE = 1 + HEADER_SIZE + CHECKSUM_SIZE + 1; - - - -static SfmMessage getSfmMessage(const QByteArray &p_data) -{ - - unsigned short id = 0; - id |= (((unsigned short)p_data[0]) & 0x7F) << 8; - id |= (((unsigned short)p_data[1]) & 0xFF) << 0; - - SfmMessage msg; - msg.setId(id); - msg.setRtr(p_data[0]&0x80); - msg.setPayload(p_data.right(p_data.size() - HEADER_SIZE)); - - return msg; -} - - - -static QByteArray getHeader(const SfmMessage &p_msg) -{ - QByteArray header(2, Qt::Uninitialized); - - header[0] = ((p_msg.id() >> 8) & 0x7f) | (p_msg.rtr() << 7); - - header[1] = (p_msg.id() >> 0) & 0xff; - - return header; -} - - - -static inline char getChecksum(const QByteArray &p_data) -{ - char sum = 0; - - - for (const char c : p_data) - sum += c; - - - return sum ^ 0xff; -} - - - -static QByteArray removeChecksum(const QByteArray &p_data) -{ - QByteArray withoutChecksum(p_data); - ConversionError error ("Checksum error"); - - if (p_data.size() >= CHECKSUM_SIZE) - { - withoutChecksum.chop(1); - - - if (p_data.at(p_data.size() - 1) != getChecksum(withoutChecksum)) - throw error; - } - else - throw error; - - return withoutChecksum; -} - - - -static QByteArray maskData(const QByteArray &p_data) -{ - QByteArray maskData; - maskData.reserve(p_data.size() * 8 / 6); - - for (const unsigned char byte : p_data) - { - switch (byte) - { - case XON: - maskData.append(SUBST); - maskData.append(SUB_XON); - break; - case XOFF: - maskData.append(SUBST); - maskData.append(SUB_XOFF); - break; - case STX: - maskData.append(SUBST); - maskData.append(SUB_STX); - break; - case ETX: - maskData.append(SUBST); - maskData.append(SUB_ETX); - break; - case SUBST: - maskData.append(SUBST); - maskData.append(static_cast(SUB_SUBST)); - break; - default: - maskData.append(byte); - break; - } - } - - return maskData; -} - -static bool mustBeMasked(const unsigned char byte) -{ - switch (byte) - { - case XON: - case XOFF: - case STX: - case ETX: - case SUBST: - return true; - default: - break; - } - return false; -} - -int lenMaskedData(const QByteArray &data) -{ - int ret = data.length(); - const unsigned char upperLen = ((data.length() & 0xff00) >> 8); - const unsigned char lowerLen = ((data.length() & 0x00ff) >> 0); - if (mustBeMasked(upperLen)) - ++ret; - if (mustBeMasked(lowerLen)) - ++ret; - for (const unsigned char byte : data) - { - if (mustBeMasked(byte)) - ++ret; - } - if (mustBeMasked(getChecksum(data))) - ++ret; - - return ret; -} - - - - -static QByteArray unmaskData(const QByteArray &p_data) -{ - QByteArray unmaskData; - unmaskData.reserve(p_data.size()); - - for (int position = 0; position < p_data.size(); ++position) - { - unsigned char byte = p_data.at(position); - if (byte == SUBST) - { - ++position; - byte = p_data.at(position); - - switch (byte) - { - case SUB_XON: unmaskData.append(XON); break; - case SUB_XOFF: unmaskData.append(XOFF); break; - case SUB_STX: unmaskData.append(STX); break; - case SUB_ETX: unmaskData.append(ETX); break; - case SUB_SUBST: unmaskData.append(SUBST); break; - default: break; - } - } - else - unmaskData.append(byte); - } - return unmaskData; -} - - - -static QByteArray addFrame(const QByteArray &p_data) -{ - QByteArray ret; - ret.reserve(2 + p_data.size()); - ret.append(STX); - ret.append(p_data); - ret.append(ETX); - return ret; -} - - - -static QByteArray removeFrame(const QByteArray &p_data) -{ - - if (p_data[0] == STX && p_data[p_data.size() - 1] == ETX) - { - - return p_data.mid(1, p_data.size() - 2); - } - else - throw ConversionError("Framing Error"); -} - - - -static void checkLength(const QByteArray &p_data) -{ - if (p_data.size() < HEADER_SIZE) - throw ConversionError("Telegram too short"); - - unsigned char length = p_data[HEADER_SIZE - 1]; - if (length != p_data.size() - HEADER_SIZE) - throw ConversionError("Telegram length does not match"); -} - -QByteArray byteStreamFromSfmMessage(const SfmMessage &message) -{ - QByteArray byteData(getHeader(message)); - - byteData.append(message.payload().size()); - byteData.append(message.payload()); - byteData.append(getChecksum(byteData)); - - return addFrame(maskData(byteData)); -} - -SfmMessage byteStreamToSfmMessage(const QByteArray &datastream) -{ - if (datastream.size() < MINIMUM_BYTE_SIZE) - throw ConversionError("Can't convert an QByteArray that hasn't the minimum size!"); - - QByteArray checkMsg; - - - checkMsg = removeFrame(datastream); - checkMsg = unmaskData(checkMsg); - - checkMsg = removeChecksum(checkMsg); - - checkLength(checkMsg); - - - return getSfmMessage(checkMsg); -} - -QByteArray byteStreamFromEscMessage(const QByteArray &message) -{ - QByteArray byteData = message; - if (message.size() == 1) - { - - byteData.append(message); - } - else - byteData.append(getChecksum(byteData)); - - byteData = maskData(byteData); - byteData = addFrame(byteData); - - return byteData; -} - -QByteArray byteStreamToEscMessage(const QByteArray &datastream) -{ - if (datastream.size() < 2) - return QByteArray("ERROR"); - - QByteArray checkMsg; - - - checkMsg = removeFrame(datastream); - checkMsg = unmaskData(checkMsg); - - - checkMsg.chop(1); - return checkMsg; -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.h b/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.h deleted file mode 100755 index 94dc41b4d..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/sfm_bytemessage_converter.h +++ /dev/null @@ -1,64 +0,0 @@ - - - -#pragma once - -#include -#include - -namespace LtsUtils -{ - -class LTSUTILSCORE_EXPORT SfmMessage -{ -public: - unsigned int id() const { return m_id; } - void setId(unsigned int id) { m_id = id; } - - bool rtr() const { return m_rtr; } - void setRtr(bool rtr) { m_rtr = rtr; } - - QByteArray payload() const { return m_payload; } - void setPayload(const QByteArray &payload) { m_payload = payload; } - - bool operator==(const SfmMessage &msg) const - { - return (m_id == msg.id() && - m_rtr == msg.rtr() && - m_payload == msg.payload()); - } - -protected: - unsigned int m_id = 0; - bool m_rtr = false; - QByteArray m_payload; -}; - - -class ConversionError : public std::runtime_error -{ -public: - using std::runtime_error::runtime_error; -}; - - - -LTSUTILSCORE_EXPORT QByteArray byteStreamFromSfmMessage(const SfmMessage &message); - - - -LTSUTILSCORE_EXPORT SfmMessage byteStreamToSfmMessage(const QByteArray &datastream); - - - -LTSUTILSCORE_EXPORT QByteArray byteStreamFromEscMessage(const QByteArray &message); - - - -LTSUTILSCORE_EXPORT QByteArray byteStreamToEscMessage(const QByteArray &datastream); - - - -LTSUTILSCORE_EXPORT int lenMaskedData(const QByteArray &data); - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsCore/variant_operators.h b/Lts/Common/LtsUtils/LtsUtilsCore/variant_operators.h deleted file mode 100755 index 0be695230..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsCore/variant_operators.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#include - - -namespace LtsUtils -{ - -inline bool lessThan(const QVariant &var1, const QVariant &var2) -{ - if (var1.canConvert(QMetaType::QString) && var2.canConvert(QMetaType::QString)) - { - QString str1(var1.toString()); - QString str2(var2.toString()); - - bool k1 = false; - double d1 = str1.toDouble(&k1); - - if (!k1) - return str1 < str2; - - bool k2 = false; - double d2 = str2.toDouble(&k2); - - if (!k2) - return str1 < str2; - - return d1 < d2; - } - else if (var1.canConvert(QMetaType::Double) && var2.canConvert(QMetaType::Double)) - return var1.toDouble() < var2.toDouble(); - - return false; -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsGui/CMakeLists.txt deleted file mode 100755 index f4f8be558..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-09-19 -# - -ADD_SUBDIRECTORY(LtsUtilsGuiDds) -ADD_SUBDIRECTORY(ConnectDialog) -ADD_SUBDIRECTORY(StyleManager) diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/CMakeLists.txt deleted file mode 100755 index 543855271..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-05-12 -# - -SET(srcs - connect_dialog.h - connect_dialog.cpp - connect_dialog_nt.h - connect_dialog_nt.cpp -) - -NGL_WRAP_UI(srcs - connect_dialog.ui -) - -NGL_ADD_LIBRARY2(WS LtsUtilsGuiConnectDialog SHARED ${srcs}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsGuiConnectDialog - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsUtilsGuiConnectDialog - Qt5::Widgets - LtsWidgets -) diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.cpp b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.cpp deleted file mode 100755 index f16856c1d..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.cpp +++ /dev/null @@ -1,73 +0,0 @@ - - - -#include "connect_dialog.h" -#include "ui_connect_dialog.h" - -#include - - -namespace LtsUtils -{ -int ConnectingDialog::s_waitForHistoricalDataWaitTime = 10000; - - -void ConnectingDialog::reset() -{ - m_ui->label->setText(QStringLiteral("%1").arg(tr("Connecting to %1 on %2 ...").arg(m_serviceName, m_exerciseName))); -} - - -void ConnectingDialog::setup() -{ - m_ui = new Ui::ConnectingDialog; - m_ui->setupUi(this); - reset(); - setMinimumSize(sizeHint()); - connect(m_ui->pbRetry, &QPushButton::clicked, - this, [this]() - { - m_ui->pbRetry->setEnabled(false); - m_ui->pbIgnore->setEnabled(false); - - reset(); - emit signalStopLoop(); - }); - - connect(m_ui->pbIgnore, &QPushButton::clicked, - this, [this]() - { - m_ui->pbIgnore->setEnabled(false); - m_ui->pbRetry->setEnabled(false); - m_ui->pbIgnore->setText(tr("continuing..")); - - deleteLater(); - return; - - - - - - - - }); -} - - -ConnectingDialog::~ConnectingDialog() -{ - delete m_ui; -} - - -void ConnectingDialog::failed() -{ - m_ui->label->setText(QStringLiteral("%1").arg(tr("Could not connect to %1 on %2 !").arg(m_serviceName, m_exerciseName))); - - m_ui->pbRetry->setEnabled(true); - m_ui->pbIgnore->setEnabled(true); - - raise(); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.h b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.h deleted file mode 100755 index eb6b3216b..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.h +++ /dev/null @@ -1,82 +0,0 @@ - - - -#pragma once - -#include -#include - -#include -#include - -#include - - -namespace Ui -{ -class ConnectingDialog; -} - -namespace LtsUtils -{ - -class LTSUTILSGUICONNECTDIALOG_EXPORT ConnectingDialog : public LtsWidgets::LtsFramelessDialog -{ - Q_OBJECT - -public: - static int s_waitForHistoricalDataWaitTime; - - template - explicit ConnectingDialog(DtcType dtc, - WaitForHistoricalDataFunction waitForHistoricalDataFunction, - const QString &serviceName, - const QString &exerciseName, - QWidget *parent = nullptr) - : LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal) - , m_serviceName(serviceName) - , m_exerciseName(exerciseName) - , m_ui(nullptr) - { - setWindowTitle(tr("Connecting ..")); - setup(); - show(); - - while (true) - { - if ((dtc.get()->*waitForHistoricalDataFunction)(s_waitForHistoricalDataWaitTime)) - { - deleteLater(); - return; - } - - if (s_waitForHistoricalDataWaitTime >= 999999) - { - deleteLater(); - return; - } - else - { - failed(); - } - } - } - - ~ConnectingDialog(); - - void failed(); - -private: - QString m_serviceName; - QString m_exerciseName; - Ui::ConnectingDialog *m_ui; - - void reset(); - void setup(); - -signals: - void signalStopLoop(); - -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.ui b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.ui deleted file mode 100755 index 082b3cb6e..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog.ui +++ /dev/null @@ -1,76 +0,0 @@ - - - ConnectingDialog - - - - 0 - 0 - 260 - 200 - - - - - 0 - 0 - - - - - 260 - 200 - - - - Login - - - - - - - 0 - 0 - - - - - - - Qt::AlignCenter - - - true - - - - - - - - - false - - - Retry - - - - - - - false - - - Ignore - - - - - - - - - - diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.cpp b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.cpp deleted file mode 100755 index 3d35ee492..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.cpp +++ /dev/null @@ -1,157 +0,0 @@ - - - -#include "connect_dialog.h" -#include "connect_dialog_nt.h" -#include "ui_connect_dialog.h" - -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) -#include -#endif - - -namespace LtsUtils -{ - - -ConnectingDialogNT::ConnectingDialogNT(const std::function &checkFn, - const QString &serviceName, - const QString &exerciseName, - QWidget *parent) - : LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal) - , m_serviceName(serviceName) - , m_exerciseName(exerciseName) - , m_checkFn(checkFn) - , m_go(false) - , m_autoRetryTimer(new QTimer(this)) - , m_ui(new Ui::ConnectingDialog ) -{ - m_ui->setupUi(this); - - m_autoRetryTimer->setInterval(1000); - connect(m_autoRetryTimer, &QTimer::timeout, - this, [this]() - { - if (m_autoRetryTick < 1) - { - slotRetry(); - return; - } - - m_ui->pbRetry->setText(QStringLiteral("%1 [%2]").arg(tr("Retry")).arg(m_autoRetryTick)); - m_autoRetryTick--; - }); - - reset(); - setMinimumSize(sizeHint()); - connect(m_ui->pbRetry, &QPushButton::clicked, - this, &ConnectingDialogNT::slotRetry); - - connect(m_ui->pbIgnore, &QPushButton::clicked, - this, [this]() - { - m_ui->pbIgnore->setEnabled(false); - m_ui->pbRetry->setEnabled(false); - m_ui->pbIgnore->setText(tr("continuing..")); - - emit signalFailed(); - deleteLater(); - return; - - - - - - - - }); - - show(); - - connect(this, &ConnectingDialogNT::signalGo, - this, &ConnectingDialogNT::slotGo, Qt::QueuedConnection); - - m_go = true; - m_ui->pbRetry->setEnabled(false); -} - - -int ConnectingDialogNT::random(int min, int max) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - return QRandomGenerator::global()->bounded(min, max); -#else - return (qrand() % (((max + 1) - min) + min)); -#endif -} - - - -bool ConnectingDialogNT::event(QEvent *event) -{ - bool result = LtsWidgets::LtsFramelessDialog::event(event); - - if (event->type() == QEvent::Paint && m_go) - { - m_go = false; - QTimer::singleShot(0, this, &ConnectingDialogNT::signalGo); - } - - return result; -} - - -void ConnectingDialogNT::slotRetry() -{ - m_autoRetryTimer->stop(); - - m_ui->pbRetry->setText(tr("Retry")); - m_ui->pbRetry->setEnabled(false); - m_ui->pbIgnore->setEnabled(false); - - reset(); - QTimer::singleShot(1, this, &ConnectingDialogNT::slotGo); -} - - -void ConnectingDialogNT::slotGo() -{ - - if (m_checkFn(ConnectingDialog::s_waitForHistoricalDataWaitTime)) - { - emit signalSuccess(); - deleteLater(); - return; - } - - - failed(); -} - - -void ConnectingDialogNT::reset() -{ - m_ui->label->setText(QStringLiteral("%1").arg(tr("Connecting to %1 on %2 ...").arg(m_serviceName, m_exerciseName))); -} - - -ConnectingDialogNT::~ConnectingDialogNT() -{ - delete m_ui; -} - - -void ConnectingDialogNT::failed() -{ - m_ui->label->setText(QStringLiteral("%1").arg(tr("Could not connect to %1 on %2 !").arg(m_serviceName, m_exerciseName))); - - m_ui->pbRetry->setEnabled(true); - m_ui->pbIgnore->setEnabled(true); - - m_autoRetryTick = random(5, 20); - m_autoRetryTimer->start(); - - raise(); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.h b/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.h deleted file mode 100755 index 00d20ef0f..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/ConnectDialog/connect_dialog_nt.h +++ /dev/null @@ -1,63 +0,0 @@ - - - -#pragma once - -#include -#include - -#include - - -class QTimer; - -namespace Ui -{ -class ConnectingDialog; -} - -namespace LtsUtils -{ - -class LTSUTILSGUICONNECTDIALOG_EXPORT ConnectingDialogNT : public LtsWidgets::LtsFramelessDialog -{ - Q_OBJECT - -public: - - - explicit ConnectingDialogNT(const std::function &checkFn, - const QString &serviceName, - const QString &exerciseName, - QWidget *parent); - ~ConnectingDialogNT(); - - - void failed(); - -private slots: - void slotGo(); - void slotRetry(); - -signals: - void signalGo(); - void signalFailed(); - void signalSuccess(); - -private: - QString m_serviceName; - QString m_exerciseName; - std::function m_checkFn; - bool m_go; - - QTimer *m_autoRetryTimer; - int m_autoRetryTick { 0 }; - - Ui::ConnectingDialog *m_ui; - - int random(int min, int max); - bool event(QEvent *event) override; - void reset(); -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/CMakeLists.txt deleted file mode 100755 index 2e647035b..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-09-19 -# - -SET(SRCS - dds_vector_to_pixmap.cpp - dds_vector_to_pixmap.h - request_helper.cpp - request_helper.h -) - -NGL_ADD_LIBRARY2(WS LtsUtilsGuiDds SHARED ${SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsGuiDds - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsUtilsGuiDds - PRIVATE - LtsUtilsCoreHeaderOnly - LtsWidgets - Qt5::Gui - Qt5::Widgets - Qt5::Svg - PUBLIC - Qt5::Core - ddscore -) -NGL_TARGET_COMPILE_DEFINITIONS(LtsUtilsGuiDds - PUBLIC - REQUEST_HELPER_WITH_GUI -) - - -NGL_ADD_LIBRARY2(AAR HLA SV LtsUtilsDds SHARED request_helper.cpp request_helper.h) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsDds - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsUtilsDds - PRIVATE - LtsUtilsCoreHeaderOnly - PUBLIC - Qt5::Core - ddscore -) diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.cpp b/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.cpp deleted file mode 100755 index c117b395b..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.cpp +++ /dev/null @@ -1,134 +0,0 @@ - - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsUtils -{ - -QIcon iconFromSvg(const QByteArray &svgData, QSize iconSize) -{ - QSvgRenderer renderer(svgData); - QPixmap p(iconSize); - p.fill(Qt::transparent); - QPainter painter(&p); - renderer.render(&painter); - painter.end(); - - return {p}; -} - -QIcon iconFromSvg(const QString &svgFilename, QSize iconSize) -{ - QFile f(svgFilename); - if (!f.open(QIODevice::ReadOnly)) - return {}; - return iconFromSvg(f.readAll(), iconSize); -} - -QPixmap pixmapFromIcon(const QIcon &icon, QSize iconSize) -{ - return icon.pixmap(iconSize).scaled(100, 100, Qt::AspectRatioMode::IgnoreAspectRatio); -} - -QPixmap pixmapFromSvg(const QString &svgFilename, QSize iconSize) -{ - return pixmapFromIcon(iconFromSvg(svgFilename, iconSize), iconSize); -} - -QPixmap getPixmap(const std::vector &data) -{ - if (data.size() == 0) - return QPixmap(); - - return getPixmap(QByteArray(reinterpret_cast(&data.at(0)), data.size())); -} - -QPixmap getPixmap(const QByteArray &data) -{ - return QPixmap::fromImage(QImage::fromData(data)); -} - -QImage getImage(const std::vector &data) -{ - if (data.size() == 0) - return QImage(); - - return getImage(QByteArray(reinterpret_cast(&data.at(0)), data.size())); -} - -QImage getImage(const QByteArray &data) -{ - return QImage::fromData(data); -} - -static inline void setIf(bool *boolPtr, bool val) -{ - if (boolPtr) - *boolPtr = val; -} - -std::vector fromPixmap(const QPixmap &pixmap, bool *bIsPng) -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - pixmap.save(&buf, "PNG"); - buf.close(); - QByteArray ba = buf.buffer(); - - setIf(bIsPng, true); - - if (ba.size() > 10240 && !pixmap.hasAlpha() && !pixmap.hasAlphaChannel()) - { - QBuffer buf2; - buf2.open(QIODevice::WriteOnly); - pixmap.save(&buf2, "JPG"); - buf2.close(); - if (buf2.buffer().size() < buf.buffer().size()) - { - setIf(bIsPng, false); - ba = buf2.buffer(); - } - } - - return convert>(ba); -} - -std::vector fromImage(const QImage &image, bool *bIsPng) -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - image.save(&buf, "PNG"); - buf.close(); - QByteArray ba = buf.buffer(); - - - setIf(bIsPng, true); - if ((ba.size() > 10240 && !image.hasAlphaChannel()) || - ba.size() > 1024 * 1024) - { - QBuffer buf2; - buf2.open(QIODevice::WriteOnly); - image.save(&buf2, "JPG"); - buf2.close(); - if (buf2.buffer().size() < buf.buffer().size()) - { - setIf(bIsPng, false); - ba = buf2.buffer(); - } - } - - return convert>(ba); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h b/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h deleted file mode 100755 index 80cccc1d5..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h +++ /dev/null @@ -1,33 +0,0 @@ - - - -#pragma once - -#include - -#include -#include - -#include -#include - -class QByteArray; -class QPixmap; -class QImage; -class QIcon; - -namespace LtsUtils -{ - -LTSUTILSCORE_EXPORT QPixmap getPixmap(const std::vector &data); -LTSUTILSCORE_EXPORT QPixmap getPixmap(const QByteArray &data); -LTSUTILSCORE_EXPORT QImage getImage(const std::vector &data); -LTSUTILSCORE_EXPORT QImage getImage(const QByteArray &data); -LTSUTILSCORE_EXPORT QIcon iconFromSvg(const QByteArray &svgData, QSize iconSize = QSize(100, 100)); -LTSUTILSCORE_EXPORT QIcon iconFromSvg(const QString &svgFilename, QSize iconSize = QSize(100, 100)); -LTSUTILSCORE_EXPORT QPixmap pixmapFromIcon(const QIcon &icon, QSize iconSize = QSize(100, 100)); -LTSUTILSCORE_EXPORT QPixmap pixmapFromSvg(const QString &svgFilename, QSize iconSize = QSize(100, 100)); -LTSUTILSCORE_EXPORT std::vector fromPixmap(const QPixmap &pixmap, bool *bIsPng = nullptr); -LTSUTILSCORE_EXPORT std::vector fromImage(const QImage &image, bool *bIsPng = nullptr); - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.cpp b/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.cpp deleted file mode 100755 index ec92f01df..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.cpp +++ /dev/null @@ -1,276 +0,0 @@ - - - -#include "request_helper.h" - -#ifdef REQUEST_HELPER_WITH_GUI -#include -#include -#include -#endif - - -#include -#include -#include -#include -#include - -#define DEBUG - - -namespace LtsUtils -{ -bool RequestHelper::s_debug = false; - -RequestHelper::RequestHelper(QObject *parent, bool quiet, - - const std::function &success, - const std::function &failure) - : QObject(parent) - , m_failed(false) - , m_repliedId(-1) - , m_quiet(quiet) - , m_blockingCall(true) - , m_successFn(success) - , m_failureFn(failure) -{ - connect(this, &RequestHelper::signalSuccess, this, m_successFn, Qt::QueuedConnection); - connect(this, &RequestHelper::signalFailed, this, m_failureFn, Qt::QueuedConnection); - - if (s_debug) - { - QTimer *t = new QTimer(this); - t->setInterval(2000); - t->setSingleShot(true); - connect(this, &RequestHelper::signalNextRequest, t, static_cast(&QTimer::start)); - connect(t, &QTimer::timeout, this, &RequestHelper::slotNextRequest, Qt::QueuedConnection); - } - else - { - connect(this, &RequestHelper::signalNextRequest, - this, &RequestHelper::slotNextRequest, - Qt::QueuedConnection - -); - } -} - - -void RequestHelper::addRequest(const std::function &requestFunction, - const std::function &baseReplyHandler) -{ - m_requestFunctions.append(RequestFunctionContainer(requestFunction, baseReplyHandler)); -} - -bool RequestHelper::requestReply(const Core::FunctionReply &baseReply) -{ - const Core::FunctionReplyObject fro(baseReply.id(), - baseReply.errorString(), - baseReply.errorParams()); - return _requestReply(fro, &baseReply); -} - -bool RequestHelper::requestReply(const Core::FunctionReplyObject &replyObject) -{ - return _requestReply(replyObject, nullptr); -} - - -bool RequestHelper::_requestReply(const Core::FunctionReplyObject &fro, const Core::FunctionReply *baseReply) -{ - m_errorString = fro.translated(); - -#ifdef REQUEST_HELPER_WITH_GUI - if (fro.error()) - { - - if (!m_quiet) - { - if (m_blockingCall) - { - LtsWidgets::LtsMessageDialog::showMessage(qobject_cast(parent()), - m_errorString, - tr("Request failed")); - } - else - { - new LtsWidgets::LtsMessageDialogNew(qobject_cast(parent()), - m_errorString, - tr("Request failed")); - } - } - } -#endif - - m_repliedId = fro.id(); - - if (m_singleRequestCustomFunctionReply != nullptr) - { - if (baseReply) - m_singleRequestCustomFunctionReply(*baseReply); - else - m_singleRequestCustomFunctionReply(Core::FunctionReply(fro.id(), fro.str(), fro.args(), fro.additionalInfo())); - } - - if (fro.error()) - { - failure(); - return false; - } - - emit signalNextRequest(); - return true; -} - - -bool RequestHelper::startSingleRequest(const std::function &requestFunction, - const std::function &baseReplyHandler) -{ - addRequest(requestFunction, baseReplyHandler); - return startRequests(); -} - - -void RequestHelper::failure() -{ - m_failed = true; - emit signalFailed(); - done(); -} - - -void RequestHelper::done() -{ -#ifdef REQUEST_HELPER_WITH_GUI - if (!m_quiet) - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); -#endif - - m_requestFunctions.clear(); - m_singleRequestCustomFunctionReply = nullptr; - - if (m_blockingCall) - emit signalQuitLoop(); - - m_blockingCall = true; - - if (!m_blockingCall) - deleteLater(); -} - - -void RequestHelper::slotNextRequest() -{ - if (m_requestFunctions.isEmpty()) - { - if (!m_blockingCall) - emit signalSuccess(); - - done(); - - return; - } - - const RequestFunctionContainer rfc = m_requestFunctions.takeFirst(); - m_singleRequestCustomFunctionReply = rfc.second; - - int64_t result = rfc.first(); - - if (result == 0) - { -#ifdef REQUEST_HELPER_WITH_GUI - if (!m_quiet) - { - if (m_blockingCall) - { - LtsWidgets::LtsMessageDialog::showMessage(qobject_cast(parent()), - tr("Request failed")); - } - else - { - connect(new LtsWidgets::LtsMessageDialogNew(qobject_cast(parent()), - tr("Request failed")), &LtsWidgets::LtsMessageDialogNew::destroyed, - this, [this]() - { - failure(); - }); - - return; - } - } -#endif - failure(); - } -} - - -void RequestHelper::startSingleBackgroundRequest(const std::function &requestFunction, - const std::function &baseReplyHandler) -{ - m_blockingCall = false; - startSingleRequest(requestFunction, baseReplyHandler); -} - - -void RequestHelper::startBackgroundRequests() -{ - m_blockingCall = false; - startRequests(); -} - - -void RequestHelper::setCustomWaitMessage(const QString &customWaitMessage) -{ - m_customWaitMessage = (customWaitMessage + QStringLiteral(" ...")); -} - - -bool RequestHelper::startRequests() -{ - if (m_requestFunctions.isEmpty()) - { -#ifdef LTS_DEBUG - qWarning("RequestHelper: started request helper without requests!"); -#endif - return true; - } - - m_failed = false; - -#ifdef REQUEST_HELPER_WITH_GUI - if (!m_quiet) - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(m_customWaitMessage); - m_customWaitMessage.clear(); -#endif - - m_errorString.clear(); - m_repliedId = -1; - - if (s_debug) - { - QTimer *t = new QTimer(this); - t->setInterval(2000); - connect(t, &QTimer::timeout, this, &RequestHelper::slotNextRequest, Qt::QueuedConnection); - connect(t, &QTimer::timeout, t, &QTimer::deleteLater); - t->start(); - } - else - { - slotNextRequest(); - - if (m_blockingCall) - { - QEventLoop loop; - connect(this, &RequestHelper::signalQuitLoop, - &loop, &QEventLoop::quit, - Qt::QueuedConnection); - loop.exec(); - } - } - - - return (!m_failed); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.h b/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.h deleted file mode 100755 index 9557aeb1b..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.h +++ /dev/null @@ -1,123 +0,0 @@ - - - -#pragma once - -#ifdef REQUEST_HELPER_WITH_GUI -#include -#define LTSUTILSGUIDDS_INTERNAL_EXPORT LTSUTILSGUIDDS_EXPORT -#else -#include -#define LTSUTILSGUIDDS_INTERNAL_EXPORT LTSUTILSDDS_EXPORT -#endif - -#include -#include - -#include -#include -#include -#include - - -namespace LtsUtils -{ - -template -dds::core::optional toOptional(T inVal) -{ - return inVal != T() ? dds::core::optional(inVal) : dds::core::optional(); -} - -template -dds::core::optional optional() -{ - return dds::core::optional(); -} - - -class LTSUTILSGUIDDS_INTERNAL_EXPORT RequestHelper : public QObject -{ - Q_OBJECT - -public: - static bool s_debug; - - explicit RequestHelper(QObject *parent = nullptr, bool quiet = false, - - const std::function &success = [](){}, - const std::function &failure = [](){}); - ~RequestHelper() = default; - - bool quiet() const { return m_quiet; } - void setQuiet(bool quiet) { m_quiet = quiet; } - void addRequest(const std::function &requestFunction, - const std::function &baseReplyHandler = nullptr); - bool startRequests(); - - - bool startSingleRequest(const std::function &requestFunction, - const std::function &baseReplyHandler = nullptr - ); - - - - - void startSingleBackgroundRequest(const std::function &requestFunction, - const std::function &baseReplyHandler = nullptr); - - - void startBackgroundRequests(); - - bool requestReply(const Core::FunctionReply &baseReply); - bool requestReply(const Core::FunctionReplyObject &replyObject); - - - - bool hasRequests() const { return !m_requestFunctions.isEmpty(); } - - - - - int getRepliedId() const { return m_repliedId; } - - - - QString getErrorString() const { return m_errorString; } - - - - void setCustomWaitMessage(const QString &customWaitMessage); - - bool failed() const { return m_failed; } - void failure(); - -private: - using RequestFunctionContainer = QPair, std::function>; - - QVector m_requestFunctions; - bool m_failed; - int m_repliedId; - std::function m_singleRequestCustomFunctionReply; - bool m_quiet; - bool m_blockingCall; - std::function m_successFn; - std::function m_failureFn; - QString m_errorString; - QString m_customWaitMessage; - - void done(); - bool _requestReply(const Core::FunctionReplyObject &fro, const Core::FunctionReply *baseReply); - -signals: - void signalNextRequest(); - void signalQuitLoop(); - void signalSuccess(); - void signalFailed(); - -private slots: - void slotNextRequest(); - -}; - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/CMakeLists.txt deleted file mode 100755 index ba7a53540..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, NLSE, 2018-02-15 -# - -SET(srcs - style_manager.h - style_manager.cpp -) - -NGL_ADD_LIBRARY2(WS LtsUtilsGuiStyleManager SHARED ${srcs}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsGuiStyleManager - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ -) -NGL_TARGET_LINK_LIBRARIES(LtsUtilsGuiStyleManager - Qt5::Core - LtsPluginLoader -) diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.cpp b/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.cpp deleted file mode 100755 index 99308ea52..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.cpp +++ /dev/null @@ -1,114 +0,0 @@ - - - -#include "style_manager.h" - -#include -#include - -#include -#include - - -namespace LtsUtils -{ - -struct StyleManagerHandler -{ - StyleManager styleManager; -}; -Q_GLOBAL_STATIC(StyleManagerHandler, s_styleManagerHandler) - - -StyleManager *StyleManager::getInstance() -{ - return &s_styleManagerHandler->styleManager; -} - -bool StyleManager::loadStyle(const QString &stylePluginName, bool highDpi) -{ - if (m_currentStylePluginLoader != nullptr) - { - - - QObject *pluginInstance = m_currentStylePluginLoader->instance(); - StylePlugin::Plugin *sp = qobject_cast(pluginInstance); - - if (sp == nullptr) - { - qWarning() << "StyleManager::loadStyle: could not unload style plugin - wrong type (" - << pluginInstance->metaObject()->className() << ") - new style not loaded"; - return false; - } - - sp->unloadStyle(); - - - - - - - - - - m_currentStylePluginLoader = nullptr; - } - - - - LtsPluginManager::LtsPluginLoader *pluginLoader = LtsPluginManager::LtsPluginLoader::getInstance(); - - QPluginLoader *stylePluginLoader = nullptr; - int pluginsLoaded = 0; - - std::function loadPluginFn = [&stylePluginLoader, &pluginsLoaded] - (const LtsPluginManager::PluginInstance &pi) - { - stylePluginLoader = pi.pluginLoader; - pluginsLoaded++; - return true; - }; - - if (!pluginLoader->loadPlugins(LtsPluginManager::PluginType::STYLE, loadPluginFn, QStringList() << stylePluginName)) - { - qWarning() << "StyleManager::loadStyle: Error loading Style Plugin - using default Qt style"; - return false; - } - - if (pluginsLoaded == 0) - { - qWarning() << "StyleManager::loadStyle: Could not find Style Plugin" << stylePluginName << "- using default Qt style"; - return false; - } - - if (pluginsLoaded > 1) - { - qWarning() << "StyleManager::loadStyle: Style Plugin" << stylePluginName << " is ambiguous - using default Qt style"; - return false; - } - - if (stylePluginLoader == nullptr) - { - qWarning() << "StyleManager::loadStyle: No Style Plugin loader available - using default Qt style"; - return false; - } - - StylePlugin::Plugin *sp = qobject_cast(stylePluginLoader->instance()); - - if (sp == nullptr) - { - qWarning() << "StyleManager::loadStyle: could not load style plugin - wrong type (" - << stylePluginLoader->instance()->metaObject()->className() << ") - new style not loaded"; - return false; - } - - sp->loadStyle(highDpi); - - m_currentStylePluginLoader = stylePluginLoader; - m_stylePluginName = stylePluginName; - emit signalStyleChanged(stylePluginName); - - return true; -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.h b/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.h deleted file mode 100755 index 6b6233f7d..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsGui/StyleManager/style_manager.h +++ /dev/null @@ -1,36 +0,0 @@ - - - -#pragma once - -#include -#include - - -class QPluginLoader; - - -namespace LtsUtils -{ -class LTSUTILSGUISTYLEMANAGER_EXPORT StyleManager : public QObject -{ - Q_OBJECT - using QObject::QObject; - ~StyleManager() = default; -public: - static StyleManager *getInstance(); - - bool loadStyle(const QString &stylePluginName, bool highDpi = false); - const QString ¤tStyleName() const { return m_stylePluginName; } - -private: - friend struct StyleManagerHandler; - - QPluginLoader *m_currentStylePluginLoader = nullptr; - QString m_stylePluginName; - -signals: - void signalStyleChanged(const QString &stylePluginName); - -}; -} diff --git a/Lts/Common/LtsUtils/LtsUtilsSettings/CMakeLists.txt b/Lts/Common/LtsUtils/LtsUtilsSettings/CMakeLists.txt deleted file mode 100755 index 51eb46a04..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsSettings/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -SET(SRCS - settings.h - settings.cpp -) - -NGL_ADD_LIBRARY2(ALL LtsUtilsSettings SHARED ${SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsSettings - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsUtilsSettings - PUBLIC - Qt5::Core -) diff --git a/Lts/Common/LtsUtils/LtsUtilsSettings/settings.cpp b/Lts/Common/LtsUtils/LtsUtilsSettings/settings.cpp deleted file mode 100755 index daf785816..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsSettings/settings.cpp +++ /dev/null @@ -1,264 +0,0 @@ - - -#include "settings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsUtils -{ - -enum class SettingType -{ - String = 1, - StringList = 2, - Json = 3, - FunctionKeys = 4, - Boolean = 5, -}; - -QString typeToString(SettingType t) -{ - switch (t) - { - case SettingType::String: - return QStringLiteral("String"); - case SettingType::StringList: - return QStringLiteral("StringList"); - case SettingType::Json: - return QStringLiteral("Json"); - case SettingType::FunctionKeys: - return QStringLiteral("FunctionKeys"); - case SettingType::Boolean: - return QStringLiteral("Boolean"); - } - Q_ASSERT(false); - return QStringLiteral("Unknown SettingType (%1)").arg(int(t)); -} - -SettingType stringToType(const QString &s) -{ - if (s == typeToString(SettingType::String)) - return SettingType::String; - if (s == typeToString(SettingType::StringList)) - return SettingType::StringList; - if (s == typeToString(SettingType::Json)) - return SettingType::Json; - if (s == typeToString(SettingType::FunctionKeys)) - return SettingType::FunctionKeys; - if (s == typeToString(SettingType::Boolean)) - return SettingType::Boolean; - Q_ASSERT(false); - return SettingType::String; -} - - -static QString craftKey(const QString &group, const QString &identifier) -{ - return group + QLatin1Char('.') + identifier; -} - - - - -static OperatorSettings *s_operatorSettings = nullptr; -static void cleanupOperatorSettings() -{ - delete s_operatorSettings; - s_operatorSettings = nullptr; -} - -OperatorSettings *OperatorSettings::getInstance() -{ - if (!s_operatorSettings) - { - s_operatorSettings = new OperatorSettings; - qAddPostRoutine(&cleanupOperatorSettings); - } - return s_operatorSettings; -} - - - - - -static ProjectSettings *s_projectSettings = nullptr; -static void cleanupProjectSettings() -{ - delete s_projectSettings; - s_projectSettings = nullptr; -} - -ProjectSettings *ProjectSettings::getInstance() -{ - if (!s_projectSettings) - { - s_projectSettings = new ProjectSettings; - qAddPostRoutine(&cleanupProjectSettings); - } - return s_projectSettings; -} - -bool ProjectSettings::initFromFile(const QString &filename) -{ - QFile f(filename); - if (!f.open(QIODevice::ReadOnly)) - return false; - - QJsonParseError jErr; - const QJsonObject settings = QJsonDocument::fromJson(f.readAll(), &jErr).object(); - if (jErr.error != QJsonParseError::NoError) - return false; - - const QJsonObject psObj = settings[QStringLiteral("ProjectSettings")].toObject(); - if (psObj.isEmpty()) - return false; - - QHash newData; - const QStringList &groupKeys(psObj.keys()); - for (const auto &gKey : groupKeys) - { - const QJsonObject objGroup(psObj[gKey].toObject()); - const QStringList &itemKeys(objGroup.keys()); - for (const auto &key : itemKeys) - { - const QJsonObject obj(objGroup[key].toObject()); - const auto strType(obj[QStringLiteral("Type")].toString()); - const auto type = stringToType(strType); - const QJsonValue jVal(obj[QStringLiteral("Default")]); - QVariant val; - switch (type) - { - case SettingType::Json: - val = QJsonDocument(jVal.toObject()).toJson(); - break; - case SettingType::StringList: - { - const QJsonArray arr(jVal.toArray()); - QStringList sl; - sl.reserve(arr.size()); - for (const auto &ele : arr) - sl.push_back(ele.toString()); - val = sl; - break; - } - case SettingType::FunctionKeys: - val = jVal.toVariant(); - break; - case SettingType::String: - val = jVal.toString(); - break; - case SettingType::Boolean: - val = jVal.toBool(); - break; - } - newData[craftKey(gKey, key)] = val; - } - } - getInstance()->m_data = newData; - return true; -} - -bool ProjectSettings::isFunctionEnabled(const QString &group, const QString &function) -{ - const QStringList functionNames = getInstance()->value(QStringLiteral("Functionalities"), - QStringLiteral("IdentifierList")).toStringList(); - - return functionNames.contains(group + QLatin1Char(':') + function); -} - - - - -void Settings::init(const QByteArray &settings) -{ - QByteArray uncompressed; - try - { - uncompressed = qUncompress(settings); - } - catch (...) - {} - - if (uncompressed.isEmpty()) - uncompressed = settings; - - QDataStream stream(uncompressed); - stream >> m_data; -} - - -QByteArray Settings::exportSettings() const -{ - if (m_data.isEmpty()) - return QByteArray(); - - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - stream << m_data; - return qCompress(ba); -} - - -void Settings::clear() -{ - m_data.clear(); -} - -void Settings::dump() -{ - qWarning() << "Settings::m_data: keys=" << m_data.keys(); - QHashIterator it(m_data); - int i=0; - while (it.hasNext()) - { - it.next(); - qWarning() << i++ << ":" << " - key='" << it.key() << "' value -> '" << it.value() << "'"; - } - qWarning() << "Settings::m_data: ------------------"; -} - -void Settings::setValue(const QString &group, const QString &identifier, const QVariant &value) -{ - - m_data.insert(craftKey(group, identifier), value); - emit valueChanged(group, identifier, value); -} - - -void Settings::removeValue(const QString &group, const QString &identifier) -{ - if (m_data.remove(craftKey(group, identifier))) - emit valueRemoved(group, identifier); -} - - -QStringList Settings::identifiers(const QString &group) const -{ - QStringList result; - for (auto it = m_data.begin(); it != m_data.end(); ++it) - { - QVector decrafted = it.key().splitRef(QLatin1Char('.')); - if (decrafted.size() > 1 && group.compare(decrafted.at(0)) == 0) - result.append(decrafted.at(1).toString()); - } - return result; -} - - -QVariant Settings::value(const QString &group, const QString &identifier, const QVariant &defaultValue) const -{ - return m_data.value(craftKey(group, identifier), defaultValue); -} - -} diff --git a/Lts/Common/LtsUtils/LtsUtilsSettings/settings.h b/Lts/Common/LtsUtils/LtsUtilsSettings/settings.h deleted file mode 100755 index b1563de7a..000000000 --- a/Lts/Common/LtsUtils/LtsUtilsSettings/settings.h +++ /dev/null @@ -1,73 +0,0 @@ - -#pragma once - -#include -#include -#include -#include - -class QJsonObject; - -namespace LtsUtils -{ - -class LTSUTILSSETTINGS_EXPORT Settings : public QObject -{ - Q_OBJECT - -public: - ~Settings() = default; - - - - void setValue(const QString &group, const QString &identifier, const QVariant &value); - - - - void removeValue(const QString &group, const QString &identifier); - - - - QVariant value(const QString &group, const QString &identifier, const QVariant &defaultValue = QVariant()) const; - - - - QStringList identifiers(const QString &group) const; - - void init(const QByteArray &settings); - QByteArray exportSettings() const; - void clear(); - - void dump(); - -protected: - Settings() = default; - -signals: - void valueChanged(const QString &group, const QString &identifier, const QVariant &value); - void valueRemoved(const QString &group, const QString &identifier); - -protected: - QHash m_data; - -}; - -class LTSUTILSSETTINGS_EXPORT OperatorSettings : public Settings -{ - Q_OBJECT -public: - static OperatorSettings *getInstance(); -}; - -class LTSUTILSSETTINGS_EXPORT ProjectSettings : public Settings -{ - Q_OBJECT -public: - static ProjectSettings *getInstance(); - - static bool initFromFile(const QString &filename); - static bool isFunctionEnabled(const QString &group, const QString &function); -}; - - -} diff --git a/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt b/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt deleted file mode 100755 index 874965a18..000000000 --- a/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# -# Lts Widgets library -# - -SET(ltsvkeyboard_SRCS - LtsKeyPushButton.h - LtsKeyPushButton.cpp - LtsVirtualKeyboard.h - LtsVirtualKeyboard.cpp -) - -NGL_WRAP_UI(ltsvkeyboard_SRCS -) - -NGL_ADD_LIBRARY2(WS LtsVirtualKeyboard SHARED ${ltsvkeyboard_SRCS} ) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsVirtualKeyboard - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsVirtualKeyboard - PRIVATE - PUBLIC - LtsWidgets - LtsSession - LtsAppManager - Qt5::Widgets - Qt5::Gui -) diff --git a/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp b/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp deleted file mode 100755 index b27b645f8..000000000 --- a/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp +++ /dev/null @@ -1,150 +0,0 @@ - - - -#include "LtsKeyPushButton.h" -#include "LtsVirtualKeyboard.h" -#include -#include -#include -#include - - -namespace LtsVirtualKeyboard -{ -LtsKeyPushButton::LtsKeyPushButton(const QString ¬TranslatedText, const QString ¬TranslatedTextAlt, QWidget *parent) - : QToolButton(parent) - , m_parent(parent) - , m_notTranslatedText(notTranslatedText) - , m_notTranslatedTextAlt(notTranslatedTextAlt) - , m_altMode(false) - , m_capsMode(false) -{ - setFocusPolicy(Qt::NoFocus); - setStyleSheet(QStringLiteral(DEFAULT_STYLE_BUTTON) + QStringLiteral(DEFAULT_BACKGROUND_BUTTON)); - - connect(this, &LtsKeyPushButton::pressedKey, this, &LtsKeyPushButton::getKeyPress); -} - -void LtsKeyPushButton::getKeyPress() -{ - int keyCode = 0; - QKeyEvent *key = nullptr; - QString text = this->text(); - - - if (!qobject_cast(m_parent)) - return; - LtsVirtualKeyboard *keyboard = qobject_cast(m_parent); - - - if (text == QStringLiteral("&&")) - text = QStringLiteral("&"); - - if (NO_SPECIAL_KEY(text) == false && (IS_BACK(text) == true || IS_TAB(text) == true || IS_RETURN(text) == true || IS_CTRL_LEFT(text) == true || - IS_ALT(text) == true || IS_CANC(text) == true || IS_CUT_LEFT(text) == true || IS_PASSWORD(text) || IS_PASTE(text) || IS_COPY(text))) - key = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier, text); - else - { - keyCode = text.toUtf8()[0]; - if (m_capsMode == false) - { - text = text.toLower(); - key = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier, text); - } - else - key = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::ShiftModifier, text); - } - keyboard->receiptChildKey(nullptr, key); - QCoreApplication::processEvents(); -} - - -void LtsKeyPushButton::mousePressEvent(QMouseEvent *) -{ - if (!qobject_cast(m_parent)) - return; - QString text = this->text(); - QString defaultStyleButton = QStringLiteral(DEFAULT_STYLE_BUTTON); - QString changedColorButton = QStringLiteral(CHANGED_BACKGROUND_BUTTON); - - if (IS_CAPS(text) == true) - { - if (!styleSheet().contains(QStringLiteral(DEFAULT_BACKGROUND_BUTTON))) - changedColorButton = QStringLiteral(DEFAULT_BACKGROUND_BUTTON); - setStyleSheet(defaultStyleButton + changedColorButton); - repaint(); - QCoreApplication::processEvents(); - } - - - else if (IS_FIN(text) == true) - setStyleSheet(defaultStyleButton + QStringLiteral(CHANGED_BACKGROUND_BUTTON)); - else - { - setStyleSheet(defaultStyleButton + changedColorButton); - repaint(); - QCoreApplication::processEvents(); - } - emit pressedKey(); - - m_oldYKey = 0; -} - -void LtsKeyPushButton::mouseReleaseEvent(QMouseEvent * ) -{ - if (!qobject_cast(m_parent)) - return; - - LtsVirtualKeyboard *tmpKeyBoard = qobject_cast(m_parent); - - bool pressedEcho = IS_PASSWORD(text()) == true; - - if (IS_CAPS(text()) == false && pressedEcho == false) - setStyleSheet(QStringLiteral(DEFAULT_STYLE_BUTTON) + QStringLiteral(DEFAULT_BACKGROUND_BUTTON)); - else if (pressedEcho == true && tmpKeyBoard->isEnabledSwitchingEcho() == false) - setStyleSheet(QStringLiteral(DEFAULT_STYLE_BUTTON) + QStringLiteral(DEFAULT_BACKGROUND_BUTTON)); -} - -void LtsKeyPushButton::changeEvent(QEvent *event) -{ - if (event->type() == QEvent::LanguageChange) - { - setAltMode(m_altMode); - } - else - QToolButton::changeEvent(event); -} - -void LtsKeyPushButton::setAltMode(bool altmode) -{ - m_altMode = altmode; - if (m_altMode) - { - if (!IS_CANC(text()) && !IS_FIN(text()) && !IS_CAPS(text()) - && !IS_COPY(text()) && !IS_PASTE(text()) && !IS_CUT_LEFT(text()) ) - setText(m_notTranslatedTextAlt); - } - else - { - setText(m_notTranslatedText); - } - setObjectName(text()); - - setCapsMode(m_capsMode); -} - -void LtsKeyPushButton::setCapsMode(bool caps) -{ - if (text().length() > 2 || IS_FIN(text())) - return; - if (m_notTranslatedText == QChar(223)) - return; - m_capsMode = caps; - if (m_capsMode) - { - setText(text().toUpper()); - } - else - setText(text().toLower()); -} -} diff --git a/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h b/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h deleted file mode 100755 index 66e87ea9f..000000000 --- a/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h +++ /dev/null @@ -1,89 +0,0 @@ - - - -#pragma once -#include -#include - -#define DEFAULT_BACKGROUND_BUTTON "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #8C8F8C, stop: 1 #333831);" -#define CHANGED_BACKGROUND_BUTTON "background:lightgray;color:darkred;" - - - -#define DEFAULT_STYLE_BUTTON "color:white;border: 1px solid #000000;border-radius:6px;" - -#define STYLE_FOCUS_TEXT "border: 1px solid red" - - - - - - - -namespace LtsVirtualKeyboard -{ -class LTSVIRTUALKEYBOARD_EXPORT LtsKeyPushButton : public QToolButton -{ - Q_OBJECT - - -#define KEY_TAB LtsKeyPushButton::tr("TAB") -#define KEY_ALT LtsKeyPushButton::tr("!?#") -#define KEY_ALT2 LtsKeyPushButton::tr("Abc") -#define KEY_CAPS QStringLiteral("CAPS") -#define KEY_CTRL_LEFT LtsKeyPushButton::tr("CTRL") -#define KEY_CUT_LEFT LtsKeyPushButton::tr("cut") -#define KEY_COPY LtsKeyPushButton::tr("copy") -#define KEY_PASTE LtsKeyPushButton::tr("paste") -#define KEY_BACKSPACE LtsKeyPushButton::tr("<--") -#define KEY_HIDECHAR LtsKeyPushButton::tr("echo") -#define KEY_CANC QStringLiteral("->") -#define KEY_RETURN QStringLiteral("RETURN") -#define KEY_FIN QStringLiteral("Ok") -#define KEY_SHIFT QStringLiteral("SHIFT") -#define KEY_SPACE " " - -#define IS_KEY(keyTextPressed, keyValCompare) (keyTextPressed).contains((keyValCompare), Qt::CaseInsensitive) - -#define IS_TAB(text) IS_KEY(text, KEY_TAB) -#define IS_ALT(text) IS_KEY(text, KEY_ALT) -#define IS_ALT2(text) IS_KEY(text, KEY_ALT2) -#define IS_CAPS(text) IS_KEY(text, KEY_CAPS) -#define IS_CTRL_LEFT(text) IS_KEY(text, KEY_CTRL_LEFT) -#define IS_CUT_LEFT(text) IS_KEY(text, KEY_CUT_LEFT) -#define IS_BACK(text) IS_KEY(text, KEY_BACKSPACE) -#define IS_CANC(text) IS_KEY(text, KEY_CANC) -#define IS_RETURN(text) IS_KEY(text, KEY_RETURN) -#define IS_FIN(text) IS_KEY(text, KEY_FIN) -#define IS_SHIFT(text) IS_KEY(text, KEY_SHIFT) -#define IS_SPACE(text) IS_KEY(text, QStringLiteral(KEY_SPACE)) -#define IS_PASSWORD(text) IS_KEY(text, KEY_HIDECHAR) -#define IS_COPY(text) IS_KEY(text, KEY_COPY) -#define IS_PASTE(text) IS_KEY(text, KEY_PASTE) -#define NO_SPECIAL_KEY(text) ((text).length() == 1) - -public: - LtsKeyPushButton(const QString ¬TranslatedText, const QString ¬TranslatedTextAlt, QWidget *parent = nullptr); - void setAltMode(bool altmode); - void setCapsMode(bool caps); - -private slots: - void getKeyPress(); - -signals: - void pressedKey(); - -private: - QWidget *m_parent; - QString m_notTranslatedText; - QString m_notTranslatedTextAlt; - int m_oldYKey; - bool m_altMode; - bool m_capsMode; - -protected: - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void changeEvent(QEvent *event); -}; -} diff --git a/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp b/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp deleted file mode 100755 index 83efc9476..000000000 --- a/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp +++ /dev/null @@ -1,953 +0,0 @@ - - - -#include "LtsVirtualKeyboard.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsVirtualKeyboard -{ - -static LtsVirtualKeyboard *s_ltsVirtualKeyboard = nullptr; -static bool lastState = false; -LtsVirtualKeyboard::LtsVirtualKeyboard(LtsSession::LtsSession *session, QWidget *activeForm, bool onlyNumericPad, QWidget *parent) - : QWidget(parent) - - , m_te(nullptr) - , m_le(nullptr) - , m_nextInput(nullptr) - , m_activeWindow(activeForm) - , m_echoMode(false) - , m_enablePasswordEcho(false) - , m_numericPad(onlyNumericPad) - , m_isAlt(false) - , m_isCap(false) - , m_enableEnter(true) - , m_ready(false) - , m_height(0) - , m_clipboard(QApplication::clipboard()) - , m_session(session) -{ - setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint | Qt::Dialog); - setFocusPolicy(Qt::NoFocus); - - setWindowTitle(QString()); - connect(m_activeWindow, &QWidget::destroyed, - this, [this](QObject *obj) - { - if (static_cast(m_activeWindow) == obj) - m_activeWindow = nullptr; - }); -} - -LtsVirtualKeyboard::~LtsVirtualKeyboard() -{ -} - -LtsVirtualKeyboard *LtsVirtualKeyboard::getInstance(LtsSession::LtsSession *session, QWidget *activeForm, bool onlyNumericPad, QWidget *parent) -{ - onlyNumericPad = false; - - - - if (s_ltsVirtualKeyboard && onlyNumericPad == lastState ) - { - - if (activeForm) - s_ltsVirtualKeyboard->setActiveWindw(activeForm); - if (parent) - s_ltsVirtualKeyboard->setParent(parent); - s_ltsVirtualKeyboard->setSession(session); - - return s_ltsVirtualKeyboard; - } - cleanupLtsVirtualKeyboard(); - lastState = onlyNumericPad; - s_ltsVirtualKeyboard = new LtsVirtualKeyboard(session, activeForm, onlyNumericPad, parent); - s_ltsVirtualKeyboard->createKeyboard(); - qAddPostRoutine(&cleanupLtsVirtualKeyboard); - - return s_ltsVirtualKeyboard; -} - -void LtsVirtualKeyboard::cleanupLtsVirtualKeyboard() -{ - delete s_ltsVirtualKeyboard; - s_ltsVirtualKeyboard = nullptr; -} - -void LtsVirtualKeyboard::enableSwitchingEcho(bool status) -{ - m_enablePasswordEcho = status; -} - -bool LtsVirtualKeyboard::isEnabledSwitchingEcho() const -{ - return (m_enablePasswordEcho); -} - -LtsKeyPushButton *LtsVirtualKeyboard::createNewKey(const QString &keyValue, const QString ¬TranslatedValue, const QString ¬TranslatedValueAlt) -{ - QString altText = notTranslatedValueAlt; - if (altText.isEmpty()) - altText = notTranslatedValue; - LtsKeyPushButton *tmp = new LtsKeyPushButton(notTranslatedValue, altText, this); - QString style = QStringLiteral(DEFAULT_STYLE_BUTTON) + QStringLiteral(DEFAULT_BACKGROUND_BUTTON); - QSize imageDim; - int width = 0, height = 0; - - imageDim.setWidth(32); - imageDim.setHeight(32); - - if (IS_PASSWORD(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setIcon(QPixmap(QStringLiteral(":/TastieraVirtuale/normalEcho"))); - } - else if (IS_PASTE(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setEnabled(false); - tmp->setIcon(QPixmap(QStringLiteral("icon:edit-paste.svg"))); - tmp->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - else if (IS_COPY(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setIcon(QPixmap(QStringLiteral("icon:edit-copy.svg"))); - tmp->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - - else if (IS_CUT_LEFT(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setIcon(QPixmap(QStringLiteral("icon:edit-cut.svg"))); - tmp->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - - else if (IS_CAPS(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setIcon(QPixmap(QStringLiteral("icon:shift_capslook.svg"))); - tmp->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - - else if (IS_RETURN(keyValue) == true) - { - tmp->setIconSize(imageDim); - tmp->setIcon(QPixmap(QStringLiteral("icon:carriage_return.svg"))); - tmp->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - - tmp->setStyleSheet(style); - - - width = 54; - height = 40; -#ifdef TABLET_MODE - width = width * 2.0; - height = height * 2.0; -#endif - - tmp->setMinimumSize(width, height); - tmp->setMaximumSize(width, height); - - if (IS_SPACE(keyValue) == true) - { - tmp->setMinimumSize(width * 20, height); - tmp->setMaximumSize(width * 20, height); - } - - if (IS_FIN(keyValue) == true) - { - tmp->setStyleSheet(style + QStringLiteral(CHANGED_BACKGROUND_BUTTON)); - } - tmp->setText(keyValue); - tmp->setCapsMode(m_isCap); - - - tmp->setObjectName(keyValue); - - return tmp; -} - -void LtsVirtualKeyboard::controlKeyEcho(QWidget *control) -{ - Q_UNUSED(control); - QString textToSearch; - LtsKeyPushButton *echoControlKey; - QString tmpStyle; - QPushButton *pasteButton = findChild(KEY_PASTE); - QPushButton *cutButton = findChild(KEY_CUT_LEFT); - QPushButton *copyButton = findChild(KEY_COPY); - - textToSearch = KEY_HIDECHAR; - - if (pasteButton != nullptr && copyButton != nullptr) - { - cutButton->setEnabled(true); - copyButton->setEnabled(true); - if (m_clipboard->text().length() > 0) - pasteButton->setEnabled(true); - else - pasteButton->setEnabled(false); - } - - echoControlKey = findChild(textToSearch); - if (echoControlKey != nullptr) - { - echoControlKey->setStyleSheet(QStringLiteral(DEFAULT_STYLE_BUTTON) + QStringLiteral(DEFAULT_BACKGROUND_BUTTON) + tmpStyle); - echoControlKey->setIcon(QPixmap(QStringLiteral(":/TastieraVirtuale/normalEcho"))); - - echoControlKey->repaint(); - QApplication::processEvents(); - } -} - -void LtsVirtualKeyboard::setTextToCurrentWidget(const QString &text) -{ - - - if (m_nextInput->inherits("QLineEdit")) - qobject_cast(m_nextInput)->setText(text); - - if (m_nextInput->inherits("QTextEdit")) - qobject_cast(m_nextInput)->setText(text); - - if (m_nextInput->inherits("QPlainTextEdit")) - qobject_cast(m_nextInput)->setPlainText(text); - - if (m_nextInput->inherits("QSpinBox")) - qobject_cast(m_nextInput)->setValue(text.toInt()); - - if (m_nextInput->inherits("QDoubleSpinBox")) - qobject_cast(m_nextInput)->setValue(text.toDouble()); -} - -QString LtsVirtualKeyboard::getTextFromCurrentWidget() -{ - if (qobject_cast(m_nextInput)) - return qobject_cast(m_nextInput)->text(); - - if (qobject_cast(m_nextInput)) - return qobject_cast(m_nextInput)->toPlainText(); - if (qobject_cast(m_nextInput)) - return qobject_cast(m_nextInput)->toPlainText(); - if (qobject_cast(m_nextInput)) - return QString::number(qobject_cast(m_nextInput)->value()); - if (qobject_cast(m_nextInput)) - return QString::number(qobject_cast(m_nextInput)->value()); - return QString(); - -} - -QString LtsVirtualKeyboard::getCurretText() const -{ - if (m_te->isVisible()) - return m_te->toPlainText(); - return m_le->text(); -} - -void LtsVirtualKeyboard::setCurrentText(const QString &text) -{ - if(m_te->isVisible()) - m_te->setText(text); - else - m_le->setText(text); -} - -int LtsVirtualKeyboard::getCurrentTextCursorPosition() -{ - if(m_te->isVisible()) - return m_te->textCursor().position(); - return m_le->cursorPosition(); -} - -void LtsVirtualKeyboard::setCurrentTextCursorPosition(int cursor) -{ - if(m_te->isVisible()) - { - QTextCursor cur = m_te->textCursor(); - cur.setPosition(cursor); - m_te->setTextCursor(cur); - }else - { - m_le->setCursorPosition(cursor); - } - -} - -bool LtsVirtualKeyboard::hasSelection() const -{ - if(m_te->isVisible()) - return m_te->textCursor().hasSelection(); - return m_le->hasSelectedText(); -} - -QString LtsVirtualKeyboard::getSelectedText() const -{ - if (m_te->isVisible()) - return m_te->textCursor().selectedText(); - return m_le->selectedText(); -} - -int LtsVirtualKeyboard::getSelectionStart() const -{ - if (m_te->isVisible()) - return m_te->textCursor().selectionStart(); - return m_le->selectionStart(); - -} - -void LtsVirtualKeyboard::reinit() -{ - qDeleteAll(findChildren()); - m_te->deleteLater(); - m_le->deleteLater(); - delete layout(); - createKeyboard(); -} - - -void LtsVirtualKeyboard::receiptChildKey(QWidget *focusThisControl, QKeyEvent *event, bool reset) -{ - - if (!m_ready) - return; - - if (reset == true) - { - m_nextInput = getNextTextbox(focusThisControl, true); - return; - } - if (m_nextInput == nullptr || !event) - { - return; - } - - - QString newKey = event->text(); - QString tmpReceiptString = getCurretText(); - int tmpPos = getCurrentTextCursorPosition(); - if (isNumericPad()) - newKey = newKey.trimmed(); - if (NO_SPECIAL_KEY(newKey) == false) - { - - if (IS_RETURN(newKey) == true) - { - tmpReceiptString = tmpReceiptString.insert(tmpPos, QChar(13)); - - setTextToCurrentWidget(tmpReceiptString); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(tmpPos+1); - } - else if (IS_FIN(newKey) == true) - { - - hide(true); - - - if (!qobject_cast(m_nextInput) && !qobject_cast(m_nextInput)) - { - QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); - QApplication::sendEvent(m_nextInput, &event); - } - m_nextInput->clearFocus(); - m_nextInput = nullptr; - - return; - } - - - - - - - - - - - - - - - else if (IS_BACK(newKey) == true) - { - if (!hasSelection()) - { - tmpReceiptString = tmpReceiptString.remove(tmpPos - 1, 1); - setTextToCurrentWidget(tmpReceiptString); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(tmpPos-1); - } - else - { - if (tmpPos - 1 >= 0) - { - int selectionStart = getSelectionStart(); - tmpReceiptString = tmpReceiptString.remove(selectionStart, getSelectedText().size()); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(selectionStart); - setTextToCurrentWidget(tmpReceiptString); - } - } - - } - else if (IS_CANC(newKey) == true) - { - tmpReceiptString = tmpReceiptString.remove(tmpPos, 1); - setTextToCurrentWidget(tmpReceiptString); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(tmpPos); - } - else if (IS_COPY(newKey) == true || IS_CUT_LEFT(newKey) == true) - { - LtsKeyPushButton *button = findChild(KEY_PASTE); - - if (button != nullptr) - { - if (hasSelection()) - { - m_clipboard->setText(getSelectedText()); - - if (IS_CUT_LEFT(newKey) == true) - { - int selectionStart =getSelectionStart(); - tmpReceiptString = tmpReceiptString.remove(selectionStart, getSelectedText().size()); - setCurrentText(tmpReceiptString); - - setCurrentTextCursorPosition(selectionStart); - - setTextToCurrentWidget(tmpReceiptString); - } - button->setEnabled(true); - } - else - button->setEnabled(false); - } - } - else if (IS_PASTE(newKey) == true) - { - tmpReceiptString.insert(tmpPos, m_clipboard->text()); - setTextToCurrentWidget(tmpReceiptString); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(tmpPos + m_clipboard->text().length()); - } - else if (IS_ALT(newKey) == true || IS_ALT2(newKey) == true) - { - QList kList = findChildren(); - m_isAlt = !m_isAlt; - - auto i = kList.begin(); - - for (; i != kList.end(); ++i) - { - LtsKeyPushButton *tw = *i; - tw->setAltMode(m_isAlt); - } - } - else if (IS_CAPS(newKey) == true) - { - QList kList = findChildren(); - m_isCap = !m_isCap; - - auto i = kList.begin(); - - for (; i != kList.end(); ++i) - { - LtsKeyPushButton *tw = *i; - tw->setCapsMode(m_isCap); - } - } - } - else - { - if (getSelectedText().size()) - { - tmpPos = getSelectionStart(); - tmpReceiptString = tmpReceiptString.remove(tmpPos, getSelectedText().size()); - } - - tmpReceiptString = tmpReceiptString.insert(tmpPos, newKey); - setTextToCurrentWidget(tmpReceiptString); - setCurrentText(tmpReceiptString); - setCurrentTextCursorPosition(tmpPos + 1); - } -} - - -void LtsVirtualKeyboard::switchKeyEchoMode(QWidget *control) -{ - setStatusEchoMode(!statusEchoMode()); - controlKeyEcho(control); -} - - -QWidget *LtsVirtualKeyboard::getNextTextbox(QWidget *thisControl, bool reset) -{ - QWidget *tmpInputFound = nullptr; - m_echoMode = false; - if (m_activeWindow == nullptr) - { - m_nextInput = nullptr; - return (nullptr); - } - if (thisControl != nullptr) - { - if (m_activeWindow->findChild(thisControl->objectName()) == 0 && - m_activeWindow->findChild(thisControl->objectName()) == 0 && - m_activeWindow->findChild(thisControl->objectName()) == 0 && - m_activeWindow->findChild(thisControl->objectName()) == 0 && - m_activeWindow->findChild(thisControl->objectName()) == 0) - { - return (nullptr); - } - else - { - return setCurrentTextStyle(thisControl); - } - } - if (m_nextInput == nullptr || reset == true) - m_nextInput = m_activeWindow->nextInFocusChain(); - else - m_nextInput = m_nextInput->nextInFocusChain(); - do - { - if (m_nextInput->inherits("QLineEdit") == true) - { - tmpInputFound = qobject_cast(m_nextInput); - return setCurrentTextStyle(tmpInputFound); - } - if (m_nextInput->inherits("QTextEdit") == true) - { - tmpInputFound = qobject_cast(m_nextInput); - return setCurrentTextStyle(tmpInputFound); - } - if (m_nextInput->inherits("QPlainTextEdit") == true) - { - tmpInputFound = qobject_cast(m_nextInput); - return setCurrentTextStyle(tmpInputFound); - } - if (m_nextInput->inherits("QSpinBox") == true) - { - tmpInputFound = qobject_cast(m_nextInput); - return setCurrentTextStyle(tmpInputFound); - } - if (m_nextInput->inherits("QDoubleSpinBox") == true) - { - tmpInputFound = qobject_cast(m_nextInput); - return setCurrentTextStyle(tmpInputFound); - } - m_nextInput = m_nextInput->nextInFocusChain(); - } - while (m_nextInput != nullptr); - return nullptr; -} - - -QWidget *LtsVirtualKeyboard::setCurrentTextStyle(QWidget *control) -{ - m_nextInput = control; - control->setStyleSheet(QStringLiteral(STYLE_FOCUS_TEXT)); - return (control); -} - -QWidget *LtsVirtualKeyboard::setDefaultTextStyle(QWidget *control) -{ - if (!m_nextInput) - return nullptr; - QLineEdit le; - control->setStyleSheet(le.styleSheet()); - return (control); -} - -QSize LtsVirtualKeyboard::availableDesktopSize() const -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) - return QApplication::desktop()->availableGeometry().size(); -#else - const QWindow *win = window()->windowHandle(); - const QScreen *scr = (win && win->screen()) ? win->screen() : QGuiApplication::primaryScreen(); - return scr ? scr->availableSize() : QSize(1280, 1024); -#endif -} - -void LtsVirtualKeyboard::init_keyboard(QWidget *focusThisControl) -{ - m_nextInput = focusThisControl; - checkInput(); - - receiptChildKey(focusThisControl, nullptr, true); -} - -void LtsVirtualKeyboard::show(QWidget *first, bool frameless) -{ - Q_UNUSED(frameless); - init_keyboard(first); - - if (windowState() == Qt::WindowMinimized) - setWindowState(Qt::WindowNoState); - setStatusEchoMode(false); - setVisible(true); - - const auto availSize = availableDesktopSize(); - move((availSize.width() - width()) / 2, - (availSize.height() - height()) - 10); - QTimer::singleShot(300, this, [this]() { m_ready = true; }); -} - - -void LtsVirtualKeyboard::borderFrame(bool visible) -{ - if (visible == true) - setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint); - else - setWindowFlags(Qt::Window | Qt::FramelessWindowHint); -} - -void LtsVirtualKeyboard::checkInput() -{ - m_enableEnter = false; - m_echoMode = false; - - m_le->setVisible(true); - m_te->setVisible(false); - if (qobject_cast(m_nextInput)) - { - QLineEdit *le = qobject_cast(m_nextInput); - m_le->setEchoMode(le->echoMode()); - } - if (qobject_cast(m_nextInput) || qobject_cast(m_nextInput)) - { - m_le->setVisible(false); - m_te->setVisible(true); - m_enableEnter = true; - } - - LtsKeyPushButton *crButton = findChild(KEY_RETURN); - if (crButton) - { - crButton->setEnabled(m_enableEnter); - } - - if (m_te->isVisible()) - setMaximumHeight(m_height+m_te->height()); - else - setMaximumHeight(m_height+m_le->height()); - -} - -bool LtsVirtualKeyboard::isNumericPad() const -{ - return (m_numericPad); -} - -void LtsVirtualKeyboard::focusThis(QWidget *control, bool numpad) -{ - m_ready = false; - if (m_activeWindow != nullptr) - { - if (m_numericPad != numpad) - { - m_numericPad = numpad; - reinit(); - } - - if (m_nextInput != nullptr) - m_nextInput = setDefaultTextStyle(m_nextInput); - init_keyboard(control); - controlKeyEcho(control); - - setCurrentText(getTextFromCurrentWidget()); - if(m_te->isVisible()) - m_te->moveCursor(QTextCursor::Start); - else - m_le->end(false); - if (m_nextInput) - setWindowTitle(m_nextInput->toolTip()); - } - QTimer::singleShot(300, this, [this]() { m_ready = true; }); - const auto availSize = availableDesktopSize(); - if (isNumericPad()) - move((availSize.width() - width()) / 2, - (availSize.height() - height()) - 10); - else - move(10, availSize.height() - height() - 10); -} - -void LtsVirtualKeyboard::closeEvent(QCloseEvent *event) -{ - event->ignore(); -} - -void LtsVirtualKeyboard::hide(bool changeColor) -{ - - m_ready = false; - try - { - if (changeColor == true) - { - if (m_activeWindow != nullptr) - setDefaultTextStyle(m_nextInput); - } - if (!parent()) - setVisible(false); - - } - catch (...) - { - } -} - - -void LtsVirtualKeyboard::createKeyboard() -{ - LtsKeyPushButton *tmp = nullptr; - QVBoxLayout *tmpVLayout = new QVBoxLayout; - QHBoxLayout *tmpLayout = new QHBoxLayout; - QString tmpStyle; - QString backspace; - m_ready = false; - - m_te = new QTextEdit(this); - m_te->setMaximumHeight(210); - m_te->setMinimumHeight(210); - - m_te->setObjectName(QStringLiteral("internalLE")); - - tmpVLayout->addWidget(m_te); - - m_te->hide(); - - - m_le = new QLineEdit(this); - m_le->setObjectName(QStringLiteral("internalLE")); - - tmpVLayout->addWidget(m_le); - - - if (isNumericPad() == true) - { - QGridLayout *gridLayout = new QGridLayout(this); - gridLayout->addLayout(tmpVLayout,0,0); - for (short i = 49; i <= 51; i++) - tmpLayout->addWidget(createNewKey(QChar(i), QString::number(i - 48), QString::number(i - 48))); - gridLayout->addLayout(tmpLayout, 3, 0); - tmpLayout = new QHBoxLayout(this); - for (short i = 52; i <= 54; i++) - tmpLayout->addWidget(createNewKey(QChar(i), QString::number(i - 48), QString::number(i - 48))); - gridLayout->addLayout(tmpLayout, 2, 0); - tmpLayout = new QHBoxLayout(this); - for (short i = 55; i <= 57; i++) - tmpLayout->addWidget(createNewKey(QChar(i), QString::number(i - 48), QString::number(i - 48))); - gridLayout->addLayout(tmpLayout, 1, 0); - tmpLayout = new QHBoxLayout(this); - tmp = createNewKey(QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("0")); - tmp->setMinimumSize(tmp->minimumSize().width() * 2, tmp->minimumSize().height()); - tmpLayout->addWidget(tmp); - tmpLayout->addWidget(createNewKey(QStringLiteral("."), QStringLiteral(" . "), QStringLiteral(" . "))); - gridLayout->addLayout(tmpLayout, 4, 0); - tmpLayout = new QHBoxLayout(this); - tmpLayout->addWidget(createNewKey(QStringLiteral(","), QStringLiteral(" , "), QStringLiteral(" , "))); - backspace = QStringLiteral("BACKSPACE"); - tmpStyle = (KEY_BACKSPACE); - - tmp = createNewKey(tmpStyle, backspace); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.5); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.5); - tmpLayout->addWidget(tmp); - - tmp = createNewKey(KEY_FIN,KEY_FIN); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.5); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.5); - tmpLayout->addWidget(tmp); - gridLayout->addLayout(tmpLayout, 5, 0); - - - - setLayout(gridLayout); - setMaximumWidth(tmp->width()*3); - m_height= tmp->height()*5; - } - else - { - - - - tmpLayout->addWidget(createNewKey(QStringLiteral("^"), QStringLiteral("^"), QStringLiteral("^"))); - - tmpLayout->addWidget(createNewKey(QStringLiteral("1"), QStringLiteral("1"), QStringLiteral("!"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("2"), QStringLiteral("2"), QStringLiteral("\""))); - tmpLayout->addWidget(createNewKey(QStringLiteral("3"), QStringLiteral("3"), QChar(207))); - tmpLayout->addWidget(createNewKey(QStringLiteral("4"), QStringLiteral("4"), QStringLiteral("$"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("5"), QStringLiteral("5"), QStringLiteral("%"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("6"), QStringLiteral("6"), QStringLiteral("&&"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("7"), QStringLiteral("7"), QStringLiteral("/"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("8"), QStringLiteral("8"), QStringLiteral("("))); - tmpLayout->addWidget(createNewKey(QStringLiteral("9"), QStringLiteral("9"), QStringLiteral(")"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("="))); - tmpLayout->addWidget(createNewKey(QChar(223), QChar(223), QStringLiteral("?"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("`"), QChar(180), QStringLiteral("`"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("["), QStringLiteral("["), QStringLiteral("{"))); - backspace = QStringLiteral("<----"); - tmpStyle = KEY_BACKSPACE; - tmp = createNewKey(tmpStyle, backspace); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.2); - tmp->setMinimumWidth(tmp->maximumWidth() * 1.2); - tmpLayout->addWidget(tmp); - tmpLayout->insertStretch(-1, 1); - tmpVLayout->addLayout(tmpLayout); - - - - tmpLayout = new QHBoxLayout(); - - QVBoxLayout *layoutReturn = new QVBoxLayout(); - tmp = createNewKey(KEY_ALT, KEY_ALT, KEY_ALT2); - tmp->setMaximumWidth(tmp->maximumWidth()); - tmp->setMinimumWidth(tmp->minimumWidth()); - tmpLayout->addWidget(tmp); - - tmpLayout->addWidget(createNewKey(QStringLiteral("Q"), QStringLiteral("Q"), QStringLiteral("@"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("W"), QStringLiteral("W"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("E"), QStringLiteral("E"), QChar(8364))); - tmpLayout->addWidget(createNewKey(QStringLiteral("R"), QStringLiteral("R"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("T"), QStringLiteral("T"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("Z"), QStringLiteral("Z"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("U"), QStringLiteral("U"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("I"), QStringLiteral("I"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("O"), QStringLiteral("O"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("P"), QStringLiteral("P"))); - tmpLayout->addWidget(createNewKey(QChar(220), QChar(220))); - tmpLayout->addWidget(createNewKey(QStringLiteral("+"), QStringLiteral("+"), QStringLiteral("*"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("]"), QStringLiteral("]"), QStringLiteral("}"))); - tmpStyle = KEY_CANC; - tmp = createNewKey(tmpStyle, QStringLiteral("->")); - tmp->setMaximumWidth(tmp->maximumWidth()); - tmp->setMinimumWidth(tmp->minimumWidth()); - tmpLayout->addWidget(tmp); - tmpLayout->insertStretch(-1, 1); - - - - - layoutReturn->addLayout(tmpLayout, 1); - - - tmpLayout = new QHBoxLayout(); - tmp = createNewKey(KEY_CAPS, KEY_CAPS); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.5 + 5); - tmpLayout->addWidget(tmp); - tmpLayout->addWidget(createNewKey(QStringLiteral("A"), QStringLiteral("A"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("S"), QStringLiteral("S"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("D"), QStringLiteral("D"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("F"), QStringLiteral("F"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("G"), QStringLiteral("G"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("H"), QStringLiteral("H"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("J"), QStringLiteral("J"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("K"), QStringLiteral("K"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("L"), QStringLiteral("L"))); - tmpLayout->addWidget(createNewKey(QChar(214), QChar(214))); - tmpLayout->addWidget(createNewKey(QChar(196), QChar(196))); - tmpLayout->addWidget(createNewKey(QStringLiteral("'"), QStringLiteral("'"), QStringLiteral("#"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("|"), QStringLiteral("|"), QStringLiteral("\\"))); - tmpLayout->insertStretch(-1, 1); - - layoutReturn->setSpacing(5); - layoutReturn->addLayout(tmpLayout, 1); - - tmpLayout = new QHBoxLayout(); - tmpLayout->addLayout(layoutReturn, 1); - - - tmp = createNewKey(KEY_RETURN, QStringLiteral("RETURN")); - tmp->setEnabled(m_enableEnter); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.4); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.4); - tmp->setMinimumHeight(tmp->minimumHeight() * 1.4); - - tmpLayout->addWidget(tmp, 2); - - - tmpVLayout->addLayout(tmpLayout); - - - - tmpLayout = new QHBoxLayout(); - tmp = createNewKey(KEY_CUT_LEFT, KEY_CUT_LEFT); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.2); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.2); - tmpLayout->addWidget(tmp); - tmpLayout->addWidget(createNewKey(QStringLiteral("<"), QStringLiteral("<"), QStringLiteral(">"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("Y"), QStringLiteral("Y"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("X"), QStringLiteral("X"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("C"), QStringLiteral("C"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("V"), QStringLiteral("V"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("B"), QStringLiteral("B"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("N"), QStringLiteral("N"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("M"), QStringLiteral("M"))); - tmpLayout->addWidget(createNewKey(QStringLiteral(","), QStringLiteral(","), QStringLiteral(";"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("."), QStringLiteral("."), QStringLiteral(":"))); - tmpLayout->addWidget(createNewKey(QStringLiteral("-"), QStringLiteral("-"), QStringLiteral("_"))); - tmpLayout->insertStretch(-1, 1); - - - tmpVLayout->addLayout(tmpLayout); - tmpLayout = new QHBoxLayout(); - - tmp = createNewKey(KEY_COPY, KEY_COPY); - tmp->setMaximumWidth(tmp->maximumWidth()); - tmp->setMinimumWidth(tmp->maximumWidth()); - tmpLayout->addWidget(tmp); - - tmp = createNewKey(KEY_PASTE, KEY_PASTE); - tmp->setMaximumWidth(tmp->maximumWidth()); - tmp->setMinimumWidth(tmp->maximumWidth()); - tmpLayout->addWidget(tmp); - - QWidget *empty = new QWidget(this); - empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - tmpLayout->addWidget(empty); - - tmp = createNewKey(QStringLiteral(KEY_SPACE), QStringLiteral(" ")); - tmp->setMinimumWidth(300); - tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - tmpLayout->addWidget(tmp, 3); - - empty = new QWidget(this); - empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - tmpLayout->addWidget(empty); - - tmp = createNewKey(KEY_FIN, KEY_FIN); - tmp->setMaximumWidth(tmp->maximumWidth() * 1.5); - tmp->setMinimumWidth(tmp->minimumWidth() * 1.5); - tmpLayout->addWidget(tmp); - - tmpVLayout->addLayout(tmpLayout); - setLayout(tmpVLayout); - - m_height = tmp->height()*5+10; - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - } - - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); -} - -} diff --git a/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h b/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h deleted file mode 100755 index 2478bc10d..000000000 --- a/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h +++ /dev/null @@ -1,97 +0,0 @@ - - - -#pragma once - -#include "LtsKeyPushButton.h" -#include -#include - -class QClipboard; -class QLabel; -class QLineEdit; -class QSize; -class QTextEdit; - -namespace LtsVirtualKeyboard -{ - -class LTSVIRTUALKEYBOARD_EXPORT LtsVirtualKeyboard : public QWidget -{ - Q_OBJECT - - -private: - LtsVirtualKeyboard(LtsSession::LtsSession *session, QWidget *activeForm = nullptr, bool onlyNumericPad = false, QWidget *parent = nullptr); - ~LtsVirtualKeyboard(); - -public: - static LtsVirtualKeyboard *getInstance(LtsSession::LtsSession *session, QWidget *activeForm = nullptr, bool onlyNumericPad = false, QWidget *parent = nullptr); - static void cleanupLtsVirtualKeyboard(); - - void createKeyboard(); - bool statusEchoMode() const { return (m_echoMode); } - void setStatusEchoMode(bool echo) { m_echoMode = echo; } - void receiptChildKey(QWidget *focusThisControl = nullptr, QKeyEvent *event = nullptr, bool reset = false); - void switchKeyEchoMode(QWidget *control); - void enableSwitchingEcho(bool status); - bool isEnabledSwitchingEcho() const; - void borderFrame(bool visible = true); - void checkInput(); - QWidget *setDefaultTextStyle(QWidget *control); - bool isNumericPad() const; - QLabel *zoomedWidget(); - void setActiveWindw(QWidget *widget) { m_activeWindow = widget; } - void setSession(LtsSession::LtsSession *session) { m_session= session; } - - - - - -public slots: - void show(QWidget *first = nullptr, bool frameless = false); - void hide(bool changeColor); - void focusThis(QWidget *control, bool numpad = false); - -protected: - void closeEvent(QCloseEvent *event) override; - -private: - QSize availableDesktopSize() const; - void init_keyboard(QWidget *focusThisControl); - QWidget *setCurrentTextStyle(QWidget *control); - LtsKeyPushButton *createNewKey(const QString &keyValue, const QString ¬TranslatedValue, const QString ¬TranslatedValueAlt = QString()); - QWidget *getNextTextbox(QWidget *thisControl = nullptr, bool reset = false); - void controlKeyEcho(QWidget *control); - void setTextToCurrentWidget(const QString &text); - QString getTextFromCurrentWidget(); - QString getCurretText()const; - void setCurrentText(const QString &text); - int getCurrentTextCursorPosition(); - void setCurrentTextCursorPosition(int cursor); - bool hasSelection() const; - QString getSelectedText() const; - int getSelectionStart() const; - void reinit(); -private: - - QTextEdit *m_te; - QLineEdit *m_le; - - QWidget *m_nextInput; - QWidget *m_activeWindow; - bool m_echoMode; - bool m_enablePasswordEcho; - bool m_numericPad; - bool m_isAlt; - bool m_isCap; - bool m_enableEnter; - bool m_ready; - - int m_height; - QClipboard *m_clipboard; - LtsSession::LtsSession *m_session; - -}; - -} diff --git a/Lts/Common/LtsWebSocket/CMakeLists.txt b/Lts/Common/LtsWebSocket/CMakeLists.txt deleted file mode 100755 index 68c7cb5f8..000000000 --- a/Lts/Common/LtsWebSocket/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Philipp Dassow, SLST, 2017-11-28 - -# -# --- Library LtsWebSocketCommon -------------------------------------------------------------------------------- -# -SET(LtsWebSocketCommon_SRCS - client_message_factory.cpp - client_message_factory.h - client_message.cpp - client_message.h - reply_message.cpp - reply_message.h - data_manager_base_dl.h - json_keys.h - - logger.cpp - logger.h -) - -NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketCommon SHARED ${LtsWebSocketCommon_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketCommon - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWebSocketCommon - Qt5::Core - Qt5::WebSockets - NGLLogger -) - -# -# --- Library ltsWebSocketServer -------------------------------------------------------------------------------- -# -SET(LtsWebSocketServer_SRCS - client_connection.cpp - client_connection.h - connection_handler.cpp - connection_handler.h - lts_web_socket_server.cpp - lts_web_socket_server.h -) - -NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketServer SHARED ${LtsWebSocketServer_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketServer - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWebSocketServer - Qt5::Core - LtsWebSocketCommon -) - -# -# --- Library ltsWebSocketClient -------------------------------------------------------------------------------- -# -SET(LtsWebSocketClient_SRCS - lts_web_socket_client.cpp - lts_web_socket_client.h -) - -NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketClient SHARED ${LtsWebSocketClient_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketClient - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWebSocketClient - Qt5::Core - LtsWebSocketCommon -) diff --git a/Lts/Common/LtsWebSocket/client_connection.cpp b/Lts/Common/LtsWebSocket/client_connection.cpp deleted file mode 100755 index 1b8bdf8d1..000000000 --- a/Lts/Common/LtsWebSocket/client_connection.cpp +++ /dev/null @@ -1,84 +0,0 @@ - - -#include "client_connection.h" - -#include - -namespace LtsWebSocket -{ - -ClientConnection::ClientConnection(QWebSocket *socket, QObject *parent) - : QThread(parent) - , m_socket(socket ? socket : new QWebSocket) - , m_logger(getLogger("ClientConnection")) -{ -} - -ClientConnection::~ClientConnection() -{ - DEBUG_LOG(m_logger) << "Thread closed" << this; - delete m_socket; - qDeleteAll(m_messageQueue); -} - -bool ClientConnection::init() -{ - if (m_socket == nullptr) - { - ERROR_LOG(m_logger) << "Initialisation failed due to an invalid socket"; - return false; - } - - DEBUG_LOG(m_logger) << "Started new thread" << this; - connect(m_socket, &QWebSocket::textMessageReceived, this, &ClientConnection::onNewTextMessageFromSocket, Qt::DirectConnection); - connect(m_socket, &QWebSocket::connected, this, &ClientConnection::sendMessagesFromQueue); - connect(m_socket, &QWebSocket::disconnected, this, &ClientConnection::onSocketDisconnect); - - return true; -} - -void ClientConnection::addReply(ReplyMessage *message) -{ - if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState) - { - m_messageQueue.enqueue(message); - DEBUG_LOG(m_logger) << "Adding message to queue, size: " << m_messageQueue.size(); - return; - } - const QString msg(message->toJsonString()); - DEBUG_LOG(m_logger) << "Sending message: " << msg ; - m_socket->sendTextMessage(msg); - delete message; -} - -void ClientConnection::onNewTextMessageFromSocket(const QString &message) -{ - DEBUG_LOG(m_logger) << "Recv msg: " << message << this; - emit newTextMessage(this, message); -} - -void ClientConnection::sendMessagesFromQueue() -{ - INFO_LOG(m_logger) << "Client connected ip: " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort(); - - if (m_socket->state() != QAbstractSocket::ConnectedState) - return; - while (!m_messageQueue.isEmpty()) - { - ReplyMessage *message = m_messageQueue.dequeue(); - const QString msg(message->toJsonString()); - TRACE_LOG(m_logger) << "Send msg: " << msg; - m_socket->sendTextMessage(msg); - delete message; - DEBUG_LOG(m_logger) << "Sent message from queue, new size: " << m_messageQueue.size(); - } -} - -void ClientConnection::onSocketDisconnect() -{ - DEBUG_LOG(m_logger) << "Client disconnected ip: " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort(); - exit(0); - emit disconnected(this); -} - -} diff --git a/Lts/Common/LtsWebSocket/client_connection.h b/Lts/Common/LtsWebSocket/client_connection.h deleted file mode 100755 index e2f6e48d4..000000000 --- a/Lts/Common/LtsWebSocket/client_connection.h +++ /dev/null @@ -1,70 +0,0 @@ - - -#pragma once - -#include -#include - -#include "reply_message.h" -#include "logger.h" -#include - -class QWebSocket; - -namespace LtsWebSocket -{ - - - -class LTSWEBSOCKETSERVER_EXPORT ClientConnection - : public QThread - , public ReplyHandler -{ - Q_OBJECT -public: - - - explicit ClientConnection(QWebSocket *socket, QObject *parent = nullptr); - - - - - ~ClientConnection(); - - - - bool init(); - - - - void addReply(ReplyMessage *message) override; - - QWebSocket *webSocket() { return m_socket; } -signals: - - - void newTextMessage(ClientConnection *client, const QString &message); - - - - void disconnected(ClientConnection *client); - - -private slots: - void onNewTextMessageFromSocket(const QString &message); - void sendMessagesFromQueue(); - void onSocketDisconnect(); -private: - - - void sendMessages(); - -private: - QWebSocket *m_socket; - QQueue m_messageQueue; - - LOGGER_T m_logger; - - -}; -} diff --git a/Lts/Common/LtsWebSocket/client_message.cpp b/Lts/Common/LtsWebSocket/client_message.cpp deleted file mode 100755 index cb6d93881..000000000 --- a/Lts/Common/LtsWebSocket/client_message.cpp +++ /dev/null @@ -1,38 +0,0 @@ - - -#include "client_message.h" - -#include "json_keys.h" - -#include - -namespace LtsWebSocket -{ - - - - -InvalidMessage::InvalidMessage(ReplyHandler *client, const QString &uuid, const QString &errorText) - : AbstractClientMessage(client, QString(), 1, uuid) - , m_errorText(errorText) -{} - -void InvalidMessage::process() -{ - ErrorMessage::ErrorData errorData; - - errorData.errorClass = 1; - errorData.errorMessage = m_errorText; - errorData.uuid = m_uuid; - - m_client->addReply(new ErrorMessage(errorData)); -} - -bool InvalidMessage::hasValidData() const -{ - - return true; -} - - -} diff --git a/Lts/Common/LtsWebSocket/client_message.h b/Lts/Common/LtsWebSocket/client_message.h deleted file mode 100755 index fdd47d7df..000000000 --- a/Lts/Common/LtsWebSocket/client_message.h +++ /dev/null @@ -1,70 +0,0 @@ - - -#pragma once - -#include -#include "reply_message.h" - -#include -#include - -namespace LtsWebSocket -{ - - - -class LTSWEBSOCKETCOMMON_EXPORT AbstractClientMessage -{ -public: - AbstractClientMessage(ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data = QJsonValue()) - : m_type(type) - , m_typeId(typeId) - , m_uuid(uuid) - , m_data(data) - , m_client(client) - {} - - virtual ~AbstractClientMessage() = default; - - - - virtual void process() = 0; - - - - virtual bool hasValidData() const = 0; - - - - void reply(ReplyMessage *reply) - { - m_client->addReply(reply); - } - -protected: - const QString m_type; - const int m_typeId; - const QString m_uuid; - const QJsonValue m_data; - ReplyHandler *m_client; - -}; - - - - - -class LTSWEBSOCKETCOMMON_EXPORT InvalidMessage final : public AbstractClientMessage -{ -public: - explicit InvalidMessage(ReplyHandler *client, const QString &uuid, const QString &errorText); - - void process() override; - bool hasValidData() const override; - -private: - QString m_errorText; - -}; - -} diff --git a/Lts/Common/LtsWebSocket/client_message_factory.cpp b/Lts/Common/LtsWebSocket/client_message_factory.cpp deleted file mode 100755 index 26a9c20cf..000000000 --- a/Lts/Common/LtsWebSocket/client_message_factory.cpp +++ /dev/null @@ -1,69 +0,0 @@ - - - -#include "client_message_factory.h" - -#include "client_connection.h" -#include "json_keys.h" - -#include - -namespace LtsWebSocket -{ - - -QJsonParseError AbstractClientMessageFactory::parseMessage(const QString &message) -{ - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error); - - if (error.error != QJsonParseError::NoError) - return error; - - QJsonObject obj = doc.object(); - - m_data = obj.value(JsonKeys::DATA()); - m_type = obj.value(JsonKeys::TYPE()).toString(); - m_typeId = obj.value(JsonKeys::TYPEID()).toInt(); - m_uuid = obj.value(JsonKeys::UUID_()).toString(); - - return error; -} - -AbstractClientMessage *AbstractClientMessageFactory::checkAttributes(ReplyHandler *client, - const QString &message) const -{ - if (!m_uuid.isEmpty() && !m_type.isEmpty()) - return nullptr; - if (m_uuid.isEmpty()) - { - WARNING_LOG(m_logger) << "Got message without uuid: " << message << "'"; - return new LtsWebSocket::InvalidMessage(client, QString(), QStringLiteral("Got message without uuid")); - } - WARNING_LOG(m_logger) << "Got message no type: " << message << "'"; - return new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Message has no type")); -} - -AbstractClientMessage *AbstractClientMessageFactory::checkResult(ReplyHandler *client, - const QString &message, - AbstractClientMessage *result) const -{ - if (result == nullptr) - { - WARNING_LOG(m_logger) << "Could not parse message: " << message; - result = new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Invalid type")); - } - - if (!result->hasValidData()) - { - WARNING_LOG(m_logger) << "Resulting JSon message has invalid data... " << message; - delete result; - result = new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Has invalid data part")); - } - - return result; -} - - - -} diff --git a/Lts/Common/LtsWebSocket/client_message_factory.h b/Lts/Common/LtsWebSocket/client_message_factory.h deleted file mode 100755 index e262e704d..000000000 --- a/Lts/Common/LtsWebSocket/client_message_factory.h +++ /dev/null @@ -1,71 +0,0 @@ - - -#pragma once - -#include "client_message.h" -#include "reply_message.h" -#include - -#include -#include - -namespace NGL -{ -class INGLLogger; -} -namespace LtsWebSocket -{ - - - -class LTSWEBSOCKETCOMMON_EXPORT AbstractClientMessageFactory -{ -public: - explicit AbstractClientMessageFactory(NGL::INGLLogger &logger) - : m_logger(logger) - {} - - virtual ~AbstractClientMessageFactory() {} - - - - QJsonParseError parseMessage(const QString &message); - - - - virtual AbstractClientMessage *createMessage(ReplyHandler *client, const QString &message) = 0; - -protected: - AbstractClientMessage *checkAttributes(ReplyHandler *client, - const QString &message) const; - AbstractClientMessage *checkResult(ReplyHandler *client, - const QString &message, - AbstractClientMessage *result) const; -protected: - QString m_type; - int m_typeId; - QString m_uuid; - QJsonValue m_data; - NGL::INGLLogger &m_logger; - -}; - -class PongMessage final : public ReplyMessage -{ -public: - static QString strPong() { return QStringLiteral("pong"); } - static int idPong() { return 2; } - explicit PongMessage(const QString &payload) - : ReplyMessage(strPong(), idPong(), QString()) - , m_payload(payload) - {} - - QString toJsonString() const override - { - return m_payload.isEmpty() ? strPong() : QStringLiteral("%1 %2").arg(strPong(), m_payload); - } -private: - QString m_payload; -}; - -} diff --git a/Lts/Common/LtsWebSocket/connection_handler.cpp b/Lts/Common/LtsWebSocket/connection_handler.cpp deleted file mode 100755 index 88425f6fa..000000000 --- a/Lts/Common/LtsWebSocket/connection_handler.cpp +++ /dev/null @@ -1,89 +0,0 @@ - - -#include "connection_handler.h" - -#include "client_connection.h" - -#include - -using namespace LtsWebSocket; - -ConnectionHandler::ConnectionHandler(QObject *parent) - : QObject(parent) - , m_server(nullptr) - , m_logger(getLogger("ConnectionHandler")) -{} - -ConnectionHandler::~ConnectionHandler() -{ - for (auto connection : qAsConst(m_connections)) - connection->disconnect(this); -} - -bool ConnectionHandler::init(const QString &serviceName, unsigned short port) -{ - if (m_server != nullptr) - { - m_server->disconnect(); - delete m_server; - } - - m_server = new QWebSocketServer(QStringLiteral("RME-") + serviceName, QWebSocketServer::NonSecureMode, this); - - connect(m_server, &QWebSocketServer::newConnection, this, &ConnectionHandler::onNewConnection); - connect(m_server, &QWebSocketServer::serverError, this, &ConnectionHandler::onServerError); - - DEBUG_LOG(m_logger) << "Trying to listen "; - if (!m_server->listen(QHostAddress::Any, port)) - { - ERROR_LOG(m_logger) << "Not possible to listen to port " << port; - return false; - } - DEBUG_LOG(m_logger) << "... ok, port " << m_server->serverPort(); - - return true; -} - -void ConnectionHandler::sendToAllClients(ReplyMessage *message) -{ - for (auto connection : qAsConst(m_connections)) - connection->addReply(new ReplyMessage(*message)); - - delete message; -} - -void ConnectionHandler::onServerError(QWebSocketProtocol::CloseCode closeCode) -{ - ERROR_LOG(m_logger) << "Got server error with close code " << closeCode; -} - -void ConnectionHandler::onNewConnection() -{ - while (m_server->hasPendingConnections()) - { - QWebSocket *socket = m_server->nextPendingConnection(); - - ClientConnection *newConnection = new ClientConnection(socket, this); - connect(newConnection, &ClientConnection::destroyed, this, [this](QObject* o) - { - m_connections.remove(static_cast(o)); - }); - - if (!newConnection->init()) - { - ERROR_LOG(m_logger) << "Failed to initialise new client"; - delete newConnection; - newConnection = nullptr; - continue; - } - newConnection->start(); - - connect(newConnection, &ClientConnection::newTextMessage, this, &ConnectionHandler::newTextMessageFromClient); - connect(newConnection, &ClientConnection::disconnected, this, &ConnectionHandler::clientDisconnected); - connect(newConnection, &ClientConnection::finished, newConnection, &ClientConnection::deleteLater); - - m_connections.insert(newConnection); - emit clientConnected(newConnection); - INFO_LOG(m_logger) << "New client connected ip: " << socket->localAddress().toString() << ":" << socket->localPort(); - } -} diff --git a/Lts/Common/LtsWebSocket/connection_handler.h b/Lts/Common/LtsWebSocket/connection_handler.h deleted file mode 100755 index 1c7b23b8f..000000000 --- a/Lts/Common/LtsWebSocket/connection_handler.h +++ /dev/null @@ -1,64 +0,0 @@ - - -#pragma once - -#include -#include -#include - -#include "logger.h" - -namespace LtsWebSocket -{ -class MessageHandler; -class ClientConnection; -class ReplyMessage; - - - -class ConnectionHandler - : public QObject -{ - Q_OBJECT -public: - - - explicit ConnectionHandler(QObject *parent = nullptr); - ~ConnectionHandler(); - - - - bool init(const QString &serviceName, unsigned short port); - - - - void sendToAllClients(ReplyMessage *message); - - - - unsigned short webSocketPort() { return m_server->serverPort(); } - -signals: - - - void newTextMessageFromClient(ClientConnection *client, const QString &message); - - - - void clientDisconnected(ClientConnection *client); - - - - void clientConnected(ClientConnection *client); - -private slots: - void onServerError(QWebSocketProtocol::CloseCode closeCode); - void onNewConnection(); - -private: - QWebSocketServer *m_server; - QSet m_connections; - LOGGER_T m_logger; - -}; -} diff --git a/Lts/Common/LtsWebSocket/data_manager_base_dl.h b/Lts/Common/LtsWebSocket/data_manager_base_dl.h deleted file mode 100755 index d2abfd609..000000000 --- a/Lts/Common/LtsWebSocket/data_manager_base_dl.h +++ /dev/null @@ -1,48 +0,0 @@ - - -#pragma once -#include -#include - -#include "reply_message.h" - -namespace LtsWebSocket -{ - - -template -class DataManagerBaseDl -{ -protected: - - - explicit DataManagerBaseDl(const std::shared_ptr &dbInterface, NGL::INGLLogger &logger) - : m_logger(logger) - , m_dbInterface(dbInterface) - {} - - - - template - ReplyMessage *toBaseDataMessageFromDb(const QString &uuid) const - { - QVector dataVec; - - if (!m_dbInterface->select(dataVec)) - { - const QString errorMessage = QStringLiteral("Error reading data from database!"); - ERROR_LOG(m_logger) << errorMessage; - ErrorMessage::ErrorData errData; - errData.errorMessage = errorMessage; - errData.uuid = uuid; - return new ErrorMessage(errData); - } - - return new MessageType (m_dbInterface->template toJson(dataVec), uuid); - } - -protected: - NGL::INGLLogger &m_logger; - std::shared_ptr m_dbInterface; -}; -} diff --git a/Lts/Common/LtsWebSocket/json_keys.h b/Lts/Common/LtsWebSocket/json_keys.h deleted file mode 100755 index b5c69c09c..000000000 --- a/Lts/Common/LtsWebSocket/json_keys.h +++ /dev/null @@ -1,26 +0,0 @@ - - -#pragma once - -#include - -namespace LtsWebSocket -{ - - -namespace JsonKeys -{ - - inline QString DATA() { return QStringLiteral("data"); } - inline QString UUID_() { return QStringLiteral("uuid"); } - inline QString TYPE() { return QStringLiteral("type"); } - inline QString TYPEID() { return QStringLiteral("typeId"); } - - - inline QString ERROR_() { return QStringLiteral("error"); } - - - inline QString ERROR_MESSAGE() { return QStringLiteral("errorMessage"); } - inline QString ERROR_CLASS() { return QStringLiteral("errorClass"); } -} -} diff --git a/Lts/Common/LtsWebSocket/logger.cpp b/Lts/Common/LtsWebSocket/logger.cpp deleted file mode 100755 index 61d789a23..000000000 --- a/Lts/Common/LtsWebSocket/logger.cpp +++ /dev/null @@ -1,32 +0,0 @@ - - - -#include "logger.h" - -#include - -namespace LtsWebSocket -{ - -LOGGER_T getLogger(const char *name) -{ - static const char ns[] = "Lts.WebSocket."; - std::string loggerName; - const size_t len = strlen(name); - loggerName.reserve(sizeof(ns) + len); - loggerName.append(ns, sizeof(ns) - 1); - loggerName.append(name, len); - - try - { - return NGL::NGLLoggerBootstrap::getNGLRootLogger(). - getNGLLogger(loggerName); - } - catch (const std::exception &ex) - { - fprintf(stderr, "Error retrieving logger: %s", ex.what()); - abort(); - } -} - -} diff --git a/Lts/Common/LtsWebSocket/logger.h b/Lts/Common/LtsWebSocket/logger.h deleted file mode 100755 index 0e1cb1771..000000000 --- a/Lts/Common/LtsWebSocket/logger.h +++ /dev/null @@ -1,18 +0,0 @@ - - - -#pragma once - -#include -#include - -namespace LtsWebSocket -{ -typedef NGL::INGLLogger &LOGGER_T; - -LTSWEBSOCKETCOMMON_EXPORT LOGGER_T getLogger(const char *name); -inline LOGGER_T getLogger(const QString &loggerName) -{ - return getLogger(loggerName.toUtf8().constData()); -} -} diff --git a/Lts/Common/LtsWebSocket/lts_web_socket_client.cpp b/Lts/Common/LtsWebSocket/lts_web_socket_client.cpp deleted file mode 100755 index 31d1d13b6..000000000 --- a/Lts/Common/LtsWebSocket/lts_web_socket_client.cpp +++ /dev/null @@ -1,202 +0,0 @@ - - -#include "lts_web_socket_client.h" - -#include -#include -#include - -#include "client_connection.h" - -namespace LtsWebSocket -{ -class WebSocketHelper : public QObject -{ - Q_OBJECT -public: - WebSocketHelper(AbstractClientMessageFactory *messageFactory, - LtsWebSocketClient *webSocketClient, - NGL::INGLLogger &logger); - ~WebSocketHelper(); - -public Q_SLOTS: - void doOpen(const QString &hostname, const unsigned short port); - void doClose(); - void doSendTextMessage(const QString &msg); - void onConnected(); - void onDisconnected(); - void onError(QAbstractSocket::SocketError error); - void parseMessage(const QString &message); -Q_SIGNALS: - void messageParsed(AbstractClientMessage *clientMessage); - void connected(); - void disconnected(); - void dataReceived(); - void error(QAbstractSocket::SocketError); -private: - AbstractClientMessageFactory *m_messageFactory; - LtsWebSocketClient *m_webSocketClient; - QWebSocket *m_socket; - NGL::INGLLogger &m_logger; -}; - -WebSocketHelper::WebSocketHelper(AbstractClientMessageFactory *messageFactory, - LtsWebSocketClient *webSocketClient, - NGL::INGLLogger &logger) - : QObject() - , m_messageFactory(messageFactory) - , m_webSocketClient(webSocketClient) - , m_socket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this)) - , m_logger(logger) -{ - connect(m_socket, &QWebSocket::textMessageReceived, this, &WebSocketHelper::parseMessage); - connect(m_socket, &QWebSocket::textFrameReceived, this, &WebSocketHelper::dataReceived); - connect(m_socket, &QWebSocket::connected, this, &WebSocketHelper::onConnected); - connect(m_socket, &QWebSocket::disconnected, this, &WebSocketHelper::onDisconnected); - connect(m_socket, QOverload::of(&QWebSocket::error), this, &WebSocketHelper::error); -} - -WebSocketHelper::~WebSocketHelper() -{ - m_socket->close(); - delete m_socket; -} - -void WebSocketHelper::doOpen(const QString &hostname, const unsigned short port) -{ - const QUrl url(QStringLiteral("ws://%1:%2/").arg(hostname).arg(port)); - - if (m_socket->state() == QAbstractSocket::SocketState::ConnectedState) - { - if (m_socket->requestUrl() != url) - doClose(); - else - { - INFO_LOG(m_logger) << "Web socket to url: " << url.toDisplayString() << " is already open."; - return; - } - } - - INFO_LOG(m_logger) << "Opening web socket to url: " << url.toDisplayString(); - m_socket->open(url); -} - -void WebSocketHelper::doClose() -{ - INFO_LOG(m_logger) << "Closing web socket to url:" << m_socket->requestUrl().toDisplayString(); - m_socket->close(); -} - -void WebSocketHelper::doSendTextMessage(const QString &msg) -{ - if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState) - { - ERROR_LOG(m_logger) << "Message could not be send, socket is not connected."; - return; - } - DEBUG_LOG(m_logger) << "Sending message: " << msg; - m_socket->sendTextMessage(msg); -} - -void WebSocketHelper::onConnected() -{ - INFO_LOG(m_logger) << "Connected to server url: " << m_socket->requestUrl().toDisplayString() - << " using local port: " << m_socket->localPort(); - emit connected(); -} - -void WebSocketHelper::onDisconnected() -{ - INFO_LOG(m_logger) << "Disconnected from server url: " << m_socket->requestUrl().toDisplayString(); - emit disconnected(); -} - -void WebSocketHelper::onError(QAbstractSocket::SocketError error) -{ - ERROR_LOG(m_logger) << "Error occured: " << error << " closing socket."; - m_socket->close(); -} - -void WebSocketHelper::parseMessage(const QString &message) -{ - const QString pingMsg(QStringLiteral("ping")); - if (message.startsWith(pingMsg)) - { - doSendTextMessage(PongMessage(message.mid(pingMsg.size())).toJsonString()); - return; - } - - AbstractClientMessage *clientMessage = nullptr; - auto result = m_messageFactory->parseMessage(message); - if (QJsonParseError::NoError == result.error) - clientMessage = m_messageFactory->createMessage(m_webSocketClient, message); - else - { - WARNING_LOG(m_logger) << "Could not parse incoming message: '" << message - << "', error: " << result.errorString(); - clientMessage = new InvalidMessage(m_webSocketClient, QString(), - QStringLiteral("Got message which is not JSON conform (%1)").arg(result.errorString())); - } - if (clientMessage) - emit messageParsed(clientMessage); -} - - - - -LtsWebSocketClient::LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent) - : QObject(parent) - , m_messageFactory(messageFactory) - , m_webSocketHelperThread(new QThread(this)) - , m_logger(getLogger("LtsWebSocketClient")) -{ - m_webSocketHelper = new WebSocketHelper(m_messageFactory, this, m_logger); - m_webSocketHelper->moveToThread(m_webSocketHelperThread); - m_webSocketHelperThread->start(); - - connect(m_webSocketHelper, &WebSocketHelper::connected, this, &LtsWebSocketClient::connected); - connect(m_webSocketHelper, &WebSocketHelper::disconnected, this, &LtsWebSocketClient::disconnected); - connect(m_webSocketHelper, &WebSocketHelper::dataReceived, this, &LtsWebSocketClient::dataReceived); - connect(m_webSocketHelper, &WebSocketHelper::messageParsed, this, &LtsWebSocketClient::onNewClientMessage); - - connect(this, &LtsWebSocketClient::onOpen, m_webSocketHelper, &WebSocketHelper::doOpen); - connect(this, &LtsWebSocketClient::onClose, m_webSocketHelper, &WebSocketHelper::doClose); - connect(this, &LtsWebSocketClient::onSendTextMessage, m_webSocketHelper, &WebSocketHelper::doSendTextMessage); -} - -LtsWebSocketClient::~LtsWebSocketClient() -{ - m_webSocketHelper->disconnect(this); - m_webSocketHelperThread->quit(); - m_webSocketHelperThread->wait(); - delete m_webSocketHelper; -} - -void LtsWebSocketClient::open(const QString &hostname, const unsigned short port) -{ - emit onOpen(hostname, port); -} - -void LtsWebSocketClient::close() -{ - emit onClose(); -} - -void LtsWebSocketClient::addReply(ReplyMessage *message) -{ - const QString msg(message->toJsonString()); - emit onSendTextMessage(msg); - delete message; -} - -void LtsWebSocketClient::onNewClientMessage(AbstractClientMessage *clientMessage) -{ - if (!clientMessage) - return; - clientMessage->process(); - delete clientMessage; -} - -} - -#include "lts_web_socket_client.moc" diff --git a/Lts/Common/LtsWebSocket/lts_web_socket_client.h b/Lts/Common/LtsWebSocket/lts_web_socket_client.h deleted file mode 100755 index e61fa3269..000000000 --- a/Lts/Common/LtsWebSocket/lts_web_socket_client.h +++ /dev/null @@ -1,67 +0,0 @@ - - -#pragma once - -#include -#include - -#include -#include "client_message_factory.h" -#include "logger.h" - - -class QThread; -namespace LtsWebSocket -{ -class WebSocketHelper; - - - -class LTSWEBSOCKETCLIENT_EXPORT LtsWebSocketClient - : public QObject - , public ReplyHandler -{ - Q_OBJECT -public: - - - explicit LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr); - ~LtsWebSocketClient(); - - - - void open(const QString &hostname, const unsigned short port); - - - - void close(); - - - - void addReply(ReplyMessage *message) override; - -signals: - void connected(); - void disconnected(); - void dataReceived(); - -private slots: - - - void onNewClientMessage(AbstractClientMessage *clientMessage); - -signals: - void onOpen(const QString &hostname, const unsigned short port); - void onClose(); - void onSendTextMessage(const QString &msg); - -private: - AbstractClientMessageFactory *m_messageFactory; - - QThread *m_webSocketHelperThread; - WebSocketHelper *m_webSocketHelper; - - - LOGGER_T m_logger; -}; -} diff --git a/Lts/Common/LtsWebSocket/lts_web_socket_server.cpp b/Lts/Common/LtsWebSocket/lts_web_socket_server.cpp deleted file mode 100755 index ff38eec0a..000000000 --- a/Lts/Common/LtsWebSocket/lts_web_socket_server.cpp +++ /dev/null @@ -1,119 +0,0 @@ - - -#include "lts_web_socket_server.h" - -#include "connection_handler.h" -#include "client_connection.h" -#include - -namespace LtsWebSocket -{ - -LtsWebSocketServer::LtsWebSocketServer(AbstractClientMessageFactory *messageFactory, QObject *parent) - : QObject(parent) - , m_queueTimer(new QTimer(this)) - , m_messageFactory(messageFactory) - , m_connectionHandler(nullptr) - , m_logger(getLogger("LtsWebSocketServer")) -{ - connect(m_queueTimer, &QTimer::timeout, - this, &LtsWebSocketServer::onQueueTimerTimeout); - - m_queueTimer->setSingleShot(true); -} - -LtsWebSocketServer::~LtsWebSocketServer() = default; - -bool LtsWebSocketServer::init(const QString &serviceName, const unsigned short port) -{ - m_connectionHandler.reset(new ConnectionHandler(this)); - - if (!m_connectionHandler->init(serviceName, port)) - { - ERROR_LOG(m_logger) << "Could not init m_connectionHandler with servicename: " << serviceName << ((port == 0) ? QStringLiteral(" random port") : QStringLiteral(" port: ") + QString::number(port)); - m_connectionHandler.reset(); - return false; - } - - connect(m_connectionHandler.get(), &ConnectionHandler::newTextMessageFromClient, this, &LtsWebSocketServer::onNewTextMessage); - connect(m_connectionHandler.get(), &ConnectionHandler::clientDisconnected, this, &LtsWebSocketServer::clientDisconnected); - connect(m_connectionHandler.get(), &ConnectionHandler::clientConnected, this, &LtsWebSocketServer::clientConnected); - - m_queueTimer->start(m_messageDelay); - - return true; -} - -bool LtsWebSocketServer::init(const QString &serviceName, const QPair &portRange) -{ - if (portRange.first < 1025 || portRange.first > portRange.second) - return init(serviceName); - - for (quint16 currentPort = portRange.first; currentPort <= portRange.second; ++ currentPort) - { - if (init(serviceName, currentPort)) - return true; - } - - return init(serviceName); -} - -void LtsWebSocketServer::sendToAllClients(ReplyMessage *message) -{ - DEBUG_LOG(m_logger) << "Sending message to all clients: " << message->toJsonString(); - m_connectionHandler->sendToAllClients(message); -} - -unsigned short LtsWebSocketServer::webSocketPort() const -{ - return m_connectionHandler->webSocketPort(); -} - -void LtsWebSocketServer::setMessageDelay(qint32 delay) -{ - m_messageDelay = delay; -} - -void LtsWebSocketServer::onNewTextMessage(ReplyHandler *client, const QString &message) -{ - const QString pingMsg(QStringLiteral("ping")); - if (message.startsWith(pingMsg)) - { - client->addReply(new PongMessage(message.mid(pingMsg.size()))); - return; - } - DEBUG_LOG(m_logger) << "Got new message: " << message; - - m_messageQueue.append({client, message}); - - if (!m_queueTimer->isActive()) - m_queueTimer->start(m_messageDelay); -} - -void LtsWebSocketServer::onQueueTimerTimeout() -{ - if (m_messageQueue.isEmpty()) - return; - - MessageCredentials credentials = m_messageQueue.takeFirst(); - AbstractClientMessage *clientMessage = nullptr; - auto result = m_messageFactory->parseMessage(credentials.message); - - if (QJsonParseError::NoError == result.error) - clientMessage = m_messageFactory->createMessage(credentials.client, credentials.message); - else - { - WARNING_LOG(m_logger) << "Could not parse incoming message: '" << credentials.message << "', error: " << result.errorString(); - - clientMessage = new InvalidMessage(credentials.client, QString(), QStringLiteral("Got message which is not JSON conform")); - } - - if (clientMessage) - clientMessage->process(); - - delete clientMessage; - - m_queueTimer->start(m_messageDelay); -} - -} diff --git a/Lts/Common/LtsWebSocket/lts_web_socket_server.h b/Lts/Common/LtsWebSocket/lts_web_socket_server.h deleted file mode 100755 index 1534e5143..000000000 --- a/Lts/Common/LtsWebSocket/lts_web_socket_server.h +++ /dev/null @@ -1,88 +0,0 @@ - - -#pragma once - -#include - -#include -#include "client_message_factory.h" -#include "logger.h" -#include -#include - -class QTimer; - -namespace LtsWebSocket -{ -class ConnectionHandler; -class ClientConnection; - - - -class LTSWEBSOCKETSERVER_EXPORT LtsWebSocketServer - : public QObject -{ - Q_OBJECT -public: - - - explicit LtsWebSocketServer(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr); - virtual ~LtsWebSocketServer(); - - - - bool init(const QString &serviceName, const unsigned short port = 0); - - - - bool init(const QString &serviceName, const QPair &portRange); - - - - void sendToAllClients(ReplyMessage *message); - - - - unsigned short webSocketPort() const; - - - - void setMessageDelay(qint32 delay); -signals: - - - void clientDisconnected(ClientConnection *client); - - - - void clientConnected(ClientConnection *client); - -private slots: - - - void onNewTextMessage(ReplyHandler *client, const QString &message); - - - - void onQueueTimerTimeout(); - -private: - struct MessageCredentials - { - ReplyHandler *client {nullptr}; - QString message; - - MessageCredentials(ReplyHandler *client, const QString &message) - : client(client) - , message(message) - {} - }; - - QQueue m_messageQueue; - QTimer *m_queueTimer { nullptr }; - AbstractClientMessageFactory *m_messageFactory { nullptr }; - std::unique_ptr m_connectionHandler; - qint32 m_messageDelay { 0 }; - LOGGER_T m_logger; -}; -} diff --git a/Lts/Common/LtsWebSocket/message_transmitter.h b/Lts/Common/LtsWebSocket/message_transmitter.h deleted file mode 100755 index 5c0931cae..000000000 --- a/Lts/Common/LtsWebSocket/message_transmitter.h +++ /dev/null @@ -1,17 +0,0 @@ - - - -namespace LtsWebSocketServer -{ -class ReplyMessage; - - - -class MessageHandler -{ -public: - - - virtual void addMessage(ReplyMessage *message) = 0; -}; -} diff --git a/Lts/Common/LtsWebSocket/reply_message.cpp b/Lts/Common/LtsWebSocket/reply_message.cpp deleted file mode 100755 index 842e9134c..000000000 --- a/Lts/Common/LtsWebSocket/reply_message.cpp +++ /dev/null @@ -1,41 +0,0 @@ - - - -#include "reply_message.h" - -#include "json_keys.h" - -#include - -namespace LtsWebSocket -{ - - -QString ReplyMessage::toJsonString() const -{ - return QString::fromUtf8(QJsonDocument(m_jsonRepresentation).toJson(QJsonDocument::Compact)); -} - -ReplyMessage::ReplyMessage(const QString &type, const int typeId, const QString &uuid) -{ - m_jsonRepresentation.insert(JsonKeys::TYPE(), type); - m_jsonRepresentation.insert(JsonKeys::TYPEID(), typeId); - - if (!uuid.isEmpty()) - m_jsonRepresentation.insert(JsonKeys::UUID_(), uuid); -} - - - -ErrorMessage::ErrorMessage(const ErrorMessage::ErrorData &data) - : ReplyMessage(JsonKeys::ERROR_(), -1, QUuid::createUuid().toString()) -{ - m_jsonRepresentation.insert(JsonKeys::DATA(), - QJsonObject({{JsonKeys::UUID_(), data.uuid}, - {JsonKeys::ERROR_CLASS(), data.errorClass}, - {JsonKeys::ERROR_MESSAGE(), data.errorMessage} - })); -} - - -} diff --git a/Lts/Common/LtsWebSocket/reply_message.h b/Lts/Common/LtsWebSocket/reply_message.h deleted file mode 100755 index 197b993c1..000000000 --- a/Lts/Common/LtsWebSocket/reply_message.h +++ /dev/null @@ -1,70 +0,0 @@ - - -#pragma once - -#include - -#include -#include -#include - -namespace LtsWebSocket -{ - - - -class LTSWEBSOCKETCOMMON_EXPORT ReplyMessage -{ -public: - - - virtual ~ReplyMessage() = default; - - - - virtual QString toJsonString() const; - - QJsonObject json() const { return m_jsonRepresentation; } - -protected: - ReplyMessage() = default; - ReplyMessage(const QString &type, const int typeId, const QString &uuid); - -protected: - QJsonObject m_jsonRepresentation; -}; - - - -class LTSWEBSOCKETCOMMON_EXPORT ReplyHandler -{ -public: - virtual ~ReplyHandler() = default; - - - virtual void addReply(ReplyMessage *message) = 0; -}; - - - - - -class LTSWEBSOCKETCOMMON_EXPORT ErrorMessage final - : public ReplyMessage -{ -public: - - - struct ErrorData - { - int errorClass = 0; - QString uuid; - QString errorMessage; - }; - - - - explicit ErrorMessage(const ErrorData &data); -}; - -} diff --git a/Lts/Common/LtsWidgets/CMakeLists.txt b/Lts/Common/LtsWidgets/CMakeLists.txt deleted file mode 100755 index 47b50f47e..000000000 --- a/Lts/Common/LtsWidgets/CMakeLists.txt +++ /dev/null @@ -1,210 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# -# Lts Widgets library -# - -SET(ltswidgets_SRCS - #lts_abstract_table_model.h - #lts_checkable_table_model.h - lts_abstract_sequence_widget.cpp - lts_abstract_sequence_widget.h - lts_background_widget.cpp - lts_background_widget.h - lts_base.h - lts_base_layout_widget.cpp - lts_base_layout_widget.h - lts_column_filter_proxy_model.cpp - lts_column_filter_proxy_model.h - lts_combo_box.cpp - lts_combo_box.h - lts_selection_popup.cpp - lts_selection_popup.h - lts_drag_icon.cpp - lts_drag_icon.h - #lts_drop_handler.h - lts_elided_label.cpp - lts_elided_label.h - lts_events.cpp - lts_events.h - lts_frameless_dialog.cpp - lts_frameless_dialog.h - lts_grid_layout.cpp - lts_grid_layout.h - lts_height_plot_widget.cpp - lts_height_plot_widget.h - lts_message_dialog.cpp - lts_message_dialog.h - lts_message_dialog_helper.cpp - lts_message_dialog_helper.h - lts_profile_button.cpp - lts_profile_button.h - lts_progress_dialog_helper.cpp - lts_progress_dialog_helper.h - lts_splitter.cpp - lts_splitter.h - lts_splitter_handle.cpp - lts_splitter_handle.h - lts_tab_bar.cpp - lts_tab_bar.h - lts_tab_widget.cpp - lts_tab_widget.h - lts_tab_sequence_widget.cpp - lts_tab_sequence_widget.h - lts_tool_bar.cpp - lts_tool_bar.h - lts_tool_box.cpp - lts_tool_box.h - lts_tool_bar_area.cpp - lts_tool_bar_area.h - lts_dialog.cpp - lts_dialog.h - lts_dialog_helper.cpp - lts_dialog_helper.h - lts_dialog_button_box.cpp - lts_dialog_button_box.h - lts_operator_menu.cpp - lts_operator_menu.h - lts_image_editor.cpp - lts_image_editor.h - lts_led_widget.cpp - lts_led_widget.h - ui_requirement.cpp - ui_requirement.h - #lts_widgets_export.h - lts_picture_view.cpp - lts_picture_view.h - lts_global_wait_dialog.cpp - lts_global_wait_dialog.h - lts_wait_dialog.cpp - lts_wait_dialog.h - lts_splash.cpp - lts_splash.h - lts_info_dialog.cpp - lts_info_dialog.h - lts_spinbox.cpp - lts_spinbox.h - lts_multi_range_spinbox.cpp - lts_multi_range_spinbox.h - lts_text_editor.cpp - lts_text_editor.h - lts_text_edit_dialog.h - lts_text_edit_dialog.cpp - lts_range_slider.cpp - lts_range_slider.h - lts_notification_box.cpp - lts_notification_box.h - lts_msec_date_time_edit.cpp - lts_msec_date_time_edit.h - lts_bitfield_widget.cpp - lts_bitfield_widget.h - lts_overlay_loading.cpp - lts_angle_edit.cpp - lts_angle_edit.h - lts_text_editor2.h - lts_text_editor2.cpp - - LtsImageCropper/lts_image_cropper.cpp -) - - -#if (TABLET_MODE) -# LIST(APPEND ltswidgets_SRCS -# lts_date_time_edit_touch.h -# lts_date_time_edit_touch.cpp -# ) -# NGL_WRAP_UI(ltswidgets_SRCS -# lts_date_time_edit_touch.ui -# ) -#else() - LIST(APPEND ltswidgets_SRCS - lts_date_time_edit.h - lts_date_time_edit.cpp - lts_date_time_edit_p.h - lts_date_time_edit_p.cpp - ) -#endif() - - -NGL_WRAP_UI(ltswidgets_SRCS - lts_selection_popup.ui - lts_base_layout.ui - lts_tool_bar_area.ui - lts_overlay_loading.ui - lts_dialog_button_box.ui - lts_operator_menu.ui - lts_dialog_helper.ui - lts_info_dialog.ui - lts_text_editor.ui - lts_notification_box.ui - lts_angle_edit.ui - lts_date_time_edit_touch.ui -) - -NGL_ADD_BIG_RESOURCES(ltswidgets_SRCS resources/lts_resources.qrc) -NGL_ADD_LIBRARY(WS TOOLSWS TOOLSSV LtsWidgets SHARED ${ltswidgets_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWidgets - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWidgets - PRIVATE - Qt5::Network # for QHostInfo - for portability - Qt5::Svg - ngltools - ngltranslator - TabBarTabStyle - LtsSessionData - LtsUtilsSettings - PUBLIC - LtsUtilsCore - Qt5::Core - Qt5::Widgets - Qt5::Gui -) - -NGL_INSTALL_TARGET(LtsWidgets) - -# -# Designer-Plugin -# -SET(ltswidgetsplugin_SRCS ltswidgetsplugin.cpp ltswidgetsplugin.json) -NGL_ADD_LIBRARY(WS ltswidgetsplugin MODULE ${ltswidgetsplugin_SRCS}) -NGL_TARGET_LINK_LIBRARIES(ltswidgetsplugin LtsWidgets ltsCoordWidget LtsDataView LtsFileDialog LtsReportDisplay svcFlmCommon) -NGL_SET_TARGET_PROPERTIES(ltswidgetsplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/designer) -NGL_INSTALL_TARGET(ltswidgetsplugin DESTINATION lib/designer COMPONENT Devel) - -IF(BUILD_TESTING) - # picture_editing_test - SET(pet_SRCS - tests/lts_picture_editing.cpp - tests/lts_picture_editing.h - tests/lts_picture_editing_main.cpp - ) - NGL_WRAP_UI(pet_SRCS - tests/lts_picture_editing.ui - ) - NGL_ADD_EXECUTABLE(WS NO_TRANSLATION picture_editing_test ${pet_SRCS}) - NGL_TARGET_LINK_LIBRARIES(picture_editing_test - LtsWidgets - ) - - -# # LtsWidgetsTest -# SET(src_test -# tests/main.cpp -# tests/window.h -# tests/window.cpp -# ) - -# NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsWidgetsTest ${src_test}) -# NGL_TARGET_LINK_LIBRARIES(LtsWidgetsTest -# LtsWidgets -# ) -ENDIF() diff --git a/Lts/Common/LtsWidgets/CMakeLists_rc.txt b/Lts/Common/LtsWidgets/CMakeLists_rc.txt deleted file mode 100755 index aa27b36e6..000000000 --- a/Lts/Common/LtsWidgets/CMakeLists_rc.txt +++ /dev/null @@ -1,208 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# -# Lts Widgets library -# - -SET(ltswidgets_SRCS - #lts_abstract_table_model.h - #lts_checkable_table_model.h - lts_abstract_sequence_widget.cpp - lts_abstract_sequence_widget.h - lts_background_widget.cpp - lts_background_widget.h - lts_base.h - lts_base_layout_widget.cpp - lts_base_layout_widget.h - lts_column_filter_proxy_model.cpp - lts_column_filter_proxy_model.h - lts_combo_box.cpp - lts_combo_box.h - lts_selection_popup.cpp - lts_selection_popup.h - lts_drag_icon.cpp - lts_drag_icon.h - #lts_drop_handler.h - lts_elided_label.cpp - lts_elided_label.h - lts_events.cpp - lts_events.h - lts_frameless_dialog.cpp - lts_frameless_dialog.h - lts_grid_layout.cpp - lts_grid_layout.h - lts_height_plot_widget.cpp - lts_height_plot_widget.h - lts_message_dialog.cpp - lts_message_dialog.h - lts_message_dialog_helper.cpp - lts_message_dialog_helper.h - lts_profile_button.cpp - lts_profile_button.h - lts_progress_dialog_helper.cpp - lts_progress_dialog_helper.h - lts_splitter.cpp - lts_splitter.h - lts_splitter_handle.cpp - lts_splitter_handle.h - lts_tab_bar.cpp - lts_tab_bar.h - lts_tab_widget.cpp - lts_tab_widget.h - lts_tab_sequence_widget.cpp - lts_tab_sequence_widget.h - lts_tool_bar.cpp - lts_tool_bar.h - lts_tool_box.cpp - lts_tool_box.h - lts_tool_bar_area.cpp - lts_tool_bar_area.h - lts_dialog.cpp - lts_dialog.h - lts_dialog_helper.cpp - lts_dialog_helper.h - lts_dialog_button_box.cpp - lts_dialog_button_box.h - lts_operator_menu.cpp - lts_operator_menu.h - lts_image_editor.cpp - lts_image_editor.h - lts_led_widget.cpp - lts_led_widget.h - ui_requirement.cpp - ui_requirement.h - #lts_widgets_export.h - lts_picture_view.cpp - lts_picture_view.h - lts_global_wait_dialog.cpp - lts_global_wait_dialog.h - lts_wait_dialog.cpp - lts_wait_dialog.h - lts_splash.cpp - lts_splash.h - lts_info_dialog.cpp - lts_info_dialog.h - lts_spinbox.cpp - lts_spinbox.h - lts_multi_range_spinbox.cpp - lts_multi_range_spinbox.h - lts_text_editor.cpp - lts_text_editor.h - lts_text_edit_dialog.h - lts_text_edit_dialog.cpp - lts_range_slider.cpp - lts_range_slider.h - lts_notification_box.cpp - lts_notification_box.h - lts_msec_date_time_edit.cpp - lts_msec_date_time_edit.h - lts_bitfield_widget.cpp - lts_bitfield_widget.h - lts_overlay_loading.cpp - lts_angle_edit.cpp - lts_angle_edit.h - - LtsImageCropper/lts_image_cropper.cpp -) - - -#if (TABLET_MODE) -# LIST(APPEND ltswidgets_SRCS -# lts_date_time_edit_touch.h -# lts_date_time_edit_touch.cpp -# ) -# NGL_WRAP_UI(ltswidgets_SRCS -# lts_date_time_edit_touch.ui -# ) -#else() - LIST(APPEND ltswidgets_SRCS - lts_date_time_edit.h - lts_date_time_edit.cpp - lts_date_time_edit_p.h - lts_date_time_edit_p.cpp - ) -#endif() - - -NGL_WRAP_UI(ltswidgets_SRCS - lts_selection_popup.ui - lts_base_layout.ui - lts_tool_bar_area.ui - lts_overlay_loading.ui - lts_dialog_button_box.ui - lts_operator_menu.ui - lts_dialog_helper.ui - lts_info_dialog.ui - lts_text_editor.ui - lts_notification_box.ui - lts_angle_edit.ui - lts_date_time_edit_touch.ui -) - -NGL_ADD_BIG_RESOURCES(ltswidgets_SRCS resources/lts_resources.qrc) -NGL_ADD_LIBRARY(WS TOOLSWS TOOLSSV LtsWidgets SHARED ${ltswidgets_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWidgets - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWidgets - PRIVATE - Qt5::Network # for QHostInfo - for portability - Qt5::Svg - ngltools - ngltranslator - TabBarTabStyle - LtsSessionData - LtsUtilsSettings - PUBLIC - LtsUtilsCore - Qt5::Core - Qt5::Widgets - Qt5::Gui -) - -NGL_INSTALL_TARGET(LtsWidgets) - -# -# Designer-Plugin -# -SET(ltswidgetsplugin_SRCS ltswidgetsplugin.cpp ltswidgetsplugin.json) -NGL_ADD_LIBRARY(WS ltswidgetsplugin MODULE ${ltswidgetsplugin_SRCS}) -NGL_TARGET_LINK_LIBRARIES(ltswidgetsplugin LtsWidgets ltsCoordWidget LtsDataView LtsFileDialog LtsReportDisplay svcFlmCommon) -NGL_SET_TARGET_PROPERTIES(ltswidgetsplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/designer) -NGL_INSTALL_TARGET(ltswidgetsplugin DESTINATION lib/designer COMPONENT Devel) - -IF(BUILD_TESTING) - # picture_editing_test - SET(pet_SRCS - tests/lts_picture_editing.cpp - tests/lts_picture_editing.h - tests/lts_picture_editing_main.cpp - ) - NGL_WRAP_UI(pet_SRCS - tests/lts_picture_editing.ui - ) - NGL_ADD_EXECUTABLE(WS NO_TRANSLATION picture_editing_test ${pet_SRCS}) - NGL_TARGET_LINK_LIBRARIES(picture_editing_test - LtsWidgets - ) - - -# # LtsWidgetsTest -# SET(src_test -# tests/main.cpp -# tests/window.h -# tests/window.cpp -# ) - -# NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsWidgetsTest ${src_test}) -# NGL_TARGET_LINK_LIBRARIES(LtsWidgetsTest -# LtsWidgets -# ) -ENDIF() diff --git a/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp b/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp deleted file mode 100755 index 5773b96ff..000000000 --- a/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp +++ /dev/null @@ -1,131 +0,0 @@ - - - -#include "lts_image_cropper.h" -#include -#include -#include - -namespace LtsWidgets -{ - -LtsImageCropper::LtsImageCropper(QWidget *parent, const QSize &size, const QPixmap &pixmap) - : QGraphicsView(parent) - , m_scene(new QGraphicsScene(this)) - , m_maxScale(2.0f) - , m_multiplier(1.0f) -{ - if(!size.isNull()) - setFixedSize(size); - - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setFrameStyle(QFrame::NoFrame); - setDragMode(QGraphicsView::ScrollHandDrag); - setTransformationAnchor(QGraphicsView::AnchorViewCenter); - - m_scene->setBackgroundBrush(Qt::Dense6Pattern); - setScene(m_scene); - - setPixmap(pixmap); -} - -LtsImageCropper::~LtsImageCropper() -{ - delete m_pixmapItem; -} - -void LtsImageCropper::setPixmap(const QPixmap &pixmap) -{ - if(pixmap.isNull()) - return; - - delete m_pixmapItem; - - m_pixmapItem = new QGraphicsPixmapItem(pixmap); - m_scene->addItem(m_pixmapItem); - - setTransform(QTransform()); - m_currentZoom = 1.0; - - - double sx = static_cast(size().height()) / pixmap.height(); - double sy = static_cast(size().width()) / pixmap.width(); - if(sx > 1 ) - scale(sx,sx); - if(sy > 1) - scale(sy,sy); - - - double ratioWidth = 1.0 * size().width() / pixmap.width(); - double ratioHeight = 1.0 * size().height() / pixmap.height(); - m_minScale = qMax(ratioHeight,ratioWidth); - - calculateScaleStep(); - - - m_scene->setSceneRect(pixmap.rect()); - centerOn(m_scene->sceneRect().center()); -} - -void LtsImageCropper::setImage(const QImage &image) -{ - setPixmap(QPixmap::fromImage(image)); -} - -void LtsImageCropper::setMultiplier(double multiplier) -{ - m_multiplier = multiplier; - calculateScaleStep(); -} - -void LtsImageCropper::calculateScaleStep() -{ - m_stepScale = (m_maxScale - m_minScale) / 100.0; -} - -QImage LtsImageCropper::image() -{ - return grab().toImage(); -} - -void LtsImageCropper::wheelEvent(QWheelEvent *event) -{ - if(!m_pixmapItem) - return; - - auto value = event->pixelDelta(); - if(value.isNull()) - value = event->angleDelta() / 15.0; - - - double scrollZoom = m_stepScale * value.y() * m_multiplier / 8.0; - - m_currentZoom += scrollZoom; - - if(m_currentZoom > m_maxScale) - m_currentZoom = m_maxScale; - else if (m_currentZoom < m_minScale) - m_currentZoom = m_minScale; - - - setTransform(QTransform(m_currentZoom, transform().m12(), transform().m13(), - transform().m21(), m_currentZoom, transform().m23(), - transform().m31(), transform().m32(), 1)); -} - -void LtsImageCropper::mousePressEvent(QMouseEvent *event) -{ - QApplication::setOverrideCursor(Qt::DragMoveCursor); - update(); - QGraphicsView::mousePressEvent(event); -} - -void LtsImageCropper::mouseReleaseEvent(QMouseEvent *event) -{ - QApplication::restoreOverrideCursor(); - update(); - QGraphicsView::mouseReleaseEvent(event); -} - -} diff --git a/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h b/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h deleted file mode 100755 index a67930d77..000000000 --- a/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h +++ /dev/null @@ -1,80 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" -#include - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsImageCropper : public QGraphicsView -{ - - Q_OBJECT - - Q_PROPERTY(QPixmap pixMap READ pixmap WRITE setPixmap) - Q_PROPERTY(double multiplier READ multiplier WRITE setMultiplier) - Q_PROPERTY(double maxZoom READ maxZoom WRITE setMaxZoom) - -public: - - - LtsImageCropper(QWidget *parent = nullptr, const QSize &size = {} , const QPixmap &pixmap = {}); - ~LtsImageCropper(); - - - - void setPixmap(const QPixmap &pixmap); - - - - void setImage(const QImage &image); - - - - void setMultiplier(double multiplier); - - - - void setMaxZoom(double maxZoom) { m_maxScale = maxZoom; } - - - - QPixmap pixmap() { return grab(); } - - - - QImage image(); - - - - double multiplier() const { return m_multiplier; } - - - - double maxZoom() const { return m_maxScale; } - -private: - QGraphicsScene *m_scene = nullptr; - QGraphicsPixmapItem *m_pixmapItem = nullptr; - - double m_currentZoom; - double m_minScale; - double m_maxScale; - double m_stepScale; - double m_multiplier; - - void calculateScaleStep(); - -protected: - void wheelEvent(QWheelEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp b/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp deleted file mode 100755 index ad50ce074..000000000 --- a/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp +++ /dev/null @@ -1,120 +0,0 @@ - - -#include "lts_abstract_sequence_widget.h" - -#include - -namespace LtsWidgets -{ - -LtsAbstractSequenceWidget::LtsAbstractSequenceWidget(QWidget *parent, const bool &readOnly) - : QGroupBox(parent) - , m_readOnly(readOnly) - , m_pbAddMember(new QToolButton) -{ - m_pbAddMember->setIcon(QIcon(QLatin1String(":/icons/png/add.png"))); - - connect(m_pbAddMember, &QToolButton::clicked, this, &LtsAbstractSequenceWidget::addMember); -} - -LtsAbstractSequenceWidget::~LtsAbstractSequenceWidget() -{ - -} - -void LtsAbstractSequenceWidget::addWidget(QWidget* widget, bool isReadOnly, bool isButtonVisible) -{ - addMemberWidget(widget, isReadOnly, isButtonVisible); - m_pbAddMember->setDisabled(isReadOnly); - m_pbAddMember->setVisible(isButtonVisible); -} - -int LtsAbstractSequenceWidget::getMemberlistSize(){ - return m_memberList.size(); -} - -void LtsAbstractSequenceWidget::getValue(QWidget *data, - const QString &findObjectName) const -{ - - if(!findChild(findObjectName)) - return; - - data = findChild(findObjectName); -} - -void LtsAbstractSequenceWidget::setMemberCount(int count) -{ - QWidget *child = nullptr; - do - { - child = findChild(getObjectName(++count)); - delete child; - } - while (child); - m_memberList.removeAll(nullptr); -} - -uint32_t LtsAbstractSequenceWidget::getMemberCount() const -{ - int count = 0; - QWidget *child = findChild(getObjectName(count+1)); - while(child) - { - count++; - child = findChild(getObjectName(count+1)); - } - return count; -} - -QToolButton *LtsAbstractSequenceWidget::pbAddMember() const -{ - return m_pbAddMember; -} - -QString LtsAbstractSequenceWidget::getObjectName(int index) const -{ - return QString::number(index); -} - -void LtsAbstractSequenceWidget::addMemberWidget(QWidget *member, bool isReadOnly, bool isButtonVisible) -{ - QToolButton *button = new QToolButton(); - button->setIcon(QIcon(QLatin1String(":/icons/png/remove.png"))); - button->setDisabled(isReadOnly); - - connect(button, &QToolButton::clicked, member, &QWidget::deleteLater); - connect(member, &QWidget::destroyed, this, &LtsAbstractSequenceWidget::deleteMemberWidget); - connect(member, &QWidget::destroyed, button, &QToolButton::deleteLater); - - m_memberList.push_back(member); - setupMemberWidget(member, button, member->objectName(), isButtonVisible); -} - -void LtsAbstractSequenceWidget::deleteMemberWidget(QObject *member) -{ - QWidget *w = qobject_cast(member); - - if(!w) - return; - - for(auto child : w->children()) - child->deleteLater(); - - m_memberList.removeAll(w); - for(int i=0; isetObjectName(getObjectName(i + 1)); - - for(auto child : m_memberList[i]->children()) - { - if(child->objectName().isEmpty()) - continue; - - QString childName = child->objectName(); - QString newId = QString::number(i + 1); - child->setObjectName(childName.replace(QRegExp(QLatin1String(".([0-9]*)")), newId)); - } - } -} -} diff --git a/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h b/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h deleted file mode 100755 index c467064c1..000000000 --- a/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef LTS_ABSTRACT_SEQUENCE_WIDGET_H -#define LTS_ABSTRACT_SEQUENCE_WIDGET_H - - - - -#pragma once - - -#include -#include -#include "lts_widgets_export.h" - -class QToolButton; -namespace LtsWidgets -{ - - -class LTSWIDGETS_EXPORT LtsAbstractSequenceWidget : public QGroupBox -{ - Q_OBJECT -public: - - - - explicit LtsAbstractSequenceWidget(QWidget *parent = nullptr, const bool &readOnly = false); - - ~LtsAbstractSequenceWidget(); - - - - virtual void addWidget(QWidget* widget, bool isReadOnly, bool isButtonVisible = true); - - - virtual void getValue(QWidget *contentType, - const QString &findObjectName) const; - - - - virtual void setMemberCount(int count); - - - - virtual uint32_t getMemberCount() const; - - - - virtual int getMemberlistSize(); - - -protected: - - - - virtual QToolButton *pbAddMember() const; - - - - - virtual QString getObjectName(int index) const; - - - - virtual void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName, bool isButtonVisible = true) = 0; - -signals: - - - void addMember(); - -private slots: - - - virtual void addMemberWidget(QWidget *member, bool isReadOnly, bool isButtonVisible = true); - - - - virtual void deleteMemberWidget(QObject *member); - -private: - bool m_readOnly; - QToolButton *m_pbAddMember = nullptr; - QList m_memberList; - - -}; -} - -#endif diff --git a/Lts/Common/LtsWidgets/lts_abstract_table_model.h b/Lts/Common/LtsWidgets/lts_abstract_table_model.h deleted file mode 100755 index dd0366de3..000000000 --- a/Lts/Common/LtsWidgets/lts_abstract_table_model.h +++ /dev/null @@ -1,286 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" - -#include - -#include -#include - -#include -#include - - -namespace LtsWidgets -{ - -template -class LtsAbstractTableModel : public QAbstractTableModel -{ -public: - LtsAbstractTableModel(const QVector &header, QObject *parent) - : QAbstractTableModel(parent) - , m_vecColumns(header) - {} - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override - { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section>= 0 && section < m_vecColumns.size()) - return m_vecColumns[section]; - - return QAbstractTableModel::headerData(section, orientation, role); - } - - bool isEmpty(const QModelIndex &parent = QModelIndex()) - { - return parent.isValid() ? true : m_vecData.isEmpty(); - } - - int rowCount(const QModelIndex &parent = QModelIndex()) const override - { - return parent.isValid() ? 0 : m_vecData.size(); - } - - int columnCount(const QModelIndex &parent = QModelIndex()) const override - { - return parent.isValid() ? 0 : m_vecColumns.size(); - } - - void clear() - { - beginResetModel(); - m_vecData.clear(); - endResetModel(); - } - - bool indexIsValid(const QModelIndex &idx) const - { - if (!idx.isValid()) - return false; - if (idx.row() < 0 || idx.row() >= m_vecData.size()) - return false; - return true; - } - - virtual bool removeRow(int row) - { - return removeRows(row, 1); - } - - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override - { - if (parent.isValid()) - return false; - - if (row < 0 || row >= m_vecData.size()) - return false; - - int range = std::min(row + count, m_vecData.size()); - - beginRemoveRows(QModelIndex(), row, range - 1); - m_vecData.erase(m_vecData.begin() + row, m_vecData.begin() + range); - endRemoveRows(); - - return true; - } - - virtual bool insertRow(const T &item) - { - if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item)) - return false; - beginInsertRows(QModelIndex(), m_vecData.size(), m_vecData.size()); - m_vecData.append(item); - endInsertRows(); - return true; - } - - template - void insertRows(const CONT &items) - { - if (items.isEmpty()) - return; - const int cntBefore = m_vecData.size(); - for (const auto &item : items) - { - if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item)) - continue; - m_vecData += item; - } - const int cntAfter = m_vecData.size(); - if (cntBefore == cntAfter) - return; - beginInsertRows(QModelIndex(), cntBefore, cntAfter - 1); - endInsertRows(); - } - - void setData(const QVector &vecData) - { - clear(); - insertRows(vecData); - } - - - - void update() - { - if (!m_vecData.isEmpty()) - emit dataChanged(index(0, 0), index(m_vecData.size() - 1, columnCount() - 1)); - } - - T getRowData(int row) const - { - if (row < 0 || row >= m_vecData.size()) - return T(); - return m_vecData.at(row); - } - - bool setRowData(int row, const T &data) - { - if (row < 0 || row >= m_vecData.size()) - return false; - - m_vecData[row] = data; - emit dataChanged(index(row, 0), index(row, columnCount() - 1)); - return true; - } - - QModelIndex findIndexForId(int id, int column = 0) const - { - const int row = findRowForId(id); - if (row < 0) - return QModelIndex(); - return index(row, column); - } - - int getId(const QModelIndex &mi) const - { - if (!indexIsValid(mi)) - return -1; - return getIdInternal(m_vecData.at(mi.row())); - } - -protected: - template - class HasId - { - template - static std::true_type testSignature(const int &(CLS::*)() const); - template - static std::true_type testSignature(int (CLS::*)() const); - - template - static decltype(testSignature(&CLS::id)) test(std::nullptr_t); - - template - static std::false_type test(...); - - public: - using type = decltype(test(nullptr)); - static const bool value = type::value; - }; - - - template ::value, int>::type = 0> - int getIdInternal(const F &data) const - { - return data.id(); - } - - - virtual int getId(const T &data) const - { - Q_ASSERT(false); - Q_UNUSED(data); - return -1; - } - - template < typename F = T, typename std::enable_if < !HasId::value, int>::type = 0> - int getIdInternal(const F &data) const - { - return getId(data); - } - - template - class HasIsDeleted - { - template - static std::true_type testSignature(const bool &(CLS::*)() const); - template - static std::true_type testSignature(bool (CLS::*)() const); - - template - static decltype(testSignature(&CLS::isDeleted)) test(std::nullptr_t); - - template - static std::false_type test(...); - - public: - using type = decltype(test(nullptr)); - static const bool value = type::value; - }; - - template ::value, int>::type = 0> - bool isDeletedInternal(const F &data) const - { - return data.isDeleted(); - } - - virtual int isDeleted(const T &data) const - { - Q_UNUSED(data); - return false; - } - template < typename F = T, typename std::enable_if < !HasIsDeleted::value, int>::type = 0> - bool isDeletedInternal(const F &data) const - { - return isDeleted(data); - } - - - virtual bool isFiltered(const T &data, DataModification modType) const - { - Q_UNUSED(data); - Q_UNUSED(modType); - return false; - } - - - virtual bool onDataChanged(const T &data, DataModification modType) - { - if (isFiltered(data, modType)) - return false; - - const int id = getIdInternal(data); - const int row = findRowForId(id); - - - if (modType == DataModification::eRemoved || isDeletedInternal(data)) - { - if (row >= 0) - return removeRow(row); - return false; - } - - if (modType == DataModification::eAdded && row < 0) - return insertRow(data); - - return setRowData(row, data); - } - - int findRowForId(int id) const - { - for (int i = 0; i < m_vecData.size(); ++i) - { - if (getIdInternal(m_vecData.at(i)) == id) - return i; - } - return -1; - } - - QVector m_vecData; - QVector m_vecColumns; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_angle_edit.cpp b/Lts/Common/LtsWidgets/lts_angle_edit.cpp deleted file mode 100755 index 489e569cb..000000000 --- a/Lts/Common/LtsWidgets/lts_angle_edit.cpp +++ /dev/null @@ -1,246 +0,0 @@ - - - -#include "lts_angle_edit.h" -#include "ui_lts_angle_edit.h" - -#include - - -namespace LtsWidgets -{ - -inline constexpr double pi() { return M_PI; } -inline constexpr double deg2rad() { return pi() / 180.; } -inline constexpr double rad2deg() { return 1. / deg2rad(); } - -constexpr int maxAngleDeg = 360; -constexpr int maxAngleMil = 6400; -constexpr std::tuple c_limDeg (0, maxAngleDeg,0, 10); -constexpr std::tuple c_limMil(0, maxAngleMil,0, 50); -constexpr std::tuple c_limRad(-99999, 99999, 3, 1); - -inline constexpr double mil2Deg(const double &mil) -{ - return maxAngleDeg * mil / maxAngleMil; -} -inline constexpr double deg2Mil(const double &angleDeg) -{ - return maxAngleMil * angleDeg / maxAngleDeg; -} -inline constexpr double deg2Rad(const double &angleDeg) -{ - return angleDeg * deg2rad(); -} -inline constexpr double rad2Degree(const double &angleRad) -{ - return angleRad * 180/pi(); -} -inline constexpr double mil2Rad(const double &mil) -{ - return deg2Rad(mil2Deg(mil)); -} -inline constexpr double rad2Mil(const double &angleRad) -{ - return deg2Mil(rad2Degree(angleRad)); -} - - - -LtsAngleEdit::LtsAngleEdit(QWidget *parent) : - QWidget(parent), - m_ui(new Ui_LtsAngleEdit) -{ - m_ui->setupUi(this); - -} - -LtsAngleEdit::~LtsAngleEdit() -{ - delete m_ui; -} - - - -void LtsAngleEdit::initAngleEditWidget(const unsigned int sboxCount) -{ - - m_dboxLimitMap.insert(AngleUnitType::Deg, c_limDeg); - m_dboxLimitMap.insert(AngleUnitType::Mil, c_limMil); - m_dboxLimitMap.insert(AngleUnitType::Rad, c_limRad); - m_hiddenUnits.clear(); - m_hiddenUnits.insert((int)AngleUnitType::Rad); - - - for(unsigned int i=0; i spinbox = QSharedPointer(new QDoubleSpinBox(this) ); - spinbox->setValue(0); - spinbox->setAlignment(Qt::AlignRight); - spinbox->setMinimumWidth(91); - - spinbox.data()->setObjectName(spinBoxName+QString::number(m_sBoxMap.count())); - m_ui->hLayoutSpinbox->addWidget(spinbox.data(), 0); - m_sBoxMap.insert(m_sBoxMap.count(), spinbox); - } - m_ui->hLayoutSpinbox->setContentsMargins(1,1,1,1); - - - - connect(m_ui->cBoxAngleUnit, qOverload(&QComboBox::currentIndexChanged), - this, &LtsAngleEdit::onCBoxAngleUnitChanged); - - setCboxUnit(); - -} - - - -void LtsAngleEdit::setValue(const double value, unsigned int dboxIndex) -{ - if ((int)dboxIndex > m_sBoxMap.count()) - return; - auto spinbox = m_sBoxMap.value(dboxIndex); - spinbox->setValue(value); -} - - - -void LtsAngleEdit::setValue(const double value, AngleUnitType valueUnit, unsigned int dboxIndex) -{ - setValue(getAngleConverted(value, getAngleCurrentUnit(), valueUnit),dboxIndex); -} - -void LtsAngleEdit::hideUnits(const QVector &list) -{ - for(auto u : list) - m_hiddenUnits.insert((int)u); - setCboxUnit(); -} - -void LtsAngleEdit::showUnits(const QVector &list) -{ - for (const auto &u : list) - m_hiddenUnits.remove((int)u); - setCboxUnit(); -} - - - -void LtsAngleEdit::setCboxUnit() -{ - m_ui->cBoxAngleUnit->clear(); - const std::vector allUnits = {AngleUnitType::Deg, AngleUnitType::Mil, AngleUnitType::Rad}; - for(const auto &u : allUnits) - { - if (m_hiddenUnits.contains((int)u)) - continue; - m_ui->cBoxAngleUnit->insertItem(static_cast(u), toString(u),static_cast(u)); - } - - setAngleUnit(AngleUnitType::Deg); -} - - - -void LtsAngleEdit::setValue(const double value, AngleUnitType valueUnit) -{ - - setValue(getAngleConverted(value, getAngleCurrentUnit(), valueUnit),0); -} - - - -void LtsAngleEdit::setAngleUnit(const AngleUnitType &type) -{ - m_ui->cBoxAngleUnit->setCurrentIndex(static_cast(type)); -} - - - -LtsAngleEdit::AngleUnitType LtsAngleEdit::getAngleCurrentUnit() const -{ - return static_cast(m_ui->cBoxAngleUnit->currentIndex()); -} - - - -double LtsAngleEdit::getAngle(unsigned int sboxIndex) const -{ - if ((int)sboxIndex > m_sBoxMap.count()) - return 0; - auto spinbox = m_sBoxMap.value(sboxIndex); - return spinbox->value(); -} - - - -QString LtsAngleEdit::toString(LtsAngleEdit::AngleUnitType t) -{ - switch(t) - { - case AngleUnitType::Deg: - return tr("Degree"); - case AngleUnitType::Mil: - return tr("Mil"); - case AngleUnitType::Rad: - return tr("Radian"); - default: - return tr(""); - } - return tr(""); -} - -void LtsAngleEdit::onCBoxAngleUnitChanged() -{ - AngleUnitType newUnit = static_cast(m_ui->cBoxAngleUnit->currentData().toInt()); - for(auto dbox : qAsConst(m_sBoxMap)) - { - const double value = getAngleConverted(dbox->value(), newUnit, m_currAngleUnit); - auto mapVal = m_dboxLimitMap.value(newUnit); - dbox->setMinimum(std::get(mapVal)); - dbox->setMaximum(std::get(mapVal)); - dbox->setDecimals(std::get(mapVal)); - dbox->setSingleStep(std::get(mapVal)); - dbox->setValue(value); - } - m_currAngleUnit = newUnit; -} - - - -double LtsAngleEdit::getAngleConverted(const double angle, LtsAngleEdit::AngleUnitType newAngleUnit, LtsAngleEdit::AngleUnitType oldAngleUnit) -{ - double convertedAngle = angle; - if (oldAngleUnit == AngleUnitType::Deg && newAngleUnit == AngleUnitType::Mil ) - convertedAngle = deg2Mil(angle); - else if (oldAngleUnit == AngleUnitType::Deg && newAngleUnit == AngleUnitType::Rad ) - convertedAngle = deg2Rad(angle); - else if (oldAngleUnit == AngleUnitType::Mil && newAngleUnit == AngleUnitType::Deg ) - convertedAngle = mil2Deg(angle); - else if (oldAngleUnit == AngleUnitType::Mil && newAngleUnit == AngleUnitType::Rad ) - convertedAngle = mil2Rad(angle); - else if (oldAngleUnit == AngleUnitType::Rad && newAngleUnit == AngleUnitType::Deg ) - convertedAngle = rad2Degree(angle); - else if (oldAngleUnit == AngleUnitType::Rad && newAngleUnit == AngleUnitType::Mil ) - convertedAngle = rad2Mil(angle); - - return convertedAngle; -} - -void LtsAngleEdit::setEnable(int sbIndex, bool enabled) -{ - if ((int)sbIndex > m_sBoxMap.count()) - return; - m_sBoxMap.value(sbIndex)->setEnabled(enabled); -} - -void LtsAngleEdit::showSpinbox(int sbIndex, bool visible) -{ - if ((int)sbIndex > m_sBoxMap.count()) - return; - m_sBoxMap.value(sbIndex)->setVisible(visible); -} - -} - diff --git a/Lts/Common/LtsWidgets/lts_angle_edit.h b/Lts/Common/LtsWidgets/lts_angle_edit.h deleted file mode 100755 index c889c84f9..000000000 --- a/Lts/Common/LtsWidgets/lts_angle_edit.h +++ /dev/null @@ -1,71 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include - - -class Ui_LtsAngleEdit; -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsAngleEdit : public QWidget -{ - Q_OBJECT - -public: - explicit LtsAngleEdit(QWidget *parent = nullptr); - ~LtsAngleEdit(); - - enum class AngleUnitType : unsigned int - { - Deg = 0, - Mil = 1, - Rad = 2 - }; - Q_ENUM(AngleUnitType) - - enum LimTuple : int - { - AngleMin, - AngleMax, - DecimalPlace, - SingleStep - }; - - const QString spinBoxName = QStringLiteral("sboxAngleValue"); - QString toString(AngleUnitType t); - void setAngleUnit(const AngleUnitType &type); - void setValue(const double value, unsigned int dboxIndex=0); - void setValue(const double value, AngleUnitType valueUnit); - void setValue(const double value, AngleUnitType valueUnit, unsigned int dboxIndex); - - void hideUnits(const QVector &list); - void showUnits(const QVector &list); - LtsAngleEdit::AngleUnitType getAngleCurrentUnit() const; - void initAngleEditWidget(const unsigned int sboxCount); - double getAngle(unsigned int sboxIndex = 0) const; - double getAngleConverted(const double angle, LtsAngleEdit::AngleUnitType newAngleUnit, LtsAngleEdit::AngleUnitType oldAngleUnit); - - void setEnable(int sbIndex, bool enabled); - void showSpinbox(int sbIndex, bool visible); - -protected: - void setCboxUnit(); - -private Q_SLOTS: - void onCBoxAngleUnitChanged(); -private: - Ui_LtsAngleEdit *m_ui; - QMap> m_sBoxMap; - QMap> m_dboxLimitMap; - AngleUnitType m_currAngleUnit; - QSet m_hiddenUnits; -}; - -} - diff --git a/Lts/Common/LtsWidgets/lts_angle_edit.ui b/Lts/Common/LtsWidgets/lts_angle_edit.ui deleted file mode 100755 index 8d96c3ea3..000000000 --- a/Lts/Common/LtsWidgets/lts_angle_edit.ui +++ /dev/null @@ -1,49 +0,0 @@ - - - LtsAngleEdit - - - - 0 - 0 - 107 - 34 - - - - Form - - - - 3 - - - 1 - - - 1 - - - 1 - - - 1 - - - - - - - - - 0 - 0 - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_background_widget.cpp b/Lts/Common/LtsWidgets/lts_background_widget.cpp deleted file mode 100755 index 57b25508d..000000000 --- a/Lts/Common/LtsWidgets/lts_background_widget.cpp +++ /dev/null @@ -1,137 +0,0 @@ - - - -#include "lts_background_widget.h" - -#include -#include -#include - -#ifdef Q_OS_WIN -#include -#endif - - -namespace LtsWidgets -{ - -LtsBackgroundWidget::LtsBackgroundWidget(QWidget *parent) - : QDialog(parent) - , m_isUltraWide(false) -{ - setMouseTracking(true); - setWindowFlags(windowFlags() | Qt::FramelessWindowHint ); - setWindowTitle(QStringLiteral("Splash")); - - setBackground(); -} - - -void LtsBackgroundWidget::setBackground() -{ - static const auto backgroundFiles = - { - QStringLiteral(":/project/background.png"), - QStringLiteral(":/project/background.jpg"), - }; - static const auto backgroundFilesWide = - { - QStringLiteral(":/project/background_wide.png"), - QStringLiteral(":/project/background_wide.jpg"), - }; - - m_pixmap = QPixmap(); - for (const QString &f : m_isUltraWide ? backgroundFilesWide : backgroundFiles) - { - if (!QFile::exists(f)) - continue; - m_pixmap = QPixmap(f); - if (m_pixmap.isNull()) - continue; - break; - } - - if (m_pixmap.isNull()) - { - if (m_isUltraWide) - { - m_isUltraWide = false; - setBackground(); - } - else - { - qWarning() << "BackgroundWidget::c_tor: file missing or is not a valid image ':/project/background.png'"; - } - } -} - - -void LtsBackgroundWidget::setUltraWide(bool set) -{ - if (set == m_isUltraWide) - return; - - m_isUltraWide = set; - setBackground(); - update(); -} - - -void LtsBackgroundWidget::resizeEvent(QResizeEvent *e) -{ - setUltraWide(width() > 3000); - QDialog::resizeEvent(e); -} - - -void LtsBackgroundWidget::paintEvent(QPaintEvent *e) -{ - if (!m_pixmap.isNull()) - { - QPainter painter(this); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - painter.drawPixmap(0, 0, width(), height(), m_pixmap); - } - QDialog::paintEvent(e); -} - - -bool LtsBackgroundWidget::event(QEvent *e) -{ - if (e->type() == QEvent::KeyPress || - e->type() == QEvent::MouseButtonPress || - e->type() == QEvent::MouseButtonRelease || - e->type() == QEvent::MouseMove) - { - - emit signalOperatorAction(); - e->ignore(); - return true; - } - - return QDialog::event(e); -} - - -void LtsBackgroundWidget::showFullScreen() -{ - -#ifdef Q_OS_WIN - winId(); - QWindowsWindowFunctions::setHasBorderInFullScreen(windowHandle(), true); -#endif - QDialog::showFullScreen(); - raise(); -} - - -void LtsBackgroundWidget::closeEvent(QCloseEvent *e) -{ -#ifdef DEVEL - deleteLater(); -#else - e->ignore(); -#endif -} - -} diff --git a/Lts/Common/LtsWidgets/lts_background_widget.h b/Lts/Common/LtsWidgets/lts_background_widget.h deleted file mode 100755 index 3ea88021c..000000000 --- a/Lts/Common/LtsWidgets/lts_background_widget.h +++ /dev/null @@ -1,51 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -class QPixmap; -class QEvent; - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsBackgroundWidget : public QDialog -{ - Q_OBJECT - -public: - explicit LtsBackgroundWidget(QWidget *parent = nullptr); - -protected: - void paintEvent(QPaintEvent *e) override; - void closeEvent(QCloseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - bool event(QEvent *e) override; - -private: - void setBackground(); - void setUltraWide(bool set = true); - -signals: - - - void signalOperatorAction(); - -public slots: - - - void showFullScreen(); -private: - QPixmap m_pixmap; - bool m_isUltraWide; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_base.h b/Lts/Common/LtsWidgets/lts_base.h deleted file mode 100755 index 1a2ce8900..000000000 --- a/Lts/Common/LtsWidgets/lts_base.h +++ /dev/null @@ -1,274 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsWidgets -{ -class LtsDialog; - -inline QString readableDuration(qint64 msecs, bool foldDays = true) -{ - if (msecs == 0) - return QObject::tr("---"); - - - if (msecs % 1000 != 0 && msecs < 1000 * 60) - return QObject::tr("%1 seconds").arg(QLocale().toString(msecs / 1000.0, 'f', 3)); - - qint64 secs = msecs / 1000; - qint64 mins = secs / 60; - qint64 hours = mins / 60; - qint64 days = hours / 24; - - if (foldDays) - days = 0; - else - hours = hours % 24; - - mins = mins % 60; - secs = secs % 60; - - - if (!secs && !mins && !hours) - return QObject::tr("%n day(s)", "", days); - if (!secs && !mins && !days) - return QObject::tr("%n hour(s)", "", hours); - if (!secs && !hours && !days) - return QObject::tr("%n minute(s)", "", mins); - if (!mins && !hours && !days) - return QObject::tr("%n second(s)", "", secs); - - QString strSecs = QObject::tr("%1s").arg(secs, 2, 10, QLatin1Char('0')); - QString strMins = QObject::tr("%1m").arg(mins, 2, 10, QLatin1Char('0')); - QString strHours = QObject::tr("%1h").arg(hours, 2, 10, QLatin1Char('0')); - QString strDays = QObject::tr("%1d").arg(days); - - QString retVal; - if (days) - retVal += strDays + QLatin1Char(' '); - if (days || hours) - retVal.append(strHours); - if (mins || hours || days) - retVal.append(strMins); - if (secs) - retVal.append(strSecs); - - return retVal; -} - -inline void addTouchScroll(QWidget *widget) -{ - if (!widget) - return; - widget->setAttribute(Qt::WA_AcceptTouchEvents); - - { - QScrollArea *sca = qobject_cast(widget); - if (sca) - { - QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture); - return; - } - } - - { - QAbstractItemView *aiv = qobject_cast(widget); - if (aiv) - { - QScroller::grabGesture(aiv->viewport(), QScroller::LeftMouseButtonGesture); - aiv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - aiv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - return; - } - } -} - -enum class ButtonSize -{ - eDefault = 40, - - - - - - -}; - - -enum class ButtonStyle -{ - eAll = 1, - eTextOnly, - eIconOnly, - eTextNextToIcon -}; - -enum class LayoutArea -{ - eUnknown, - eInfoTab, - eDialogTab, - eSpecialTab, - eMainTab, -}; - -enum class ExercisePart -{ - ePlanning, - eExecution, - eAfterAction, - eSpecial, -}; - - -namespace ExerciseState -{ - enum State - { - - eUnknown = 0, - eCreated = 1 << 0, - eStarted = 1 << 1, - ePaused = 1 << 2, - eStopped = 1 << 4, - eTerminated = 1 << 5, - - eAll = eCreated | eStarted | ePaused | eStopped | eTerminated - }; - - Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags) -} - -typedef ExerciseState::State ExcState; -typedef QFlags ExerciseStates; - -enum class CreateInstantType -{ - DontCreateInstant, - CreateInstantAndAddAction, - CreateInstantAndDiscardAction, -}; - -struct PluginData -{ - - using createWidgetFuncPtr = std::function; - using directFuncPtr = std::function; - - createWidgetFuncPtr createWidgetFunc = nullptr; - directFuncPtr directFunc = nullptr; - QStringList iconPaths; - QIcon menuIcon; - QString tooltip; - qulonglong dropHandlerKey = 0; - QString menuText; - QString menuGroup; - LtsUtils::LtsTranslatableIdentifier translatableMenuGroup; - QString identifier; - bool closable = true; - LayoutArea layoutArea = LayoutArea::eDialogTab; - int operatorRight = 0; - CreateInstantType createInstant = CreateInstantType::DontCreateInstant; - bool visibleInMenu = true; - ExercisePart exercisePart = ExercisePart::eSpecial; - ExerciseStates exerciseStates = ExerciseState::eAll; - - PluginData() = default; - PluginData(const QString &_menuGroup, - const QString &_menuText, - const QString &_identifier, - const createWidgetFuncPtr &_cwfp, - int _operatorRight, - ExercisePart _exercisePart = ExercisePart::eSpecial, - ExerciseStates _exerciseStates = ExerciseState::eAll) - : PluginData(_menuGroup, _menuText, _identifier, QIcon(), _cwfp, _operatorRight, _exercisePart, _exerciseStates) - {} - - PluginData(const QString &_menuGroup, - const QString &_menuText, - const QString &_identifier, - const QIcon &_icon, - const createWidgetFuncPtr &_cwfp, - int _operatorRight, - ExercisePart _exercisePart = ExercisePart::eSpecial, - ExerciseStates _exerciseStates = ExerciseState::eAll) - : createWidgetFunc(_cwfp) - , menuIcon(_icon) - , menuText(_menuText) - , menuGroup(_menuGroup) - , identifier(_identifier) - , operatorRight(_operatorRight) - , exercisePart(_exercisePart) - , exerciseStates(_exerciseStates) - { - } - - static PluginData createGroup(const QString &menuGroup, - const QString &menuText, - const QString &identifier, - int operatorRight) - { - return PluginData(menuGroup, menuText, identifier, nullptr, operatorRight, ExercisePart::eSpecial); - } - bool isValid() const { return createWidgetFunc != nullptr; } -}; - - - -class LTSWIDGETS_EXPORT Designer -{ - Q_GADGET -public: - enum ButtonSize - { - eDefault = static_cast(LtsWidgets::ButtonSize::eDefault), - - - - - - - }; - Q_ENUM(ButtonSize) - enum ButtonStyle - { - eAll = static_cast(LtsWidgets::ButtonStyle::eAll), - eTextOnly = static_cast(LtsWidgets::ButtonStyle::eTextOnly), - eIconOnly = static_cast(LtsWidgets::ButtonStyle::eIconOnly), - eTextNextToIcon = static_cast(LtsWidgets::ButtonStyle::eTextNextToIcon) - }; - Q_ENUM(ButtonStyle) - enum LayoutArea - { - eUnknownArea = static_cast(LtsWidgets::LayoutArea::eUnknown), - eInfoTab = static_cast(LtsWidgets::LayoutArea::eInfoTab), - eDialogTab = static_cast(LtsWidgets::LayoutArea::eDialogTab), - eSpecialTab = static_cast(LtsWidgets::LayoutArea::eSpecialTab), - eMainTab = static_cast(LtsWidgets::LayoutArea::eMainTab) - }; - Q_ENUM(LayoutArea) -}; - -} -Q_DECLARE_METATYPE(LtsWidgets::Designer::ButtonSize) -Q_DECLARE_METATYPE(LtsWidgets::Designer::ButtonStyle) -Q_DECLARE_METATYPE(LtsWidgets::Designer::LayoutArea) - -Q_DECLARE_METATYPE(LtsWidgets::ButtonSize) -Q_DECLARE_METATYPE(LtsWidgets::ButtonStyle) -Q_DECLARE_METATYPE(LtsWidgets::LayoutArea) diff --git a/Lts/Common/LtsWidgets/lts_base_layout.ui b/Lts/Common/LtsWidgets/lts_base_layout.ui deleted file mode 100755 index 30f16a1cd..000000000 --- a/Lts/Common/LtsWidgets/lts_base_layout.ui +++ /dev/null @@ -1,645 +0,0 @@ - - - LtsBaseLayoutWidget - - - - 0 - 0 - 1024 - 768 - - - - - 1024 - 768 - - - - Operator Application - - - - :/rde_icon_256.png:/rde_icon_256.png - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - RDE_SPLITTER_LEFT - - - false - - - - - - - - 0 - 0 - - - - <html><head/><body><p>PROJECT_LOGO</p></body></html> - - - - - - - 3 - - - - - - - App name - - - %APP_NAME% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - • - - - - - - - System time - - - %CUR_TIME% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Exercise name - - - %EX_NAME% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - • - - - - - - - Exercise phase - - - %EX_PHASE% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - • - - - - - - - Exercise status - - - %EX_STATUS% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - • - - - - - - - Exercise time - - - %EX_TIME% - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - • - - - - - - - Session type (Live or Replay) - - - %PART_TYPE% - - - - - - - • - - - - - - - Session type (Live or Replay) - - - %CUSTOM_TEXT% - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - 40 - 40 - - - - - - - - Qt::Vertical - - - - - - - - - - - - - - - [debug] - - - - - - - - 48 - 48 - - - - - 48 - 48 - - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 100 - 100 - - - - Qt::Horizontal - - - 10 - - - - - 0 - 0 - - - - - 100 - 100 - - - - true - - - - - - 0 - 0 - - - - - 100 - 100 - - - - Qt::Vertical - - - 10 - - - - - 0 - 0 - - - - - 100 - 100 - - - - - - - true - - - - - - 0 - 0 - - - - - 50 - 50 - - - - true - - - - - - - 100 - 100 - - - - Qt::Vertical - - - 10 - - - - - - - - 0 - 0 - - - - Planning - - - true - - - - - - - - 0 - 0 - - - - Execution - - - true - - - - - - - - 0 - 0 - - - - After Action - - - true - - - - - - - - 0 - 0 - - - - Extras - - - true - - - - - - - - 0 - 0 - - - - - 250 - 100 - - - - - - 0 - 0 - - - - - 100 - 100 - - - - - - - 0 - 0 - - - - - 100 - 100 - - - - - - - 0 - 0 - - - - - 100 - 100 - - - - - - - 0 - 0 - - - - - 100 - 100 - - - - - - - - - - - 0 - 0 - - - - - 100 - 100 - - - - true - - - - - - - - - - LtsWidgets::LtsProfileButton - QWidget -
LtsWidgets/lts_profile_button.h
- 1 -
- - LtsWidgets::LtsSplitter - QSplitter -
LtsWidgets/lts_splitter.h
- 1 -
- - LtsWidgets::LtsTabWidget - QTabWidget -
LtsWidgets/lts_tab_widget.h
- 1 -
- - LtsWidgets::LtsToolBarArea - QWidget -
LtsWidgets/lts_tool_bar_area.h
- 1 -
- - LtsWidgets::LtsToolBox - QToolBox -
LtsWidgets/lts_tool_box.h
- 1 -
-
- - - - -
diff --git a/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp b/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp deleted file mode 100755 index 5a6e2a1d7..000000000 --- a/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp +++ /dev/null @@ -1,1828 +0,0 @@ - - - -#include "lts_base_layout_widget.h" -#include "ui_lts_base_layout.h" - -#include "lts_dialog.h" -#include "lts_dialog_helper.h" -#include "lts_drag_icon.h" -#include "lts_grid_layout.h" -#include "lts_message_dialog.h" -#include "lts_operator_menu.h" -#include "lts_tool_bar_area.h" -#include "lts_splash.h" -#include "lts_events.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 - - -namespace LtsWidgets -{ - -static QVariant splitterInterpolator(const QList &start, const QList &end, qreal progress) -{ - QList ret; - ret = start; - for (int i = 0; i < start.size(); i++) - { - ret[i] = start[i] + qRound((end[i] - start[i]) * progress); - if (ret[i] <= 0) - ret[i] = 1; - } - - return QVariant::fromValue(ret); -} - - -LtsBaseLayoutWidget::LtsBaseLayoutWidget(QWidget *parent, bool splitMenu) - : QWidget(parent) - , m_currentAnimation(nullptr) - , m_ui(new Ui::LtsBaseLayoutWidget) - , m_partitionType(IDL::PartitionType::Global) - , m_domainId(-1) - , m_partitionId(-1) - , m_firstShow(true) - , m_showTerminalButton(false) - , m_exerciseTime(0) - , m_setCurrentTimeTimer(new QTimer(this)) - , m_irreplaceableOnce(false) - , m_bg(nullptr) - , m_screenCount(1) - , m_menuCollapsed(true) - , m_notiBox(nullptr) -{ - m_ui->setupUi(this); - -#ifndef LTS_DEBUG - m_ui->pbDebug->hide(); -#endif - - addDebugMenu(); - - - { - - m_ui->lbCurrentTime->setContextMenuPolicy(Qt::CustomContextMenu); - QMenu *currentTimeMenu = new QMenu(m_ui->lbCurrentTime); - currentTimeMenu->addAction(tr("Copy"), this, [this]() - { - QGuiApplication::clipboard()->setText(m_ui->lbCurrentTime->text()); - }); - currentTimeMenu->addAction(tr("Copy in milliseconds"), this, []() - { - QGuiApplication::clipboard()->setText(QString::number(QDateTime::currentMSecsSinceEpoch())); - }); - connect(m_ui->lbCurrentTime, &QLabel::customContextMenuRequested, - this, [this, currentTimeMenu](const QPoint &pos) { currentTimeMenu->popup(m_ui->lbCurrentTime->mapToGlobal(pos)); }); - } - - - { - - m_ui->lbExerciseTime->setContextMenuPolicy(Qt::CustomContextMenu); - QMenu *exerciseTimeMenu = new QMenu(m_ui->lbExerciseTime); - exerciseTimeMenu->addAction(tr("Copy"), this, [this]() - { - QGuiApplication::clipboard()->setText(m_ui->lbExerciseTime->text()); - }); - exerciseTimeMenu->addAction(tr("Copy in milliseconds"), this, [this]() - { - QGuiApplication::clipboard()->setText(QString::number(m_exerciseTime)); - }); - connect(m_ui->lbExerciseTime, &QLabel::customContextMenuRequested, - this, [this, exerciseTimeMenu](const QPoint &pos) { exerciseTimeMenu->popup(m_ui->lbExerciseTime->mapToGlobal(pos)); }); - } - - - setMinimumSize(1280, 900); - - m_ui->detailWidget->setSubTabsVisible(true); - - - qRegisterAnimationInterpolator >(splitterInterpolator); - qRegisterMetaType("LayoutArea"); - - m_ui->mainWidget->setTabBarAutoHide(true); - - m_ui->infoWidget->setActuatorTabStyle(true); - m_ui->infoWidget->setTabBarAutoHide(false); - m_ui->infoWidget->setTabPosition(QTabWidget::West); - - if (m_ui->infoWidget->count() == 0) - m_ui->infoWidget->hide(); - - if (m_ui->detailWidget->count() == 0) - m_ui->detailWidget->hide(); - - if (m_ui->specialWidget->count() == 0) - m_ui->specialWidget->hide(); - - - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->infoWidget), 0); - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->mainSplitter), 20); - - - m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->mainWidget), 20); - m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->detailWidget), 0); - - - m_ui->rightSplitter->setStretchFactor(m_ui->rightSplitter->indexOf(m_ui->stackedWidget), 20); - m_ui->rightSplitter->setStretchFactor(m_ui->rightSplitter->indexOf(m_ui->specialWidget), 0); - - m_isHidden = false; - - - connect(m_ui->profileButton, &LtsProfileButton::signalClicked, this, &LtsBaseLayoutWidget::slotOperatorMenuClicked); - - - - - - m_ui->lbCustomTextBullet->hide(); - m_ui->lbCustomText->hide(); - - m_ui->lbExerciseTime->setText(QStringLiteral(" # ")); - m_ui->lbCurrentTime->setText(QStringLiteral(" # ")); - - connect(m_setCurrentTimeTimer, &QTimer::timeout, this, &LtsBaseLayoutWidget::setCurrentTime); - m_setCurrentTimeTimer->start(200); - - setExerciseData(m_exerciseData); - - if (splitMenu) - { - m_bg = new QButtonGroup(m_ui->naviWidget); - m_bg->addButton(m_ui->pbPlanning, (int) ExercisePart::ePlanning); - m_bg->addButton(m_ui->pbExecution, (int) ExercisePart::eExecution); - m_bg->addButton(m_ui->pbAfterAction, (int) ExercisePart::eAfterAction); - m_bg->addButton(m_ui->pbSpecial, (int) ExercisePart::eSpecial); - - connect(m_bg, QOverload::of(&QButtonGroup::buttonClicked), m_ui->stackedWidget, [this](int id) - { - m_ui->stackedWidget->setCurrentWidget(getToolBox((ExercisePart) id)); - }); - m_ui->pbSpecial->click(); - } - else - { - m_ui->stackedWidget->setCurrentWidget(getToolBox(ExercisePart::eSpecial)); - } - - checkExercisePartButtonVisibility(); -} - - -LtsBaseLayoutWidget::~LtsBaseLayoutWidget() -{ - - - - - if (m_notiBox != nullptr) - m_notiBox->setParent(nullptr); - delete m_notiBox; - - delete m_currentAnimation; - m_currentAnimation = nullptr; - clearMenuActions(); - delete m_ui; - m_ui = nullptr; - -} - - -void LtsBaseLayoutWidget::setCurrentTime() -{ - m_ui->lbCurrentTime->setText(DTG::toString(QDateTime::currentDateTime())); - - if (m_exerciseTime > 0) - m_ui->lbExerciseTime->setText(DTG::toStringMSec(m_exerciseTime)); - else - m_ui->lbExerciseTime->setText(QStringLiteral(" # ")); -} - - -void LtsBaseLayoutWidget::slotScreenCountChanged(int count) -{ - m_screenCount = count; -} - - -const QString LtsBaseLayoutWidget::customText() const -{ - return m_ui->lbCustomText->text(); -} - - -void LtsBaseLayoutWidget::setCustomText(const QString &text) -{ - if (text.isEmpty()) - { - m_ui->lbCustomText->setText(QString()); - m_ui->lbCustomText->hide(); - m_ui->lbCustomTextBullet->hide(); - - return; - } - - m_ui->lbCustomText->show(); - m_ui->lbCustomTextBullet->show(); - m_ui->lbCustomText->setText(text); -} - - -void LtsBaseLayoutWidget::addDebugMenu() -{ -#ifdef LTS_DEBUG - QMenu *menu = new QMenu(this); - - menu->addAction(QStringLiteral("NOTE: all actions are delayed by 1s"))->setEnabled(false); - - menu->addSeparator(); - - menu->addAction(QStringLiteral("logout"), this, [this]() - { - QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::slotLogout); - }); - - menu->addAction(QStringLiteral("forced logout"), this, [this]() - { - QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::signalLogout); - }); - - menu->addAction(QStringLiteral("app exit"), this, [this]() - { - QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::signalClose); - }); - - menu->addSeparator(); - - menu->addAction(QStringLiteral("show test message, unparented"), this, [this]() - { - QTimer::singleShot(1000, this, []() { LtsWidgets::LtsMessageDialog::showMessage(nullptr , QStringLiteral("test"), QStringLiteral("test")); } ); - }); - - menu->addAction(QStringLiteral("show test message"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("test"), QStringLiteral("test")); } ); - }); - - menu->addSeparator(); - - menu->addAction(QStringLiteral("hide details tab"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { this->hideWidgets(); } ); - }); - - menu->addAction(QStringLiteral("show details tab"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { this->showWidgets(); } ); - }); - - menu->addAction(QStringLiteral("hide details tab, animated"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { this->onHideAnimated(); } ); - }); - - menu->addAction(QStringLiteral("show details tab, animated"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { this->onReShowAnimated(); } ); - }); - - menu->addSeparator(); - - menu->addAction(QStringLiteral("hide top bar"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() { this->setTopBarVisible(false); } ); - }); - - menu->addSeparator(); - - menu->addAction(QStringLiteral("test NotificationBox with error"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() - { - showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about
something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Error, - [this](const QString &) - { - new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); - }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); - }); - }); - - menu->addAction(QStringLiteral("test NotificationBox with success"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() - { - showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Success, - [this](const QString &) - { - new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); - }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); - }); - }); - - menu->addAction(QStringLiteral("test NotificationBox with info"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() - { - showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Info, - [this](const QString &) - { - new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); - }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); - }); - }); - - menu->addAction(QStringLiteral("test NotificationBox with warning"), this, [this]() - { - QTimer::singleShot(1000, this, [this]() - { - showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Warning, - [this](const QString &) - { - new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); - }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); - }); - }); - - m_ui->pbDebug->setMenu(menu); -#endif -} - - -void LtsBaseLayoutWidget::showEvent(QShowEvent *event) -{ - QWidget::showEvent(event); - - if (!m_firstShow) - return; - - m_firstShow = false; - - - QSize rendererSizeHint = m_ui->topLayoutWidget->sizeHint(); - rendererSizeHint.scale(rendererSizeHint.width() * 0.65, rendererSizeHint.height() * 0.65, Qt::IgnoreAspectRatio); - const auto dpr = devicePixelRatioF(); - - { - const QSize s(QSize(rendererSizeHint.height(), rendererSizeHint.height()) * dpr); - - QSvgRenderer svgRenderer; - QPixmap pixmap(s); - pixmap.fill(Qt::transparent); - - QPainter painter(&pixmap); - svgRenderer.load(QLatin1String(":rde_splitter_left.svg")); - svgRenderer.render(&painter); - painter.end(); - - pixmap.setDevicePixelRatio(dpr); - m_ui->lbRdeSplitterLeft->setPixmap(pixmap); - } - - { - - QString projectLogoFilename = QLatin1String(":project/logo.png"); - QPixmap pixmap(projectLogoFilename); - pixmap.setDevicePixelRatio(dpr); - - if (pixmap.isNull()) - { - qWarning() << "LoginWindow::c_tor: (PROJECT LOGO) file missing or is not an valid image '" << projectLogoFilename << "'"; - } - else - { - pixmap = pixmap.scaled(rendererSizeHint * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation); - m_ui->lbProjectLogo->setPixmap(pixmap); - } - } -#ifdef TABLET_MODE - QList scrollWidgets = findChildren(); - - for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i) - { - QWidget *widget = *i; - - QVariant isConst = widget->property("const"); - if (isConst.isValid() && isConst.toBool()) - continue; - - addTouchScroll(widget); - } -#endif - - Splash::getInstance()->done(); -} - - -void LtsBaseLayoutWidget::addTouchScroll(QWidget *widget) -{ - if (!widget) - return; - widget->setAttribute(Qt::WA_AcceptTouchEvents); - - QScrollArea *sca = qobject_cast(widget); - if (sca) - { - QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture); - return; - } - - QAbstractItemView *aiv = qobject_cast(widget); - if (aiv) - { - QScroller::grabGesture(aiv->viewport(), QScroller::LeftMouseButtonGesture); - aiv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - aiv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - return; - } -} - - -void LtsBaseLayoutWidget::setAppName(const QString &appName) -{ - m_ui->lbAppName->setText(appName); -} - - -void LtsBaseLayoutWidget::setExerciseData(const ExerciseData &data) -{ - m_exerciseData = data; - - m_ui->lbExerciseName->setText(data.exName.isEmpty() ? tr("no exercise") : data.exName); - - QString exerciseNameToolTip(tr("Exercise name")); - if (!data.exName.isEmpty() && m_partitionId > 0) - { - exerciseNameToolTip += tr("\nPartition Id:"); - exerciseNameToolTip += QStringLiteral(" %1").arg(m_partitionId); - } - - if (!data.exDescription.isEmpty()) - { - exerciseNameToolTip += tr("\nDescription:"); - exerciseNameToolTip += QStringLiteral(" %1").arg(data.exDescription); - } - m_ui->lbExerciseName->setToolTip(exerciseNameToolTip); - - LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); - const bool enableEditPhases = ps->value(QStringLiteral("exm"), - QStringLiteral("enableEditPhases")).toBool(); - if (!enableEditPhases) - { - m_ui->lbExercisePhase->hide(); - m_ui->bullet1->hide(); - } - - m_ui->lbExercisePhase->setText(data.phName.isEmpty() ? tr("no phase") : data.phName); - - QString phaseNameToolTip(tr("Exercise phase")); - if (!data.phDescription.isEmpty()) - { - phaseNameToolTip += tr("\nDescription:"); - phaseNameToolTip += QStringLiteral(" %1").arg(data.phDescription); - } - m_ui->lbExercisePhase->setToolTip(phaseNameToolTip); - - m_ui->lbExerciseStatus->setText(data.exStateString.isEmpty() ? tr("no status") : data.exStateString); - - if (data.exStateCol.isValid() && data.exStateCol != QColor()) - { - QPalette p = m_ui->lbExerciseStatus->palette(); - p.setColor(m_ui->lbExerciseStatus->backgroundRole(), data.exStateCol); - p.setColor(m_ui->lbExerciseStatus->foregroundRole(), data.exStateCol); - m_ui->lbExerciseStatus->setPalette(p); - } - - m_ui->lbPartitionType->setText(data.partitionType); - - applyExerciseStateRestricition(); -} - -void LtsBaseLayoutWidget::applyExerciseStateRestricition() -{ - for (auto it = m_mapButtons.cbegin(); it != m_mapButtons.cend(); ++it) - { - bool visible = it.value().exerciseStates.testFlag(m_exerciseData.exState); - it.key()->setButtonVisible(visible); - } -} - -void LtsBaseLayoutWidget::initilizationFinished() -{ - - - - - - - - -} - -void LtsBaseLayoutWidget::setToolBarActions(const QStringList &actions) -{ - QVector desiredActions; - - m_notFoundYet = actions; - - for (const QString &action : actions) - { - for (auto innerIt = m_mapButtons.cbegin(); innerIt != m_mapButtons.cend(); ++innerIt) - { - if (innerIt.value().identifier.compare(action) == 0) - { - desiredActions << innerIt.key(); - m_notFoundYet.removeAll(action); - } - } - } - - m_ui->toolBar->setMenuEntries(desiredActions); -} - -QStringList LtsBaseLayoutWidget::toolBarActions() const -{ - if (m_ui == nullptr || m_ui->toolBar == nullptr) - return QStringList(); - return m_ui->toolBar->menuEntries(); -} - -void LtsBaseLayoutWidget::setFunctionKeys(const QMap &actions) -{ - for (auto it = actions.cbegin(); it != actions.cend(); ++it) - m_ui->toolBar->setFunctionKey(it.key(), it.value()); -} - -QMap LtsBaseLayoutWidget::functionKeys() const -{ - if (m_ui == nullptr) - return QMap(); - return m_ui->toolBar->functionKeys(); -} - -void LtsBaseLayoutWidget::setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId) -{ - m_domainId = domainId; - m_partitionType = partitionType; - m_partitionId = partitionId; - m_workspaceId = workspaceId; -} - - - -void LtsBaseLayoutWidget::setOperatorName(const QString &operatorName, bool showTerminalButton) -{ - m_operatorName = operatorName; - if (m_operatorName.isEmpty()) - m_operatorName = tr("Admin"); - - m_ui->profileButton->setToolTip(tr("Operator Name: %1").arg(m_operatorName)); - m_showTerminalButton = showTerminalButton; -} - - -void LtsBaseLayoutWidget::setProfilePicture(const QPixmap &profilePicture) -{ - m_ui->profileButton->setPixmap(profilePicture); -} - - -void LtsBaseLayoutWidget::slotOperatorMenuClicked() -{ - bool debug = false; - -#ifndef LTS_DEBUG - if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("DeveloperTools.Actions")) == QVariant(true)) -#endif - { - debug = true; - } - - LtsOperatorMenu *operatorMenu = new LtsOperatorMenu(m_operatorName, m_workspaceId, debug, m_screenCount, this); - operatorMenu->setTerminalButton(m_showTerminalButton); - - bool hasOperatorSettings = false; - for (const PluginData &pd : qAsConst(m_mapButtons)) - { - if (pd.identifier == QStringLiteral("_OpmShowOperatorSettings")) - { - hasOperatorSettings = true; - break; - } - } - operatorMenu->setOperatorSettingsButton(hasOperatorSettings); - - connect(operatorMenu, &LtsOperatorMenu::signalLogout, this, &LtsBaseLayoutWidget::slotLogout); - connect(operatorMenu, &LtsOperatorMenu::signalExit, this, &LtsBaseLayoutWidget::slotClose); - connect(operatorMenu, &LtsOperatorMenu::signalShowOperatorSettings, this, &LtsBaseLayoutWidget::signalShowOperatorSettings); - connect(operatorMenu, &LtsOperatorMenu::signalSwap, this, &LtsBaseLayoutWidget::signalSwap); - - operatorMenu->show(); -} - - -bool LtsBaseLayoutWidget::haveUnsavedChanges() -{ - if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eDialogTab))) - return true; - if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eInfoTab))) - return true; - if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eMainTab))) - return true; - if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eSpecialTab))) - return true; - - return false; -} - - - - - - - - - - - - -bool LtsBaseLayoutWidget::notifyUnsavedChanges() -{ - if (isSignalConnected(QMetaMethod::fromSignal(&LtsBaseLayoutWidget::signalNotifyUnsavedChanges))) - { - bool reject = false; - emit signalNotifyUnsavedChanges(reject); - return reject; - } - else - { - if (haveUnsavedChanges()) - { - LtsWidgets::Buttons buttons; - buttons.setFlag(Button::Yes, true); - buttons.setFlag(Button::Cancel, true); - QHash buttonTexts; - buttonTexts.insert(Button::Yes, tr("Discard All")); - const auto ret = LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.
Please save or discard their changes before closing"), tr("Unsaved changes"),Behaviour::Modal, buttons, buttonTexts); - return ret == Button::Yes; - } - } - return true; -} - - -void LtsBaseLayoutWidget::slotLogout() -{ - emit signalLogout(); -} - - -bool LtsBaseLayoutWidget::slotClose() -{ - if (!notifyUnsavedChanges()) - return false; - - - - - - - - - - - - - - - - - - - - - - - hide(); - - emit signalClose(); - return true; -} - - - - - - - - -void LtsBaseLayoutWidget::closeEvent(QCloseEvent *event) -{ - event->ignore(); - - slotClose(); -} - - -bool LtsBaseLayoutWidget::focusTab(const QString &menuGroup, const QString &identifier, LayoutArea area) -{ - LtsTabWidget *tab = getTabFromLayoutArea(area); - if (!tab) - { - qWarning() << "LtsBaseLayoutWidget::focusTab: tab, by enum type, not found!"; - return false; - } - - return tab->focusTab(menuGroup, identifier); -} - - -bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const -{ - QWidget *w = tab->widget(tab->currentIndex()); - - if (w == nullptr) - return false; - - const LtsTabWidget *secondTab = qobject_cast(w); - - if (secondTab != nullptr) - return isDialogFocused(dialog, secondTab); - - const LtsDialogHelper *helper = qobject_cast(w); - - if (helper != nullptr) - return helper->compare(dialog); - - qWarning() << "LtsBaseLayoutWidget::isDialogFocused(LtsDialog *dialog, LtsTabWidget *tab): i found something weird.." << w->metaObject()->className(); - return false; -} - - -bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const -{ - const LtsTabWidget *tab = getTabFromLayoutArea(layoutArea); - if (tab == nullptr) - { - qWarning() << "LtsBaseLayoutWidget::addWidget: tab, by enum type, not found!"; - return false; - } - - return isDialogFocused(dialog, tab); -} - - -bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog) const -{ - return (isDialogFocused(dialog, LayoutArea::eDialogTab) || - isDialogFocused(dialog, LayoutArea::eInfoTab) || - isDialogFocused(dialog, LayoutArea::eSpecialTab) || - isDialogFocused(dialog, LayoutArea::eMainTab)); -} - - -bool LtsBaseLayoutWidget::addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool focusNewTab) -{ - LtsTabWidget *tab = getTabFromLayoutArea(area); - if (tab && dialog && (!identifier.isEmpty())) - { - QWidget *existingWidget = tab->getWidget(menuGroup, identifier); - - if (nullptr != existingWidget) - { - if (tab->removeWidget(existingWidget)) - { - existingWidget->deleteLater(); - - } - else - { - qWarning() << __FUNCTION__ << " : Remove existing widget from TabWidget failed."; - } - } - } - return addWidget(dialog, menuGroup, identifier, area, setClosable, focusNewTab); -} - - -bool LtsBaseLayoutWidget::addWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool focusNewTab) -{ - if (!dialog) - { - qWarning() << "LtsBaseLayoutWidget::addWidget: cannot add null-widget!"; - return false; - } - - LtsTabWidget *tab = getTabFromLayoutArea(area); - if (!tab) - { - qWarning() << "LtsBaseLayoutWidget::addWidget: tab, by enum type, not found!"; - return false; - } - - dialog->setMenuGroup(menuGroup); - dialog->setIdentifier(identifier); - QWidget *helper = dialog->getHelper(); - if (!helper) - { - helper = dialog; - dialog->setAttribute(Qt::WA_DeleteOnClose); - } - - if (!identifier.isEmpty()) - { - QWidget *existingWidget = tab->getWidget(menuGroup, identifier); - if (existingWidget != nullptr) - { - helper->deleteLater(); - - if (focusNewTab) - tab->focusTab(menuGroup, identifier); - - - int indexOfSplitter = getIndexOfTab(tab); - - if (area == LayoutArea::eDialogTab && !(m_ui->mainSplitter->sizes().at(indexOfSplitter))) - m_ui->mainSplitter->animate(1); - else if (area == LayoutArea::eInfoTab && !(m_ui->masterSplitter->sizes().at(indexOfSplitter))) - m_ui->masterSplitter->animate(1); - - - return false; - } - } - - dialog->setBaseLayoutWidget(this); - - connect(dialog, &LtsDialog::startMainSelect, - this, &LtsBaseLayoutWidget::onHideAnimated); - connect(dialog, &LtsDialog::endMainSelect, - this, &LtsBaseLayoutWidget::onReShowAnimated); - - - emit signalStapleTopicLockHandler(dialog); - dialog->triggerTopicLock(); - - - - - tab->addTab(menuGroup, identifier, helper, setClosable, focusNewTab); - - if (tab->count() > 1) - { - int indexOfSplitter = getIndexOfTab(tab); - if (area == LayoutArea::eDialogTab && !(m_ui->mainSplitter->sizes().at(indexOfSplitter))) - { - qDebug()<<"animate 1a: "; - m_ui->mainSplitter->animate(1); - } - else if (area == LayoutArea::eInfoTab && !(m_ui->masterSplitter->sizes().at(indexOfSplitter))) - { - qDebug()<<"animate 1b: "; - m_ui->masterSplitter->animate(1); - } - } - -#ifdef ADJUST_SPLITTER_SIZES_DIALOGTAB - - - if (LayoutArea::eDialogTab == area) - { - const QList & sizes = m_ui->mainSplitter->sizes(); - int requestedTabHeight = tab->sizeHint().height(); - int mainHeight = sizes[0]+sizes[1]-requestedTabHeight; - - m_ui->mainSplitter->setSizes( QList() << mainHeight << requestedTabHeight ); - } -#endif - - return true; -} - - -LtsDialog *LtsBaseLayoutWidget::getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area) -{ - LtsTabWidget *tab = getTabFromLayoutArea(area); - - if (tab == nullptr) - return nullptr; - - QWidget *w = tab->getWidget(menuGroup, identifier); - - if (w == nullptr) - return nullptr; - - LtsDialogHelper *helper = qobject_cast(w); - - if (helper == nullptr) - return nullptr; - - LtsDialog *dialog = qobject_cast(helper->getDialog()); - return dialog; -} - - -void LtsBaseLayoutWidget::clearMenuActions() -{ - - while (m_ui->naviActionsPlanning->count()) - m_ui->naviActionsPlanning->removeItem(0); - while (m_ui->naviActionsExecution->count()) - m_ui->naviActionsExecution->removeItem(0); - while (m_ui->naviActionsAfterAction->count()) - m_ui->naviActionsAfterAction->removeItem(0); - while (m_ui->naviActionsSpecial->count()) - m_ui->naviActionsSpecial->removeItem(0); - - checkExercisePartButtonVisibility(); - - - for(auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) - delete it.key(); - - m_mapButtons.clear(); -} - - -void LtsBaseLayoutWidget::executeCreateInstantFunctions(const PluginData &plData) -{ - if (plData.createWidgetFunc) - { - LtsDialog *w = plData.createWidgetFunc(this); - - if (w) - addWidget(w, plData.menuGroup, plData.identifier, plData.layoutArea, plData.closable); - } - - if (plData.directFunc) - { - plData.directFunc(plData); - } -} - - -void LtsBaseLayoutWidget::setExerciseTime(qint64 mSecsSinceEpoch) -{ - m_exerciseTime = mSecsSinceEpoch; -} - -LtsDragIcon *LtsBaseLayoutWidget::addMenuAction(const PluginData &plData, bool isFirstRun) -{ - if (!LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(plData.operatorRight)) - { - qDebug() << " > Action" << plData.menuGroup << ":" << plData.menuText << "was not added due to missing operator right 0x" << QString::number(plData.operatorRight, 16); - return nullptr; - } - - if (isFirstRun) - { - switch (plData.createInstant) - { - case CreateInstantType::CreateInstantAndAddAction: - { - executeCreateInstantFunctions(plData); - break; - } - - case CreateInstantType::CreateInstantAndDiscardAction: - { - executeCreateInstantFunctions(plData); - return nullptr; - } - - default: - { - break; - } - } - } - else - { - if (plData.createInstant == CreateInstantType::CreateInstantAndDiscardAction) - return nullptr; - } - - - - bool err = false; - if (plData.identifier.isEmpty()) - { -#ifdef LTS_DEBUG - LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("STOP!\n" - "Hier wurde ein Menu Entry hinzugefuegt, welcher ueber keinen Identifier verfuegt!\n" - "Bitte aendern!\n" - "Jetzt Debugger attachen und schauen woher der aufruf kommt.\n" - "Da der Identifier fehlt kann ich Dir hier nicht sagen um welchen Menu Entry es sich handelt =)")); -#endif - err = true; - } - - if (plData.menuGroup.isEmpty()) - { -#ifdef LTS_DEBUG - LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("STOP!\n" - "Hier wurde ein Menu Entry hinzugefuegt, welcher ueber keine Menu Group verfuegt!\n" - "Bitte aendern!\n" - "Jetzt Debugger attachen und schauen woher der aufruf kommt.\n" - "Da der Identifier fehlt kann ich Dir hier nicht sagen um welchen Menu Entry es sich handelt =)")); -#endif - err = true; - } - - if (err) - return nullptr; - - - - bool found = false; - - if (!isFirstRun) - { - for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) - { - LtsDragIcon *thatIcon = it.key(); - - if (thatIcon->objectName() == plData.identifier) - { - thatIcon->setText(plData.menuText); - thatIcon->setContext(plData.menuGroup); - - bool groupFound = false; - - for (int i = 0; i < m_ui->stackedWidget->count(); ++i) - { - LtsWidgets::LtsToolBox *tb = qobject_cast(m_ui->stackedWidget->widget(i)); - int groupWidgetIndex = tb->indexOf(thatIcon->parentWidget() ); - if (groupWidgetIndex >= 0) - { - tb->setItemText(groupWidgetIndex, plData.menuGroup); - groupFound = true; - break; - } - } - if (!groupFound) - { - qWarning() << "group widget not found"; - } - found = true; - break; - } - } - - if (!found) - { - - } - else - { - return nullptr; - } - } - - LtsWidgets::LtsToolBox *tb = getToolBox(m_bg ? plData.exercisePart : ExercisePart::eSpecial); - - - QWidget *tmpW = tb->findChild(plData.menuGroup); - if (!tmpW) - { - - if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) - return nullptr; - - - tmpW = new QWidget; - tmpW->setObjectName(plData.menuGroup); - - LtsGridLayout *ltsGridLayout = new LtsGridLayout(LtsWidgets::ButtonSize::eDefault, tmpW); - tmpW->setLayout(ltsGridLayout); - - tb->addItem(tmpW, plData.menuGroup); - connect(tb, &LtsToolBox::resized, - ltsGridLayout, &LtsGridLayout::onParentResize); - } - - - QLayout *widgetLayout = tmpW->layout(); - LtsGridLayout *ltsGridLayout = qobject_cast(widgetLayout); - if (!ltsGridLayout) - { - - qWarning() << "temp widget has no layout! cannot add action" << plData.menuText << "to menu! (the bad widget was of type" << tmpW->metaObject()->className() << ") [did you forget to add the menuGroup to that action?]"; - return nullptr; - } - - - if (widgetLayout->count() > 0) - { - QSpacerItem *spacer = dynamic_cast(widgetLayout->itemAt(widgetLayout->count() - 1)); - if (spacer) - { - ltsGridLayout->removeItem(spacer); - delete spacer; - } - } - - - LtsDragIcon *dragIcon = nullptr; - if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) - { - QFrame *line = new QFrame(tmpW); - line->setLineWidth(2); - line->setFrameStyle(QFrame::HLine); - - ltsGridLayout->add_widget(line); - } - else - { - if (isFirstRun || !found) - { - dragIcon = createButton(plData); - - ltsGridLayout->add_widget(dragIcon); - m_mapButtons.insert(dragIcon, plData); - - auto it = std::find_if(m_notFoundYet.begin(), m_notFoundYet.end(), [plData](const QString &s) - { - return s == plData.identifier; - }); - - if (it != m_notFoundYet.end()) - { - m_ui->toolBar->addMenuEntry(dragIcon); - m_notFoundYet.erase(it); - } - } - } - - checkExercisePartButtonVisibility(); - - QSpacerItem *spacer = new QSpacerItem(10, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - ltsGridLayout->addItem(spacer, ltsGridLayout->rowCount(), 0); - - return dragIcon; -} - - -void LtsBaseLayoutWidget::addNotification(QToolButton *noti) -{ - m_ui->notificationLayoutWidget->layout()->addWidget(noti); -} - - -bool LtsBaseLayoutWidget::notificationExists(const QString &identifier) const -{ - return (m_ui->notificationLayoutWidget->findChild(identifier, Qt::FindDirectChildrenOnly) != nullptr); -} - - -bool LtsBaseLayoutWidget::removeMenuAction(const QString &byIdentifier) -{ - for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) - { - if (it.value().identifier == byIdentifier) - return removeMenuAction(it.key()); - } - - return false; -} - - -bool LtsBaseLayoutWidget::removeMenuAction(const PluginData &plData) -{ - for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) - { - if (it.value().identifier == plData.identifier) - return removeMenuAction(it.key()); - } - - return false; -} - - -bool LtsBaseLayoutWidget::removeMenuAction(LtsDragIcon *icon) -{ - auto it = m_mapButtons.find(icon); - if (it == m_mapButtons.end()) - return false; - - const QString menuGroup = it->menuGroup; - const ExercisePart ep = it->exercisePart; - m_mapButtons.erase(it); - - icon->setParent(nullptr); - icon->deleteLater(); - - checkMenuGroup(menuGroup, ep); - - return true; -} - -void LtsBaseLayoutWidget::executeAction(const QString &actionIdentifier) -{ - const QList plDatas(m_mapButtons.values()); - auto it = std::find_if(plDatas.constBegin(), plDatas.constEnd(), [actionIdentifier](const PluginData &plData) - { - return (plData.identifier == actionIdentifier); - }); - - if (it == plDatas.constEnd()) - return; - executePluginData(*it); -} - - -void LtsBaseLayoutWidget::executePluginData(const PluginData &plData) -{ - const QString &identifier = plData.identifier; - const QString &menuGroup = plData.menuGroup; - - LtsTabWidget *tab = getTabFromLayoutArea(plData.layoutArea); - - if (!tab->focusTab(menuGroup, identifier)) - { - - - if (plData.createWidgetFunc) - { - LtsDialog *w = plData.createWidgetFunc(this); - if (w) - { - - addWidget(w, menuGroup, identifier, plData.layoutArea, plData.closable); - } - else - { - - } - } - else - { - if (plData.directFunc) - plData.directFunc(plData); - } - } - else - { - - } -} - - -void LtsBaseLayoutWidget::onMenuEntryClicked() -{ - LtsDragIcon *btn = qobject_cast(sender()); - if (!btn) - return; - - if (!m_mapButtons.contains(btn)) - return; - - executePluginData(m_mapButtons.value(btn)); -} - - -void LtsBaseLayoutWidget::resetLayoutVisibility() -{ - for (auto i = m_oldLayoutVisibilityStates.cbegin(); i != m_oldLayoutVisibilityStates.cend(); ++i) - { - const auto &tabPtr = i.key(); - if (tabPtr) - tabPtr->setVisible(i.value()); - } - - m_oldLayoutVisibilityStates.clear(); -} - - -void LtsBaseLayoutWidget::hideVisibleLayouts() -{ - if (!m_oldLayoutVisibilityStates.isEmpty()) - { - qWarning() << "LtsBaseLayoutWidget::hideVisibleLayouts: old states available - ignoring"; - return; - } - - LtsWidgets::LtsTabWidget *infoTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eInfoTab); - LtsWidgets::LtsTabWidget *specialTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eSpecialTab); - LtsWidgets::LtsTabWidget *dialogTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eDialogTab); - - m_oldLayoutVisibilityStates.insert(infoTab, infoTab->isVisible()); - m_oldLayoutVisibilityStates.insert(specialTab, specialTab->isVisible()); - m_oldLayoutVisibilityStates.insert(dialogTab, dialogTab->isVisible()); - - - if (infoTab->isVisible()) - infoTab->setVisible(false); - if (specialTab->isVisible()) - specialTab->setVisible(false); - if (dialogTab->isVisible()) - dialogTab->setVisible(false); -} - - -void LtsBaseLayoutWidget::setTopBarVisible(bool visible) -{ - m_ui->topLayoutWidget->setVisible(visible); -} - - -bool LtsBaseLayoutWidget::isTopBarVisible() const -{ - return m_ui->topLayoutWidget->isVisible(); -} - - -bool LtsBaseLayoutWidget::isMenuCollapsed() const -{ - return m_menuCollapsed; -} - - -void LtsBaseLayoutWidget::setMenuVisible(bool visible) -{ - m_ui->rightSplitter->setVisible(visible); -} - - -bool LtsBaseLayoutWidget::isMenuVisible() const -{ - return m_ui->rightSplitter->isVisible(); -} - - -void LtsBaseLayoutWidget::collapseMenu(bool collapse) -{ - m_menuCollapsed = collapse; - if (collapse) - { - m_ui->masterSplitter->setSizes(m_menuSplitterSizes); - } - else - { - m_menuSplitterSizes = m_ui->masterSplitter->sizes(); - m_ui->masterSplitter->setSizes(QList() << 1 << 0 ); - } -} - - -void LtsBaseLayoutWidget::onReShowAnimated() -{ - QPropertyAnimation *animation = new QPropertyAnimation(m_ui->mainSplitter, "sizes", this); - animation->setDuration(500); - - - QList start = m_mainSplitterSizes; - start[getIndexOfTab(getTabFromLayoutArea(LayoutArea::eDialogTab))] = 0; - - QList end = m_mainSplitterSizes; - - animation->setStartValue(QVariant::fromValue(start)); - animation->setEndValue(QVariant::fromValue(end)); - - connect(animation, &QPropertyAnimation::finished, this, [this]() { m_currentAnimation = nullptr; }); - - m_ui->detailWidget->show(); - animation->start(QAbstractAnimation::DeleteWhenStopped); - m_isHidden = false; - - delete m_currentAnimation; - m_currentAnimation = animation; -} - - -void LtsBaseLayoutWidget::onHideAnimated() -{ - if (m_currentAnimation) - { - delete m_currentAnimation; - m_currentAnimation = nullptr; - } - - QPropertyAnimation *animation = new QPropertyAnimation(m_ui->mainSplitter, "sizes", this); - animation->setDuration(500); - - m_mainSplitterSizes = m_ui->mainSplitter->sizes(); - QList start = m_ui->mainSplitter->sizes(); - - QList end = start; - end[getIndexOfTab(getTabFromLayoutArea(LayoutArea::eDialogTab))] = 1; - - animation->setStartValue(QVariant::fromValue(start)); - animation->setEndValue(QVariant::fromValue(end)); - connect(animation, &QPropertyAnimation::finished, this, &LtsBaseLayoutWidget::hideWidgets); - connect(animation, &QPropertyAnimation::finished, this, [this]() { m_currentAnimation = nullptr; }); - - animation->start(QAbstractAnimation::DeleteWhenStopped); - m_isHidden = true; - m_currentAnimation = animation; -} - - -void LtsBaseLayoutWidget::retranslate() -{ - emit signalRetranslateActions(); - setExerciseData(m_exerciseData); - m_ui->retranslateUi(this); - -} - - -void LtsBaseLayoutWidget::changeEvent(QEvent *e) -{ - QWidget::changeEvent(e); - if (e->type() == QEvent::LanguageChange) - retranslate(); -} - - -int LtsBaseLayoutWidget::getIndexOfTab(LtsTabWidget *tab) -{ - int result = 0; - - result = m_ui->masterSplitter->indexOf(tab); - - if (result > -1) - return result; - - - - result = m_ui->mainSplitter->indexOf(tab); - - if (result > -1) - return result; - - - - result = m_ui->rightSplitter->indexOf(tab); - - if (result > -1) - return result; - - - qWarning() << "LtsBaseLayoutWidget::getIndexOfTab: tab not found in any splitter.."; - return result; -} - - -LtsToolBox* LtsBaseLayoutWidget::getToolBox(const ExercisePart &exercisePart) const -{ - switch (exercisePart) - { - case ExercisePart::eAfterAction: - return m_ui->naviActionsAfterAction; - case ExercisePart::eExecution: - return m_ui->naviActionsExecution; - case ExercisePart::ePlanning: - return m_ui->naviActionsPlanning; - case ExercisePart::eSpecial: - return m_ui->naviActionsSpecial; - } - Q_ASSERT(false); - return nullptr; -} - - -QPushButton* LtsBaseLayoutWidget::getExercisePartButton(const ExercisePart &exercisePart) const -{ - switch (exercisePart) - { - case ExercisePart::eAfterAction: - return m_ui->pbAfterAction; - case ExercisePart::eExecution: - return m_ui->pbExecution; - case ExercisePart::ePlanning: - return m_ui->pbPlanning; - case ExercisePart::eSpecial: - return m_ui->pbSpecial; - } - Q_ASSERT(false); - return nullptr; -} - - -void LtsBaseLayoutWidget::checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart) -{ - QWidget *tmpW = getToolBox(exercisePart)->findChild(menuGroup); - if (!tmpW) - return; - -#ifdef NEVER_PRINT_DEBUG - for (int i = 0; i < tmpW->children().size(); i++) - { - QObject * whatEver = tmpW->children().at( i ); - const QString & name = whatEver->objectName(); - qCritical() << "MenuGroup child " << i << " : " << name; - } -#endif - - - - - - if (tmpW->children().size() <= 1) - delete tmpW; - checkExercisePartButtonVisibility(); -} - - -void LtsBaseLayoutWidget::checkExercisePartButtonVisibility() -{ - if (nullptr == m_bg) - { - for (int ep = (int)ExercisePart::ePlanning; ep <= (int)ExercisePart::eSpecial; ++ep) - { - getExercisePartButton(static_cast(ep))->setVisible(false); - } - return; - } - - int visibleButtons = 0; - ExercisePart lastVisibleButton = ExercisePart::ePlanning; - for (int ep = (int) ExercisePart::ePlanning; ep <= (int) ExercisePart::eSpecial; ++ep) - { - bool visible = getToolBox((ExercisePart) ep)->children().count() > 1; - getExercisePartButton((ExercisePart) ep)->setVisible(visible); - if (visible) - { - ++visibleButtons; - lastVisibleButton = (ExercisePart) ep; - } - } - if (visibleButtons == 0) - lastVisibleButton = ExercisePart::eSpecial; - getExercisePartButton(lastVisibleButton)->setChecked(true); - getExercisePartButton(lastVisibleButton)->setVisible(visibleButtons > 1); -} - - -bool LtsBaseLayoutWidget::isWidgetsHidden() const -{ - return m_ui->detailWidget->isVisible(); -} - - -void LtsBaseLayoutWidget::showWidgets() -{ - m_ui->detailWidget->show(); -} - - -void LtsBaseLayoutWidget::hideWidgets() -{ - m_ui->detailWidget->hide(); -} - - -LtsDragIcon *LtsBaseLayoutWidget::createButton(const PluginData &plData) -{ - - LtsDragIcon *tmpBtn = new LtsDragIcon(nullptr); - tmpBtn->setObjectName(plData.identifier); - tmpBtn->setButtonStyle(LtsWidgets::ButtonStyle::eTextNextToIcon); - tmpBtn->setIcon(plData.menuIcon); - tmpBtn->setContext(plData.menuGroup); - tmpBtn->setText(plData.menuText); - if (!plData.tooltip.isEmpty()) - tmpBtn->setToolTip(plData.tooltip); - tmpBtn->setDropHandlerKey(plData.dropHandlerKey); - tmpBtn->setIdentifier(plData.identifier); - - connect(tmpBtn, &QWidget::destroyed, - this, []() - { - qDebug() << "LtsDragIcon was destroyed"; - }); - - connect(tmpBtn, &LtsDragIcon::clicked, this, &LtsBaseLayoutWidget::onMenuEntryClicked); - return tmpBtn; -} - - -LtsToolBarArea * LtsBaseLayoutWidget::getToolbar() const -{ - return m_ui->toolBar; -} - - -QWidget *LtsBaseLayoutWidget::getMenu() const -{ - return m_ui->naviWidget; -} - - -LtsTabWidget *LtsBaseLayoutWidget::getTabFromLayoutArea(LayoutArea area) const -{ - switch (area) - { - case LayoutArea::eDialogTab: - return m_ui->detailWidget; - case LayoutArea::eInfoTab: - return m_ui->infoWidget; - case LayoutArea::eSpecialTab: - return m_ui->specialWidget; - case LayoutArea::eMainTab: - return m_ui->mainWidget; - case LayoutArea::eUnknown: - return m_ui->detailWidget; - } - return nullptr; -} - - -void LtsBaseLayoutWidget::showNextQueuedMessage() -{ - if (m_messageQueue.isEmpty()) - { - return; - } - - const Message m = m_messageQueue.takeLast(); - - showMessage(m.title, m.text, m.type, m.clickedLinkCallback, m.closeOnLinkClicked, m.identifier); -} - - -void LtsBaseLayoutWidget::removeQueuedMessage(const QString &messageIdentifier) -{ - if (messageIdentifier.isEmpty()) - { - return; - } - - m_messageQueue.erase(std::remove_if(m_messageQueue.begin(), m_messageQueue.end(), [messageIdentifier](const Message &m) - { - return (m.identifier == messageIdentifier); - }), m_messageQueue.end()); - - if (!m_currentMessage.isEmpty()) - { - if (m_currentMessage.first().identifier == messageIdentifier) - { - - m_currentMessage.clear(); - m_notiBox->hide(); - showNextQueuedMessage(); - } - } -} - - -void LtsBaseLayoutWidget::showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type, const std::function &clickedLinkCallback, bool closeOnLinkClicked, const QString &identifier) -{ - if (m_notiBox == nullptr) - { - m_notiBox = new LtsNotificationBox; - - connect(m_notiBox, &LtsNotificationBox::signalLinkClicked, - this, [this, closeOnLinkClicked, clickedLinkCallback](const QString &link) - { - - - auto *event = new LtsLinkEvent(QUrl(link), this); - event->setAccepted(false); - QApplication::postEvent(qApp, event); - - if (clickedLinkCallback) - clickedLinkCallback(link); - - if (closeOnLinkClicked) - { - m_notiBox->hide(); - m_currentMessage.clear(); - showNextQueuedMessage(); - } - }); - - connect(m_notiBox, &QObject::destroyed, - this, [this]() - { - if (qobject_cast(sender()) == m_notiBox) - m_notiBox = nullptr; - }); - - connect(m_notiBox, &LtsNotificationBox::signalClose, - this, [this]() - { - m_notiBox->hide(); - m_currentMessage.clear(); - showNextQueuedMessage(); - }); - - connect(m_notiBox, &LtsNotificationBox::signalAck, - this, [this]() - { - auto *event = new LtsLinkEvent(QUrl(QStringLiteral("lts://NotificationAck/%1").arg(m_currentMessage.first().identifier)), this); - event->setAccepted(false); - QApplication::postEvent(qApp, event); - - m_notiBox->hide(); - m_currentMessage.clear(); - showNextQueuedMessage(); - }); - - m_notiBox->setParent(this); - m_notiBox->setGeometry(0, 0, m_notiBox->width(), m_notiBox->height()); - } - - - m_messageQueue.erase(std::remove_if(m_messageQueue.begin(), m_messageQueue.end(), [identifier](const Message &m) - { - return (!identifier.isEmpty() && m.identifier == identifier); - }), m_messageQueue.end()); - - if (!m_currentMessage.isEmpty()) - { - const auto m = m_currentMessage.takeFirst(); - if (identifier.isEmpty() || m.identifier != identifier) - { - m_messageQueue += m; - } - } - - m_currentMessage += Message({ title, text, type, clickedLinkCallback, closeOnLinkClicked, identifier }); - - m_notiBox->message(title, text, type); - - - - auto *animation = new QPropertyAnimation(m_notiBox, "geometry"); - connect(animation, &QPropertyAnimation::finished, animation, &QObject::deleteLater); - animation->setDuration(700); - animation->setStartValue(QRect(width() + 10, height() + 10, m_notiBox->width(), m_notiBox->height())); - animation->setEndValue(QRect(width() - m_notiBox->width() - 15, height() - m_notiBox->height() - 15, m_notiBox->width(), m_notiBox->height())); - animation->setEasingCurve(QEasingCurve::OutElastic); - animation->start(QAbstractAnimation::DeleteWhenStopped); -} - - -bool LtsBaseLayoutWidget::messageIdentifierExists(const QString &identifier) const -{ - if (identifier.isEmpty()) - { - return false; - } - - if (!m_currentMessage.isEmpty() && m_currentMessage.first().identifier == identifier) - { - return true; - } - - for (const Message &m : qAsConst(m_messageQueue)) - { - if (m.identifier == identifier) - { - return true; - } - } - - return false; -} - -} - - - - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_base_layout_widget.h b/Lts/Common/LtsWidgets/lts_base_layout_widget.h deleted file mode 100755 index 937e3d307..000000000 --- a/Lts/Common/LtsWidgets/lts_base_layout_widget.h +++ /dev/null @@ -1,346 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" -#include "lts_notification_box.h" -#include "qevent.h" - -#include -#include -#include -#include - - -class QButtonGroup; -class QPropertyAnimation; -class QPushButton; -class QTimer; -class QToolButton; - -namespace Ui -{ -class LtsBaseLayoutWidget; -} - -namespace IDL -{ -enum class PartitionType; -} - -namespace LtsUtils { class Promise; } - -namespace LtsWidgets -{ -class LtsDragIcon; -class LtsProgressDialogHelper; -class LtsTabWidget; -class LtsToolBarArea; -class LtsToolBox; - -struct ExerciseData -{ - QString exName; - QString exDescription; - int phId = -1; - QString phName; - QString phDescription; - QString exStateString; - ExcState exState = ExerciseState::eUnknown; - QColor exStateCol; - QString partitionType; - - bool operator==(const ExerciseData &o) const - { - return exName == o.exName && - exDescription == o.exDescription && - phId == o.phId && - phName == o.phName && - phDescription == o.phDescription && - exStateString == o.exStateString && - exState == o.exState && - exStateCol == o.exStateCol && - partitionType == o.partitionType; - } - bool operator!=(const ExerciseData &o) const - { - return !operator ==(o); - } - -}; - - - - -class LTSWIDGETS_EXPORT LtsBaseLayoutWidget : public QWidget -{ - Q_OBJECT - -public: - - explicit LtsBaseLayoutWidget(QWidget *parent = nullptr, bool splitMenu = true); - ~LtsBaseLayoutWidget(); - - - - void hideVisibleLayouts(); - - - - void resetLayoutVisibility(); - - - - void setMenuVisible(bool visible); - - - - bool isMenuVisible() const; - - bool isTopBarVisible() const; - void setTopBarVisible(bool visible); - - - - bool isMenuCollapsed() const; - - - - void collapseMenu(bool collapse = true); - - - - void setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId); - - - - LtsDragIcon *addMenuAction(const PluginData &plData, bool isFirstRun = false); - - - - bool removeMenuAction(const PluginData &plData); - - - - bool removeMenuAction(LtsDragIcon *icon); - - bool removeMenuAction(const QString &byIdentifier); - - - - void clearMenuActions(); - - void setAppName(const QString &appName); - bool isDialogFocused(const LtsDialog *dialog) const; - - - - void setCustomText(const QString &text); - const QString customText() const; - - - - LtsTabWidget *getTabFromLayoutArea(LayoutArea area) const; - - - - LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown); - - - - QWidget *getMenu() const; - - - - LtsToolBarArea *getToolbar() const; - - - - void setOperatorName(const QString &operatorName, bool showTerminalButton); - - - - void setProfilePicture(const QPixmap &profilePicture); - - - - void setExerciseData(const ExerciseData &data); - - - - void initilizationFinished(); - - - - void addNotification(QToolButton *noti); - - - - bool isWidgetsHidden() const; - - bool notificationExists(const QString &identifier) const; - const QMap &mapButtons() const { return m_mapButtons; } - void setToolBarActions(const QStringList &actions); - QStringList toolBarActions() const; - void setFunctionKeys(const QMap &actions); - QMap functionKeys() const; - void setWorkspaceId(const QString &workspaceId) { m_workspaceId = workspaceId; } - - - - void showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type = LtsNotificationBox::MessageType::Info, const std::function &clickedLinkCallback = nullptr, bool closeOnLinkClicked = true, const QString &identifier = {}); - bool messageIdentifierExists(const QString &identifier) const; - - void executeAction(const QString &actionIdentifier); - bool isSecondaryWidget() const { return m_isSecondaryWidget; } - void setSecondaryWidget() { m_isSecondaryWidget = true; } - - - - void removeQueuedMessage(const QString &messageIdentifier); - - -public slots: - bool slotClose(); - void slotLogout(); - - void slotScreenCountChanged(int count); - - - - void onMenuEntryClicked(); - - void onHideAnimated(); - void onReShowAnimated(); - - - - bool addWidget(LtsWidgets::LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true); - - - - bool addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true ); - - bool focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area); - - - - void executeCreateInstantFunctions(const LtsWidgets::PluginData &plData); - - void setExerciseTime(qint64 mSecsSinceEpoch); - bool haveUnsavedChanges(); - - - - - - bool notifyUnsavedChanges(); - - - - void setIrreplaceableOnce() { m_irreplaceableOnce = true; } - bool irreplaceableReset() { bool r = m_irreplaceableOnce; m_irreplaceableOnce = false; return r; } - bool isIrreplaceable() const { return m_irreplaceableOnce; } - - - - void showWidgets(); - - - - void hideWidgets(); - - -private slots: - - - void slotOperatorMenuClicked(); - - void setCurrentTime(); - - -signals: - - - - - void signalClose(); - void signalLogout(); - void signalShowOperatorSettings(); - void signalRetranslateActions(); - void signalSwap(); - void signalStapleTopicLockHandler(LtsWidgets::LtsDialog *widget); - void signalSuccessfullyPlaced(); - - - - void signalNotifyUnsavedChanges(bool &reject); - -protected: - - - void closeEvent(QCloseEvent *event) override; - - void showEvent(QShowEvent *event) override; - void changeEvent(QEvent *e) override; - - -private: - void addDebugMenu(); - void retranslate(); - void addTouchScroll(QWidget *widget); - LtsDragIcon *createButton(const PluginData &plData); - int getIndexOfTab(LtsTabWidget *tab); - LtsToolBox *getToolBox(const ExercisePart &exercisePart) const; - QPushButton *getExercisePartButton(const ExercisePart &exercisePart) const; - void checkExercisePartButtonVisibility(); - void checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart); - bool isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const; - bool isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const; - void applyExerciseStateRestricition(); - void executePluginData(const PluginData &plData); - void showNextQueuedMessage(); - - - struct Message - { - public: - QString title; - QString text; - LtsNotificationBox::MessageType type; - std::function clickedLinkCallback; - bool closeOnLinkClicked; - QString identifier; - }; - - ExerciseData m_exerciseData; - QMap m_mapButtons; - QList m_mainSplitterSizes; - QList m_menuSplitterSizes; - bool m_isHidden; - QPropertyAnimation *m_currentAnimation{nullptr}; - Ui::LtsBaseLayoutWidget *m_ui{nullptr}; - IDL::PartitionType m_partitionType; - int m_domainId; - int m_partitionId; - QString m_operatorName; - bool m_firstShow; - bool m_showTerminalButton; - qint64 m_exerciseTime; - QTimer *m_setCurrentTimeTimer{nullptr}; - bool m_irreplaceableOnce; - QButtonGroup *m_bg{nullptr}; - QString m_workspaceId; - int m_screenCount; - bool m_menuCollapsed; - QMap, bool> m_oldLayoutVisibilityStates; - LtsNotificationBox *m_notiBox{nullptr}; - QStringList m_notFoundYet; - bool m_isSecondaryWidget { false }; - QVector m_currentMessage; - QVector m_messageQueue; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp b/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp deleted file mode 100755 index be35119b8..000000000 --- a/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp +++ /dev/null @@ -1,129 +0,0 @@ - - - -#include "lts_bitfield_widget.h" - -#include - -namespace LtsWidgets -{ - -LtsBitfieldWidget::LtsBitfieldWidget(QWidget *parent, quint8 value) - : QWidget(parent) - , m_value(value) -{ - setObjectName(QString::fromUtf8("LtsBitfieldWidget")); -} - -void LtsBitfieldWidget::paintEvent(QPaintEvent *pe) -{ - QWidget::paintEvent(pe); - - QPainter painter; - painter.begin(this); - painter.setPen(Qt::SolidLine); - - if (m_hasCaption) - { - QFont font = painter.font(); - font.setPointSizeF( (height()/2.0 > width()/8.0) ? width()/8.0 : height()/2.0 ); - painter.setFont(font); - } - - for (int i = 0; i < 8; ++i) - { - QRectF bitGraphic; - - bitGraphic.setLeft(width() / 8.0 * i); - bitGraphic.setWidth((width() / 8.0) - 1); - bitGraphic.setHeight(height() - 1); - - bool bitVal = (m_value & (1 << (7 - i))) > 0; - - painter.fillRect(bitGraphic, QBrush(bitVal ? m_onColor : m_offColor, Qt::SolidPattern)); - - if (m_hasCaption) - { - painter.setPen(bitVal ? m_offColor : m_onColor); - painter.drawText(bitGraphic, Qt::AlignCenter, bitVal ? QStringLiteral("1") : QStringLiteral("0")); - } - - painter.setPen(Qt::gray); - painter.drawRect(bitGraphic); - } - - painter.end(); -} - -void LtsBitfieldWidget::setHasCaption(bool hasCaption) -{ - if (m_hasCaption == hasCaption) - return; - - m_hasCaption = hasCaption; - repaint(); - - emit hasCaptionChanged(hasCaption); -} - -void LtsBitfieldWidget::setOnColor(const QColor &onColor) -{ - if (m_onColor == onColor) - return; - - m_onColor = onColor; - repaint(); -} - -void LtsBitfieldWidget::setOffColor(const QColor &offColor) -{ - if (m_offColor == offColor) - return; - - m_offColor = offColor; - repaint(); -} - -void LtsBitfieldWidget::mousePressEvent(QMouseEvent *event) -{ - if (!m_isEditable) - { - QWidget::mousePressEvent(event); - return; - } - - for (int i = 0; i < 8; ++i) - { - if (event->pos().x() > width()/8 * i && - event->pos().x() < width()/8 * (i + 1)) - { - m_value ^= (1 << (7 - i)); - emit valueChanged(m_value); - break; - } - } - repaint(); -} - -void LtsBitfieldWidget::setEditable(bool editable) -{ - if (m_isEditable == editable) - return; - - m_isEditable = editable; - - emit isEditableChanged(m_isEditable); -} - -void LtsBitfieldWidget::setValue(quint8 value) -{ - if (m_value == value) - return; - - m_value = value; - repaint(); - - emit valueChanged(m_value); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_bitfield_widget.h b/Lts/Common/LtsWidgets/lts_bitfield_widget.h deleted file mode 100755 index 889fb79cf..000000000 --- a/Lts/Common/LtsWidgets/lts_bitfield_widget.h +++ /dev/null @@ -1,95 +0,0 @@ - - - -#pragma once - -#include -#include - -#include "lts_widgets_export.h" - - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsBitfieldWidget : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(quint8 value READ value WRITE setValue NOTIFY valueChanged) - Q_PROPERTY(bool isEditable READ isEditable WRITE setEditable NOTIFY isEditableChanged) - Q_PROPERTY(bool hasCaption READ hasCaption WRITE setHasCaption NOTIFY hasCaptionChanged) - Q_PROPERTY(QColor onColor READ onColor WRITE setOnColor) - Q_PROPERTY(QColor offColor READ offColor WRITE setOffColor) - -public: - explicit LtsBitfieldWidget(QWidget *parent = nullptr, quint8 value = 0); - -signals: - void valueChanged(quint8 newValue); - void isEditableChanged(bool option); - void hasCaptionChanged(bool option); - -public slots: - - - void setValue(quint8 value); - - - - void setEditable(bool editable); - - - - void setHasCaption(bool hasCaption); - - - - void setOnColor(const QColor &onColor); - - - - void setOffColor(const QColor &offColor); - - -public: - - - quint8 value() const { return m_value; } - - - - bool isEditable() const { return m_isEditable; } - - - - bool hasCaption() const { return m_hasCaption; } - - - - QColor onColor() const {return m_onColor;} - - - - QColor offColor() const {return m_offColor;} - -private: - - - void mousePressEvent(QMouseEvent *event) override; - - - - void paintEvent(QPaintEvent *pe) override; - -private: - quint8 m_value = 0; - - bool m_isEditable = true; - bool m_hasCaption = true; - - QColor m_onColor = Qt::black; - QColor m_offColor = Qt::white; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_checkable_table_model.h b/Lts/Common/LtsWidgets/lts_checkable_table_model.h deleted file mode 100755 index 93d22ecf4..000000000 --- a/Lts/Common/LtsWidgets/lts_checkable_table_model.h +++ /dev/null @@ -1,131 +0,0 @@ - - - -#pragma once - -#include "lts_abstract_table_model.h" - - -namespace LtsWidgets -{ - -template -class LtsCheckableTableModel : public LtsAbstractTableModel -{ -public: - LtsCheckableTableModel(const QVector &header, int checkableColumn, QObject *parent) - : LtsAbstractTableModel(header, parent) - , m_checkableColumn(checkableColumn) - { - } - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override - { - if (role == Qt::CheckStateRole && index.column() == m_checkableColumn && LtsAbstractTableModel::indexIsValid(index)) - return LtsAbstractTableModel::m_vecData.at(index.row()).first ? Qt::Checked : Qt::Unchecked; - return QVariant(); - } - - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override - { - if (role == Qt::CheckStateRole) - { - switchCheckbox(index); - return true; - } - return QAbstractItemModel::setData(index, value, role); - } - - Qt::ItemFlags flags(const QModelIndex &index) const override - { - Qt::ItemFlags flags(LtsWidgets::LtsAbstractTableModel::flags(index) & ~Qt::ItemIsSelectable); - if (index.column() == m_checkableColumn) - return flags.setFlag(Qt::ItemIsUserCheckable); - - return flags; - } - - QVector getCheckedIds() const - { - QVector ids; - - for (int row = 0; row < LtsAbstractTableModel::rowCount(); ++row) - { - const T &entry = LtsAbstractTableModel::m_vecData.at(row); - if (entry.first) - ids << entry.second.id(); - } - - return ids; - } - - QVector getUncheckedIds() const - { - QVector ids; - - for (int row = 0; row < LtsAbstractTableModel::rowCount(); ++row) - { - const T &entry = LtsAbstractTableModel::m_vecData.at(row); - if (!entry.first) - ids << entry.second.id(); - } - - return ids; - } - - bool setChecked(const QVector &vecCheckedIds) - { - for (int row = 0; row < LtsAbstractTableModel::rowCount(); ++row) - { - T &entry = LtsAbstractTableModel::m_vecData[row]; - bool bContains = vecCheckedIds.contains(entry.second.id()); - if (entry.first != bContains) - { - entry.first = bContains; - const QModelIndex idx(LtsAbstractTableModel::index(row, m_checkableColumn, QModelIndex())); - emit LtsAbstractTableModel::dataChanged(idx, idx, {Qt::CheckStateRole}); - } - } - - return true; - } - bool isFiltered(const T &data, DataModification modType) const override - { - Q_UNUSED(data); - Q_UNUSED(modType); - return false; - } - -public slots: - void switchCheckbox(const QModelIndex &index) - { - auto entry = LtsAbstractTableModel::getRowData(index.row()); - entry.first = !entry.first; - LtsAbstractTableModel::setRowData(index.row(), entry); - } - - void selectAll() - { - for (int row = 0; row < LtsAbstractTableModel::rowCount(); ++row) - { - T entry = LtsAbstractTableModel::m_vecData.at(row); - entry.first = true; - LtsAbstractTableModel::setRowData(row, entry); - } - } - - void unselectAll() - { - for (int row = 0; row < LtsAbstractTableModel::rowCount(); ++row) - { - T entry = LtsAbstractTableModel::m_vecData.at(row); - entry.first = false; - LtsAbstractTableModel::setRowData(row, entry); - } - } - -private: - int m_checkableColumn; - -}; -} diff --git a/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp b/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp deleted file mode 100755 index 2a9efa15b..000000000 --- a/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp +++ /dev/null @@ -1,156 +0,0 @@ - - - -#include "lts_column_filter_proxy_model.h" - -#include - -#include - - -namespace LtsWidgets -{ -LtsColumnFilterProxyModel::LtsColumnFilterProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - setSortCaseSensitivity(Qt::CaseInsensitive); -} - -bool LtsColumnFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - return LtsUtils::lessThan(sourceModel()->data(left), sourceModel()->data(right)); -} - -void LtsColumnFilterProxyModel::onSetColumnStringFilter(const QString &newFilterString, int column) -{ - setColumnStringFilter(newFilterString, column); -} - -void LtsColumnFilterProxyModel::onSetColumnDataFilter(const QVariant &newFilterData, int column) -{ - setColumnDataFilter(newFilterData, column); -} - -void LtsColumnFilterProxyModel::setColumnStringFilter(const QString &newFilterString, int column) -{ - if (column < 0) - return; - auto it = m_columnStringFilters.find(column); - if (newFilterString.isEmpty()) - { - if (it == m_columnStringFilters.end()) - return; - m_columnStringFilters.erase(it); - } - else - { - auto rx = QRegularExpression(newFilterString, - QRegularExpression::CaseInsensitiveOption); - if (it == m_columnStringFilters.end()) - { - m_columnStringFilters.insert(column, rx); - } - else - { - if (rx == it.value()) - return; - it.value() = rx; - } - } - invalidateFilter(); -} - -void LtsColumnFilterProxyModel::setColumnDataFilter(const QVariant &newFilterData, int column) -{ - if (column < 0) - return; - auto it = m_columnDataFilters.find(column); - if (newFilterData.isNull()) - { - if (it == m_columnDataFilters.end()) - return; - m_columnDataFilters.erase(it); - } - else - { - if (it == m_columnDataFilters.end()) - { - m_columnDataFilters[column] = newFilterData; - } - else - { - if (newFilterData == it.value()) - return; - it.value() = newFilterData; - } - } - invalidateFilter(); -} - - -bool LtsColumnFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const -{ - - const auto sm = sourceModel(); - bool bRet = true; - for (auto it = m_columnDataFilters.begin(); bRet && it != m_columnDataFilters.end(); ++it) - { - const QModelIndex idx = sm->index(source_row, it.key(), source_parent); - const QVariant _data = sm->data(idx, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole).toString(); - bRet = _data == it.value(); - if (!bRet) - break; - } - - for (auto it = m_columnStringFilters.begin(); bRet && it != m_columnStringFilters.end(); ++it) - { - const QRegularExpression &rx = it.value(); - if (!rx.isValid()) - continue; - const QModelIndex idx = sm->index(source_row, it.key(), source_parent); - const QString _data = sm->data(idx, Qt::DisplayRole).toString(); - bRet = _data.contains(rx); - if (!bRet) - break; - } - -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - if (!bRet && m_bRecursiveFiltering) - { - const QModelIndex idx = sm->index(source_row, 0, source_parent); - if (checkIfOneChildIsVisible(idx)) - return true; - } -#endif - - return bRet; -} - -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) -void LtsColumnFilterProxyModel::setRecursiveFilteringEnabled(bool recursive) -{ - if (m_bRecursiveFiltering == recursive) - return; - m_bRecursiveFiltering = recursive; - invalidateFilter(); -} - -bool LtsColumnFilterProxyModel::recursiveFilteringEnabled() const -{ - return m_bRecursiveFiltering; -} - -bool LtsColumnFilterProxyModel::checkIfOneChildIsVisible(const QModelIndex &parent) const -{ - const auto sm = sourceModel(); - const int childCount = sm->rowCount(parent); - for (int i = 0; i < childCount; ++i) - { - if (filterAcceptsRow(i, parent)) - return true; - } - return false; -} -#endif - -} diff --git a/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h b/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h deleted file mode 100755 index 90f841dc4..000000000 --- a/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h +++ /dev/null @@ -1,122 +0,0 @@ - - - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -struct ComboboxFilterData -{ - QString text; - QVariant data; - QIcon icon; - - inline bool operator<(const ComboboxFilterData &cfd) - { - return LtsUtils::lessThan(data, cfd.data); - } - inline friend QDataStream &operator<<(QDataStream &ds, const ComboboxFilterData &cfd) - { - ds << cfd.text << cfd.data << cfd.icon; - return ds; - } - inline friend QDataStream &operator>>(QDataStream &ds, ComboboxFilterData &cfd) - { - ds >> cfd.text >> cfd.data >> cfd.icon; - return ds; - } -}; - - -struct ComboboxFilterContent -{ - QVector data; - bool sortItems = false; - bool isValid() const { return m_isValid; } - - QVariant toVariant() const - { - if (!isValid()) - return QVariant(); - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - ds << (*this); - return ba; - } - - static ComboboxFilterContent fromVariant(const QVariant &v) - { - ComboboxFilterContent ret; - QDataStream ds(v.toByteArray()); - ds.startTransaction(); - ds >> ret; - ds.commitTransaction(); - ret.m_isValid = ds.status() == QDataStream::Ok; - - return ret; - } - - inline friend QDataStream &operator<<(QDataStream &ds, const ComboboxFilterContent &cfc) - { - ds << cfc.data << cfc.sortItems; - return ds; - } - inline friend QDataStream &operator>>(QDataStream &ds, ComboboxFilterContent &cfc) - { - ds >> cfc.data >> cfc.sortItems; - return ds; - } - -private: - bool m_isValid = true; -}; - - -class LTSWIDGETS_EXPORT LtsColumnFilterProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - static constexpr int ComboboxFilterRole = Qt::UserRole + 17; - - explicit LtsColumnFilterProxyModel(QObject *parent = nullptr); - -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - void setRecursiveFilteringEnabled(bool recursive); - bool recursiveFilteringEnabled() const; -#endif - - const QHash &columnStringFilters() const { return m_columnStringFilters; } - const QHash &columnDataFilters() const { return m_columnDataFilters; } - - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - - virtual void setColumnStringFilter(const QString &newFilterString, int column); - virtual void setColumnDataFilter(const QVariant &newFilterData, int column); - -public slots: - void onSetColumnStringFilter(const QString &newFilterString, int column); - void onSetColumnDataFilter(const QVariant &newFilterData, int column); - -protected: - virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - bool checkIfOneChildIsVisible(const QModelIndex &parent) const; -#endif -private: - QHash m_columnStringFilters; - QHash m_columnDataFilters; -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - bool m_bRecursiveFiltering = false; -#endif -}; -} diff --git a/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui b/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui deleted file mode 100755 index e51415e80..000000000 --- a/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - LtsColumnOrganiserWidget - - - - 0 - 0 - 419 - 402 - - - - Form - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - - - - Qt::Horizontal - - - - 178 - 20 - - - - - - - - - - Reset - - - - - - - Apply - - - - - - - Cancel - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_combo_box.cpp b/Lts/Common/LtsWidgets/lts_combo_box.cpp deleted file mode 100755 index ccbd3bea6..000000000 --- a/Lts/Common/LtsWidgets/lts_combo_box.cpp +++ /dev/null @@ -1,118 +0,0 @@ - - - -#include "lts_combo_box.h" -#include "lts_selection_popup.h" -#include "lts_message_dialog.h" - -#include -#include - - -#ifdef NEVER -# include -#endif - -namespace LtsWidgets -{ - -bool LtsComboBox::s_useSelectionDialog = false; - -LtsComboBox::LtsComboBox(QWidget *parent) - : QComboBox(parent) - , m_currentIndex(-1) -{ - connect(this, &LtsComboBox::signalExternallyChanged, - this, &LtsComboBox::signalRefill); -} - -void LtsComboBox::setUseSelectionDialog(bool bUseSelectionDialog) -{ - s_useSelectionDialog = bUseSelectionDialog; -} - -void LtsComboBox::showEvent(QShowEvent *e) -{ - QComboBox::showEvent(e); - - m_currentIndex = currentIndex(); -} - -void LtsComboBox::enterEvent(QEvent *e) -{ - BaseClass::enterEvent(e); - emit signalEnterEvent(); -} - -void LtsComboBox::leaveEvent(QEvent *e) -{ - BaseClass::leaveEvent(e); - emit signalLeaveEvent(); -} - -void LtsComboBox::showPopup() -{ - bool bUseSelectionDialog = s_useSelectionDialog; - if (QGuiApplication::queryKeyboardModifiers() == Qt::ControlModifier) - bUseSelectionDialog = !bUseSelectionDialog; - if (!bUseSelectionDialog) - { - QComboBox::showPopup(); - return; - } - - if (!model()->rowCount()) - { - LtsWidgets::LtsMessageDialog::showMessage(this, tr("There are no items available!")); - return; - } - - - LtsSelectionPopup* popup = new LtsSelectionPopup(model(), currentIndex(), this); - popup->setAttribute(Qt::WA_DeleteOnClose); - -#ifdef NEVER - - - - - - - - connect(popup, static_cast(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){ - qCritical() << " LtsComboBox: called: currentIndexChanged(int), idx=" << idx; - }); - - - connect(popup, static_cast(&LtsSelectionPopup::activated), this, [](int idx){ - qCritical() << " LtsComboBox: called: activated(int), idx=" << idx; - }); - -#endif - - - - connect(popup, &LtsSelectionPopup::currentIndexChanged, - this, &LtsComboBox::userChangedIndex); - connect(popup, &LtsSelectionPopup::activated, - this, static_cast(&LtsComboBox::activated)); - - popup->show(); -} - - -void LtsComboBox::userChangedIndex(int index) -{ - m_currentIndex = index; - setCurrentIndex(index); -} - - -bool LtsComboBox::hasValueChanged() const -{ - if (m_currentIndex == -1) - return false; - return !(currentIndex() == m_currentIndex); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_combo_box.h b/Lts/Common/LtsWidgets/lts_combo_box.h deleted file mode 100755 index 5264ce5c4..000000000 --- a/Lts/Common/LtsWidgets/lts_combo_box.h +++ /dev/null @@ -1,59 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsComboBox : public QComboBox -{ - Q_OBJECT - using BaseClass = QComboBox; - -public: - explicit LtsComboBox(QWidget *parent = nullptr); - ~LtsComboBox() = default; - - - - static void setUseSelectionDialog(bool bUseSelectionDialog); - - - - bool hasValueChanged() const; - - void setExternalContentType(int ect) { m_externalContentType = ect; } - int externalContentType() const { return m_externalContentType; } - -protected: - - - void showPopup() override; - - void showEvent(QShowEvent *e) override; - void enterEvent(QEvent *e) override; - void leaveEvent(QEvent *) override; - -protected slots: - void userChangedIndex(int index); - -signals: - void signalExternallyChanged(); - void signalRefill(); - void signalEnterEvent(); - void signalLeaveEvent(); - -private: - int m_currentIndex; - int m_externalContentType; - static bool s_useSelectionDialog; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit.cpp b/Lts/Common/LtsWidgets/lts_date_time_edit.cpp deleted file mode 100755 index f2a316504..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit.cpp +++ /dev/null @@ -1,1785 +0,0 @@ - - - -#include "lts_date_time_edit.h" -#include "lts_date_time_edit_p.h" - -#include "ui_lts_date_time_edit_touch.h" - - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr qint64 s_oneSecondMSec = 1000LL; -static constexpr qint64 s_oneMinuteMSec = 60 * s_oneSecondMSec; -static constexpr qint64 s_oneHourMSec = 60 * s_oneMinuteMSec; -static constexpr qint64 s_oneDayMSec = 24 * s_oneHourMSec; - -namespace Ui -{ -class LtsDateTimeEditLe -{ -public: - LtsWidgets::DigitsLineEdit *leLongDay = nullptr; - LtsWidgets::DigitsLineEdit *leLongHHMMSS = nullptr; - LtsWidgets::LineEdit *leLongTZ = nullptr; - LtsWidgets::DigitsLineEdit *leLongMonth = nullptr; - LtsWidgets::DigitsLineEdit *leLongYear = nullptr; - QCheckBox *checkBox = nullptr; - QGridLayout *gridLayout = nullptr; - QPushButton *pbBBB = nullptr; - QPushButton *pbBB = nullptr; - QPushButton *pbB = nullptr; - QPushButton *pbF = nullptr; - QPushButton *pbFF = nullptr; - QPushButton *pbFFF = nullptr; - QPushButton *pbNow = nullptr; - QSlider *slTime = nullptr; - - static void setFixedSize(QLineEdit *le, int charCount) - { - QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(false); - - const QFontMetricsF fm(le->font()); - const auto textMargins = le->textMargins(); - le->setSizePolicy(sizePolicy); - charCount += 3; -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - const qreal w = fm.width(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); -#else - const qreal w = fm.horizontalAdvance(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); -#endif - le->setMinimumWidth(w); - le->setMaximumWidth(w); - } - - void setupUi(QWidget *parent) - { - gridLayout = new QGridLayout(parent); - gridLayout->setMargin(0); - - int iStart = 3; - - checkBox = new QCheckBox; - gridLayout->addWidget(checkBox, 0, iStart++, 1, 1); - - auto horizontalLayout = new QHBoxLayout; - horizontalLayout->setSpacing(0); - horizontalLayout->setContentsMargins(0, 0, 0, 0); - - leLongDay = new LtsWidgets::DigitsLineEdit(parent); - setFixedSize(leLongDay, 2); - leLongDay->setObjectName(QStringLiteral("leLongDay")); - horizontalLayout->addWidget(leLongDay); - - leLongHHMMSS = new LtsWidgets::DigitsLineEdit(parent); - leLongHHMMSS->setExactNumDigits(6); - setFixedSize(leLongHHMMSS, 6); - leLongHHMMSS->setObjectName(QStringLiteral("leLongHHMMSS")); - horizontalLayout->addWidget(leLongHHMMSS); - - leLongTZ = new LtsWidgets::LineEdit(parent); - leLongTZ->setAlignment(Qt::AlignCenter); - setFixedSize(leLongTZ, 1); - leLongTZ->setObjectName(QStringLiteral("leLongTZ")); - horizontalLayout->addWidget(leLongTZ); - - leLongMonth = new LtsWidgets::DigitsLineEdit(parent); - leLongMonth->setAlignment(Qt::AlignCenter); - setFixedSize(leLongMonth, 3); - leLongMonth->setObjectName(QStringLiteral("leLongMonth")); - horizontalLayout->addWidget(leLongMonth); - - leLongYear = new LtsWidgets::DigitsLineEdit(parent); - leLongYear->setExactNumDigits(2); - setFixedSize(leLongYear, 2); - leLongYear->setObjectName(QStringLiteral("leLongYear")); - horizontalLayout->addWidget(leLongYear); - - gridLayout->addLayout(horizontalLayout, 0, iStart++, 1, 1); - - pbNow = new QPushButton(QApplication::translate("LtsDateTimeEdit", "Now", nullptr)); - pbNow->setObjectName(QStringLiteral("pbNow")); - gridLayout->addWidget(pbNow, 0, iStart++, 1, 1); - - QWidget::setTabOrder(checkBox, leLongDay); - QWidget::setTabOrder(leLongDay, leLongHHMMSS); - QWidget::setTabOrder(leLongHHMMSS, leLongTZ); - QWidget::setTabOrder(leLongTZ, leLongMonth); - QWidget::setTabOrder(leLongMonth, leLongYear); - QWidget::setTabOrder(leLongYear, pbNow); - - parent->setFocusProxy(leLongDay); - } - - static QPushButton *addButton(const QString &text, QWidget *parent) - { - QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - auto pb = new QPushButton(parent); - pb->setMinimumWidth(27); - pb->setMaximumWidth(27); - pb->setText(text); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pb->sizePolicy().hasHeightForWidth()); - pb->setSizePolicy(sizePolicy); - return pb; - } - - void addButtons(QWidget *parent) - { - pbBBB = addButton(QLatin1String("<<<"), parent); - pbBB = addButton(QLatin1String("<<"), parent); - pbB = addButton(QLatin1String("<"), parent); - gridLayout->addWidget(pbBBB, 0, 0, 1, 1); - gridLayout->addWidget(pbBB, 0, 1, 1, 1); - gridLayout->addWidget(pbB, 0, 2, 1, 1); - - int iStart = 5; - pbF = addButton(QLatin1String(">"), parent); - pbFF = addButton(QLatin1String(">>"), parent); - pbFFF = addButton(QLatin1String(">>>"), parent); - gridLayout->addWidget(pbF, 0, iStart++, 1, 1); - gridLayout->addWidget(pbFF, 0, iStart++, 1, 1); - gridLayout->addWidget(pbFFF, 0, iStart++, 1, 1); - } - - void addSlider(QWidget *parent) - { - slTime = new QSlider(parent); - slTime->setOrientation(Qt::Horizontal); - gridLayout->addWidget(slTime, 1, 0, 1, 7); - } -}; -} - - -namespace LtsWidgets -{ - - - - -void setItemText(LtsWidgets::LineEdit *lineEdit, const QString &text) -{ - if (!lineEdit) - return; - lineEdit->setText(text); -} -void setItemText(QComboBox *comboxBox, const QString &text) -{ - if (!comboxBox) - return; - comboxBox->setCurrentIndex(comboxBox->findText(text)); -} -void setItemData(QComboBox *comboxBox, int data) -{ - if (!comboxBox) - return; - comboxBox->setCurrentIndex(comboxBox->findData(data)); -} -void setItemValue(QSpinBox *spinBox, int value) -{ - if (!spinBox) - return; - spinBox->setValue(value); -} - - -QString getItemText(LtsWidgets::LineEdit *lineEdit) -{ - if (!lineEdit) - return {}; - return lineEdit->text(); -} -QString getItemText(QComboBox *comboxBox) -{ - if (!comboxBox || (comboxBox->count() <= 0)) - return {}; - const QString &curTest = comboxBox->currentText(); - if (curTest.isEmpty()) - return {}; - return curTest.at(0).toUpper(); -} -int getItemData(QComboBox *comboxBox) -{ - if (!comboxBox || (comboxBox->count() <= 0)) - return {}; - int data = comboxBox->currentData().toInt(); - return data; -} -int getItemValue(QSpinBox *spinBox) -{ - if (!spinBox) - return 0; - return spinBox->value(); -} - - - - - - -LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool checkable) - : QWidget(parent) - - , m_scFixed(new QShortcut(QKeySequence(Qt::ShiftModifier + Qt::Key_F2), this, SLOT(setFixedDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_scOffset(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(setOffsetDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_checkable(checkable) - , m_withNowButton(withNowButton) -{ - -#ifdef TABLET_MODE - - setupUiTouch(); - return; -#else - - m_uiLe = new Ui::LtsDateTimeEditLe; - m_uiLe->setupUi(this); - - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - setFocusPolicy(Qt::StrongFocus); - setContentsMargins(QMargins()); - - QCompleter *completer = new QCompleter(DTG::months(), this); - completer->setCaseSensitivity(Qt::CaseInsensitive); - completer->setCompletionMode(QCompleter::InlineCompletion); - - QRegularExpression rx(DTG::months().join(QLatin1Char('|'))); - QRegularExpressionValidator *val = new QRegularExpressionValidator(rx, this); - m_uiLe->leLongMonth->setCompleter(completer); - m_uiLe->leLongMonth->setValidator(val); - - connect(m_uiLe->leLongDay, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueDayChanged); - connect(m_uiLe->leLongMonth, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueMonthChanged2); - connect(m_uiLe->leLongTZ, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueTimezoneChangedStr); - connect(m_uiLe->leLongYear, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueYearChanged); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutHHMMSS); - connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutDay); - connect(m_uiLe->leLongMonth, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutMonth); - connect(m_uiLe->leLongTZ, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutTZ); - connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutYear); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongMonth, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongTZ, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onHmsWheelEvent); - connect(m_uiLe->leLongDay, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onDayWheelEvent); - connect(m_uiLe->leLongMonth, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onMonthWheelEvent); - connect(m_uiLe->leLongYear, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onYearWheelEvent); - - connect(m_uiLe->checkBox, &QCheckBox::toggled, this, &LtsDateTimeEdit::onCheckBoxChanged); - connect(m_uiLe->pbNow, &QPushButton::clicked, this, &LtsDateTimeEdit::setOffsetDateTime); - connect(checkBox(), &QCheckBox::toggled, this, &LtsDateTimeEdit::toggled); - - setTimezone(DTG::timezone()); - - setCheckable(m_checkable); - - setWithNowButton(m_withNowButton); - - onCheckBoxChanged(); -#endif -} - -LtsDateTimeEdit::~LtsDateTimeEdit() -{ - delete m_uiLe; -} - -bool LtsDateTimeEdit::setupUiTouch() -{ - m_uiTouch = new Ui::LtsDateTimeEdit; - m_uiTouch->setupUi(this); - setFocusPolicy(Qt::StrongFocus); - - for (int i = 65; i < 91; i++ ) - { - if (QChar(i).toUpper() == QChar::fromLatin1('I')) - continue; - m_uiTouch->cbxTimeZone->addItem(QChar(i), QChar(i)); - } - - connect(m_uiTouch->pbNow, &QPushButton::clicked, - this, &LtsDateTimeEdit::setOffsetDateTime); - connect(m_uiTouch->cbxMonth, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spDay, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spHour, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spMinutes, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spSeconds, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spYear, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - - connect(m_uiTouch->checkBox, &QCheckBox::toggled, - this, &LtsDateTimeEdit::onCheckBoxChanged); - connect(m_uiTouch->cbxTimeZone, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::valueTimezoneChangedInt); - - - - connect(m_uiTouch->cbxMonth, &QComboBox::currentTextChanged, - this, &LtsDateTimeEdit::valueMonthChanged); - - connect(checkBox(), &QCheckBox::toggled, this, &LtsDateTimeEdit::toggled); - - setMinMaxMonth(1,12); - - setTimezone(DTG::timezone()); - - - setCheckable(m_checkable); - - setWithNowButton(m_withNowButton); - - onCheckBoxChanged(); - - return true; -} - - -void LtsDateTimeEdit::setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb) -{ - m_getCurrentTimeCallback = cb; -} - -void LtsDateTimeEdit::setEditTimezone(const bool edit) -{ - m_editTimezone = edit; - if (m_uiLe) - m_uiLe->leLongTZ->setEnabled(m_editTimezone); - if (m_uiTouch) - m_uiTouch->cbxTimeZone->setEnabled(m_editTimezone); -} - -void LtsDateTimeEdit::setWithButtons(const bool buttons) -{ - m_withButtons = buttons; - if (buttons) - { - if (m_uiLe && !m_uiLe->pbBBB) - { - m_uiLe->addButtons(this); - connect(m_uiLe->pbBBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackHourClicked); - connect(m_uiLe->pbBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackMinuteClicked); - connect(m_uiLe->pbB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackSecondClicked); - connect(m_uiLe->pbF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardSecondClicked); - connect(m_uiLe->pbFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardMinuteClicked); - connect(m_uiLe->pbFFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardHourClicked); - disableControls(); - } - } - else - { - if (m_uiLe) - { - delete m_uiLe->pbBBB; - delete m_uiLe->pbBB; - delete m_uiLe->pbB; - delete m_uiLe->pbFFF; - delete m_uiLe->pbFF; - delete m_uiLe->pbF; - - m_uiLe->pbBBB = nullptr; - m_uiLe->pbBB = nullptr; - m_uiLe->pbB = nullptr; - m_uiLe->pbFFF = nullptr; - m_uiLe->pbFF = nullptr; - m_uiLe->pbF = nullptr; - } - } -} - -void LtsDateTimeEdit::setWithSlider(const bool slider) -{ - m_withSlider = slider; - - if (!m_uiLe) - return; - - if (slider) - { - m_uiLe->addSlider(this); - m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000); - m_uiLe->slTime->setMaximum(m_maxTimeMSec > 0 ? m_maxTimeMSec / 1000 : time(nullptr)); - connect(m_uiLe->slTime, &QSlider::sliderMoved, this, &LtsDateTimeEdit::setSliderValue); - disableControls(); - } - else - { - delete m_uiLe->slTime; - m_uiLe->slTime = nullptr; - } -} - -void LtsDateTimeEdit::setReadOnly(const bool readOnly) -{ - if (m_readOnly == readOnly) - return; - m_readOnly = readOnly; - - if (m_uiLe) - { - m_uiLe->leLongDay->setReadOnly(m_readOnly); - m_uiLe->leLongHHMMSS->setReadOnly(m_readOnly); - m_uiLe->leLongMonth->setReadOnly(m_readOnly); - m_uiLe->leLongTZ->setReadOnly(m_readOnly); - m_uiLe->leLongYear->setReadOnly(m_readOnly); - } - if (m_uiTouch) - { - m_uiTouch->spDay->setReadOnly(m_readOnly); - m_uiTouch->spHour->setReadOnly(m_readOnly); - m_uiTouch->spMinutes->setReadOnly(m_readOnly); - m_uiTouch->spSeconds->setReadOnly(m_readOnly); - m_uiTouch->spYear->setReadOnly(m_readOnly); - m_uiTouch->cbxTimeZone->setEnabled(m_readOnly); - } -} - -QCheckBox *LtsDateTimeEdit::checkBox() const -{ - return m_uiLe ? m_uiLe->checkBox : m_uiTouch ? m_uiTouch->checkBox : nullptr; -} - -void LtsDateTimeEdit::setCheckable(bool checkable) -{ - if (!checkBox() || (m_checkable == checkable)) - return; - - m_checkable = checkable; - checkBox()->setVisible(checkable); - if (!checkable) - checkBox()->setChecked(true); -} - -void LtsDateTimeEdit::setChecked(bool bChecked) -{ - if (!checkBox()) - return; - checkBox()->setChecked(bChecked); -} - -bool LtsDateTimeEdit::isChecked() const -{ - if (!checkBox()) - return false; - return checkBox()->isChecked(); -} - -QPushButton *LtsDateTimeEdit::pbNow() const -{ - return m_uiLe ? m_uiLe->pbNow : m_uiTouch ? m_uiTouch->pbNow : nullptr; -} - -void LtsDateTimeEdit::setWithNowButton(bool nowButton) -{ - if (!pbNow()) - return; - - pbNow()->setVisible(nowButton); -} - -bool LtsDateTimeEdit::setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible) -{ - if (msec == 0 && isCheckable()) - { - if (m_uiLe) - m_uiLe->checkBox->setChecked(false); - m_dateIsAcceptable = true; - return true; - } - if (m_uiLe) - m_uiLe->checkBox->setChecked(true); - - m_dateIsAcceptable = false; - if (msec == 0) - return false; - - - - - - - if (!setToNearestPossible) - { - if (!isDataAcceptable()) - return false; - } - else - { - if (msec < m_minTimeMSec && m_minTimeMSec > 0) - msec = m_minTimeMSec; - if (msec > m_maxTimeMSec && m_maxTimeMSec > 0) - msec = m_maxTimeMSec; - } - - m_curTimeMSec = msec; - - const qint64 offsetTimeZoneMsec = getTimezoneAsSecs() * 1000; - const QDateTime datetime(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec + offsetTimeZoneMsec, Qt::UTC)); - const QDate date(datetime.date()); - const QTime time(datetime.time()); - - const int day = date.day(); - const int month = date.month(); - const int year = date.year(); - const int hour = time.hour(); - const int minute = time.minute(); - const int second = time.second(); - - - if (!setYear(year) || - !setMonth(month) || - !setDay(day) || - !setHHMMSS(hour, minute, second)) - return false; - - if (!isDataAcceptable()) - return false; - - enableControls(); - - emit dateTimeChanged(); - - return true; -} - - -qint64 LtsDateTimeEdit::msecsSinceEpoch() const -{ - if (isEmpty()) - return 0; - - if (m_uiLe && !m_uiLe->checkBox->isChecked()) - return 0; - if (m_uiTouch && !m_uiTouch->checkBox->isChecked()) - return 0; - - auto that = const_cast(this); - if (!that->isDataAcceptable()) - return 0; - - return m_curTimeMSec; -} - -bool LtsDateTimeEdit::setTimezone(char tz) -{ - tz = toupper(tz); - if (!DTG::isTimezoneValid(tz)) - return false; - - if (m_uiLe) - setItemText(m_uiLe->leLongTZ, QString(QLatin1Char(tz))); - if (m_uiTouch) - setItemText(m_uiTouch->cbxTimeZone, QString(QLatin1Char(tz))); - - setMsecsSinceEpoch(m_curTimeMSec); - - return true; -} - -char LtsDateTimeEdit::getTimezone() const -{ - QString str; - if (m_uiLe) - str = getItemText(m_uiLe->leLongTZ); - if (m_uiTouch) - str = getItemText(m_uiTouch->cbxTimeZone); - - if (str.isEmpty()) - return DTG::timezone(); - return str[0].toUpper().toLatin1(); -} - -qint64 LtsDateTimeEdit::getTimezoneAsSecs() const -{ - const char tzGUI = getTimezone(); - return DTG::timezone2Secs(tzGUI); -} - -void LtsDateTimeEdit::clearTimezone() -{ - if (m_uiLe) - m_uiLe->leLongTZ->clear(); - if (m_uiTouch) - m_uiTouch->cbxTimeZone->setCurrentIndex(0); -} - -void LtsDateTimeEdit::setMinMaxMonth(int minMonth, int maxMonth) -{ - if (!m_uiTouch) - return; - - m_minMonth = minMonth; - m_maxMonth = maxMonth; - - const int currentMonth = m_uiTouch->cbxMonth->currentData().toInt(); - m_uiTouch->cbxMonth->blockSignals(true); - m_uiTouch->cbxMonth->clear(); - for (int i = 1; i <= 12; ++i) - { - if (i < m_minMonth) - continue; - if (i > m_maxMonth) - break; - - - - const QStringList &monthSl = DTG::months(); - m_uiTouch->cbxMonth->addItem(monthSl[i-1], i); - - - - - } - - - m_uiTouch->cbxMonth->setCurrentIndex(m_uiTouch->cbxMonth->findData(currentMonth)); - m_uiTouch->cbxMonth->blockSignals(false); -} - - -void LtsDateTimeEdit::setSubWidgetsProperties(const QString &name, const QVariant &value) -{ - if (m_uiLe) - { - m_uiLe->leLongDay->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongHHMMSS->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongMonth->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongYear->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongTZ->setProperty(name.toStdString().c_str(), value); - } - if (m_uiTouch) - { - m_uiTouch->spDay->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spHour->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spMinutes->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spSeconds->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spYear->setProperty(name.toStdString().c_str(), value); - } -} - -bool LtsDateTimeEdit::setMonth(int month) -{ - const auto months(DTG::months()); - if (month < 1 || month > months.size()) - return false; - - if (m_uiLe) - setItemText(m_uiLe->leLongMonth, months.at(month - 1)); - if (m_uiTouch) - setItemData(m_uiTouch->cbxMonth, month); - return true; -} - -int LtsDateTimeEdit::getMonth() const -{ - if (m_uiLe) - { - const QString str = getItemText(m_uiLe->leLongMonth); - if (str.length() != 3) - return -1; - - int idx = DTG::months().indexOf(str); - if (idx < 0) - return -1; - return idx + 1; - } - if (m_uiTouch) - { - int month = getItemData(m_uiTouch->cbxMonth); - return month; - } - return 0; -} - -bool LtsDateTimeEdit::setDay(int day) -{ - if (day < 1 || day > 31) - return false; - - QString oldDay; - if (m_uiLe) - oldDay = m_uiLe->leLongDay->text(); - int oldDayInt = 0; - if (m_uiTouch) - oldDayInt = getItemValue(m_uiTouch->spDay); - - if (m_uiLe) - setItemText(m_uiLe->leLongDay, QStringLiteral("%1").arg(day, 2, 10, QLatin1Char('0'))); - if (m_uiTouch) - setItemValue(m_uiTouch->spDay, day); - - if (!isDayAcceptable()) - { - if (m_uiLe) - setItemText(m_uiLe->leLongDay, oldDay); - if (m_uiTouch) - setItemValue(m_uiTouch->spDay, oldDayInt); - return false; - } - - return true; -} - -int LtsDateTimeEdit::getDay() const -{ - if (m_uiLe) - return m_uiLe->leLongDay->getValue(); - if (m_uiTouch) - return getItemValue(m_uiTouch->spDay); - return 0; -} - -bool LtsDateTimeEdit::setHHMMSS(int hour, int minute, int second) -{ - if (hour < 0 || hour > 23 || - minute < 0 || minute > 59 || - second < 0 || second > 59) - return false; - - if (m_uiLe) - m_uiLe->leLongHHMMSS->setValue(hour * 10000 + minute * 100 + second); - if (m_uiTouch) - { - setItemValue(m_uiTouch->spHour , hour); - setItemValue(m_uiTouch->spMinutes, minute); - setItemValue(m_uiTouch->spSeconds, second); - } - - return true; -} - -int LtsDateTimeEdit::getHour() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return time / 10000; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spHour); - } - return 0; -} - -int LtsDateTimeEdit::getMinute() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return (time / 100) % 100; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spMinutes); - } - return 0; -} - -int LtsDateTimeEdit::getSecond() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return time % 100; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spSeconds); - } - return 0; -} - -bool LtsDateTimeEdit::setYear(int year) -{ - int finalYear = -1; - if (year >= 0 && year <= 99) - finalYear = year; - else if (year >= 2000 && year <= 2099) - finalYear = year - 2000; - - if (finalYear >= 0) - { - if (m_uiLe) - m_uiLe->leLongYear->setValue(finalYear); - if (m_uiTouch) - setItemValue(m_uiTouch->spYear, finalYear); - return true; - } - - return false; -} - -int LtsDateTimeEdit::getYear() const -{ - if (m_uiLe) - return 2000 + m_uiLe->leLongYear->getValue(); - if (m_uiTouch) - return 2000 + m_uiTouch->spYear->value(); - return 2000; -} - -bool LtsDateTimeEdit::isEmpty() const -{ - return (isEmptyDay() || - isEmptyHHMMSS() || - isEmptyMonth() || - isEmptyTimezone() || - isEmptyYear()); -} - -bool LtsDateTimeEdit::isValidDate() const -{ - return m_dateIsAcceptable; -} - - -bool LtsDateTimeEdit::isEmptyDay() const -{ - if (m_uiLe) - return m_uiLe->leLongDay->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyHHMMSS() const -{ - if (m_uiLe) - return m_uiLe->leLongHHMMSS->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyMonth() const -{ - if (m_uiLe) - return m_uiLe->leLongMonth->text().isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyTimezone() const -{ - if (m_uiLe) - return m_uiLe->leLongTZ->text().isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyYear() const -{ - if (m_uiLe) - return m_uiLe->leLongYear->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::hasFocus() const -{ - if (!m_uiLe) - return false; - return (m_uiLe->leLongDay->hasFocus() || - m_uiLe->leLongHHMMSS->hasFocus() || - m_uiLe->leLongMonth->hasFocus() || - m_uiLe->leLongTZ->hasFocus() || - m_uiLe->leLongYear->hasFocus()); -} - -bool LtsDateTimeEdit::isDataAcceptable() -{ - m_dateIsAcceptable = false; - - bool state = - isDayAcceptable() && - isHHMMSSAcceptable() && - isMonthAcceptable() && - isYearAcceptable() && - QDate::isValid(getYear(), getMonth(), getDay()) && - QTime::isValid(getHour(), getMinute(), getSecond()); - - if (m_editTimezone && state) - state = isTzAcceptable(); - - showInvalidData(false); - - if (!state) - { - disableControls(); - return false; - } - - qint64 msec = QDateTime(QDate(getYear(), getMonth(), getDay()), QTime(getHour(), getMinute(), getSecond()), Qt::UTC).toMSecsSinceEpoch(); - msec -= DTG::timezone2Secs(getTimezone()) * 1000LL; - if (m_minTimeMSec > 0 && m_maxTimeMSec > 0 && m_minTimeMSec < m_maxTimeMSec) - { - if (msec < m_minTimeMSec || msec > m_maxTimeMSec) - return false; - } - - m_curTimeMSec = msec; - enableControls(); - m_dateIsAcceptable = true; - return true; -} - -bool LtsDateTimeEdit::isDayAcceptable() const -{ - int value = getDay(); - int max = 31; - if (isMonthAcceptable()) - { - const int month = getMonth(); - switch (month) - { - case 2: - { - max = 28; - if (isYearAcceptable()) - { - if (getYear() % 4 == 0 && (getYear() % 100 != 0 || getYear() % 400 == 0)) - - max = 29; - } - break; - } - case 4: - case 6: - case 9: - case 11: - max = 30; - break; - default: - break; - } - } - return (value > 0 && value <= max); -} - -bool LtsDateTimeEdit::isHHMMSSAcceptable() const -{ - int hh = getHour(); - if (hh < 0 || hh > 23) - return false; - - int mm = getMinute(); - if (mm < 0 || mm > 59) - return false; - - int ss = getSecond(); - if (ss < 0 || ss > 59) - return false; - - return true; -} - -bool LtsDateTimeEdit::isMonthAcceptable() const -{ - int value = getMonth(); - return (value > 0 && value <= 12); -} - -bool LtsDateTimeEdit::isYearAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongYear); - return true; -} - -bool LtsDateTimeEdit::isTzAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongTZ); - return true; -} - -void LtsDateTimeEdit::showInvalidData(bool setFocus) -{ - if (!m_uiLe) - return; - - bool focus = setFocus; - - auto check = [&](QLineEdit * le, bool isValid) - { - if (isValid) - le->setStyleSheet(QString()); - else - { - le->setStyleSheet(QLatin1String("background-color: yellow;")); - if (focus) - { - le->setFocus(); - focus = false; - } - } - }; - - check(m_uiLe->leLongDay, isDayAcceptable()); - check(m_uiLe->leLongHHMMSS, isHHMMSSAcceptable()); - if (m_editTimezone) - check(m_uiLe->leLongTZ, isTzAcceptable()); - check(m_uiLe->leLongMonth, isMonthAcceptable()); - check(m_uiLe->leLongYear, isYearAcceptable()); -} - -bool LtsDateTimeEdit::isLineEditDataAcceptable(QLineEdit *le) const -{ - const QValidator *pValidator = le->validator(); - - if (!pValidator) - return true; - - QString text = le->text(); - - int pos; - QValidator::State state = pValidator->validate(text, pos); - - if (state == QValidator::Acceptable || state == QValidator::Intermediate) - return true; - - return false; -} - -void LtsDateTimeEdit::setFixedDateTime() -{ - setMsecsSinceEpoch(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("FixedTime"), 0).toLongLong()); -} - -void LtsDateTimeEdit::setOffsetDateTime() -{ - if (!m_getCurrentTimeCallback) - return; - - qint64 offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong(); - - setMsecsSinceEpoch((m_getCurrentTimeCallback() / 1000) * 1000 + offset); -} - -void LtsDateTimeEdit::setSliderValue(int val) -{ - setMsecsSinceEpoch(val * 1000LL); -} - -void LtsDateTimeEdit::valueDayChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongDay->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString tag(str); - - if (tag.length() > 2) - { - tag = tag.left(2); - changed = true; - } - - bool ok; - int t = tag.toInt(&ok, 10); - - if (ok) - { - if (t > 31) - { - tag.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - if (changed) - { - m_uiLe->leLongDay->setText(tag); - m_uiLe->leLongDay->setCursorPosition(cp); - } - - if (tag.length() == 2) - { - showInvalidData(false); - - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::fixupHHMMSS() -{ - if (!m_uiLe) - return; - - int curpos = m_uiLe->leLongHHMMSS->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString uhrzeit(m_uiLe->leLongHHMMSS->text()); - - - - - - - QString hh; - QString mm; - QString ss; - - if (uhrzeit.length() > 1) - hh = uhrzeit.left(2); - else - hh = uhrzeit + QLatin1Char('0'); - - if (uhrzeit.length() > 0) - { - if (uhrzeit.length() == 1) - hh = uhrzeit.mid(0, 1) + QLatin1Char('0'); - else if (uhrzeit.length() >= 2) - { - hh = uhrzeit.mid(0, 2); - if (uhrzeit.length() > 2) - { - if (uhrzeit.length() == 3) - mm = uhrzeit.mid(2, 1) + QLatin1Char('0'); - else if (uhrzeit.length() >= 4) - { - mm = uhrzeit.mid(2, 2); - if (uhrzeit.length() == 5) - ss = uhrzeit.mid(4, 1) + QLatin1Char('0'); - else if (uhrzeit.length() == 6) - ss = uhrzeit.mid(4, 2); - } - } - } - } - - bool ok; - - int h = hh.toInt(&ok, 10); - if (ok) - { - if (h > 23) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - int m = mm.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2) && (m > 59)) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - int s = ss.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2 && mm.length() == 2) && (s > 59)) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - if (changed) - { - m_uiLe->leLongHHMMSS->setText(uhrzeit); - m_uiLe->leLongHHMMSS->setCursorPosition(cp); - } - - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::valueMonthChanged2(const QString &str) -{ - if (!m_uiLe) - return; - if (str.length() == 3 && m_uiLe->leLongMonth->selectedText().isEmpty()) - focusNextPrevChild(true); -} - -void LtsDateTimeEdit::valueMonthChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongMonth->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString monStr = str; - - const auto months = DTG::months(); - for (int i = 0; i < months.size(); ++i) - { - const QString &mon = months.at(i); - if (mon.startsWith(monStr, Qt::CaseInsensitive)) - { - monStr = mon; - changed = true; - break; - } - } - - if (!changed) - { - monStr.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - - if (changed) - { - m_uiLe->leLongMonth->setText(monStr); - - - if (cp > 0) - m_uiLe->leLongMonth->setCursorPosition(cp); - else - m_uiLe->leLongMonth->setCursorPosition(1); - } - - if (monStr.size() == 3) - { - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::valueTimezoneChangedStr(const QString &str) -{ - if (str.length() > 1) - { - clearTimezone(); - return; - } - if (str.length() == 0) - return; - QChar c = str.at(0); - if (!c.isLetter() || c.toUpper() == QLatin1Char('J')) - { - clearTimezone(); - return; - } - if (c.isLower()) - setTimezone(c.toUpper().toLatin1()); - - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); -} - -void LtsDateTimeEdit::valueTimezoneChangedInt(int newTimezone) -{ - Q_UNUSED(newTimezone); - if (!m_uiTouch) - return; - const QString &curText = m_uiTouch->cbxTimeZone->currentText(); - if (curText.isEmpty()) - return; - - const QChar tz = curText.at(0); - setTimezone(tz.toUpper().toLatin1()); -} - -void LtsDateTimeEdit::valueYearChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongYear->cursorPosition(); - int cp = curpos; - - bool changed = false; - - const QString &year = str; - - QString yy; - if (year.length() > 2) - { - yy = year.left(2); - changed = true; - } - - if (changed) - { - m_uiLe->leLongYear->setText(yy); - m_uiLe->leLongYear->setCursorPosition(cp); - } - - if (year.length() == 2) - { - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::onBackHourClicked() -{ - if (!setMsecsSinceEpoch(m_curTimeMSec - s_oneHourMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onBackMinuteClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneMinuteMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onBackSecondClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneSecondMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onForwardSecondClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneSecondMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onForwardMinuteClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneMinuteMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onForwardHourClicked() -{ - if (!setMsecsSinceEpoch(m_curTimeMSec + s_oneHourMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onCheckBoxChanged() -{ - QVector widgets; - bool bEnable = false; - - if (m_uiLe) - { - widgets = {m_uiLe->leLongDay, m_uiLe->leLongHHMMSS, m_uiLe->leLongTZ, - m_uiLe->leLongMonth, m_uiLe->leLongYear}; - } - - if (m_uiTouch) - { - widgets = {m_uiTouch->spDay, m_uiTouch->spHour, m_uiTouch->spMinutes, m_uiTouch->spSeconds, m_uiTouch->spYear, m_uiTouch->cbxMonth, m_uiTouch->cbxTimeZone, m_uiTouch->pbNow}; - } - - bEnable = checkBox()->isChecked(); - for (auto w : qAsConst(widgets)) - { - if (w) - w->setEnabled(bEnable); - } - enaDisaControls(bEnable); -} - -void LtsDateTimeEdit::disableControls() -{ - enaDisaControls(false); -} - -void LtsDateTimeEdit::enableControls() -{ - if (!m_uiLe) - return; - - enaDisaControls(true); - if (m_withSlider && - m_minTimeMSec != 0 && - m_maxTimeMSec > 0 && - m_maxTimeMSec > m_minTimeMSec) - { - m_uiLe->slTime->setEnabled(true); - m_uiLe->slTime->setValue(m_curTimeMSec / 1000LL); - } -} - -void LtsDateTimeEdit::enaDisaControls(bool bEnable) -{ - if (!m_uiLe) - return; - const QVector widgets({m_uiLe->pbBBB, m_uiLe->pbBB, m_uiLe->pbB, - m_uiLe->pbF, m_uiLe->pbFF, m_uiLe->pbFFF, - m_uiLe->slTime}); - for (auto w : widgets) - { - if (w) - w->setEnabled(bEnable); - } -} - -void LtsDateTimeEdit::setMinimumTimeMSec(qint64 msec) -{ - if (m_minTimeMSec == msec) - return; - m_minTimeMSec = msec; - if (m_minTimeMSec > 0 && m_uiLe && m_uiLe->slTime) - { - m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000LL); - if (m_maxTimeMSec > m_minTimeMSec) - m_uiLe->slTime->setEnabled(true); - } - if (m_curTimeMSec < m_minTimeMSec) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::setMaximumTimeMSec(qint64 msec) -{ - if (m_maxTimeMSec == msec) - return; - m_maxTimeMSec = msec; - if (!m_uiLe || !m_uiLe->slTime) - return; - - m_uiLe->slTime->setMaximum(m_maxTimeMSec / 1000LL); - if (m_minTimeMSec > 0 && m_maxTimeMSec > m_minTimeMSec) - m_uiLe->slTime->setEnabled(true); - if (m_maxTimeMSec > 0 && m_curTimeMSec > m_maxTimeMSec) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::clear() -{ - if (m_uiLe) - { - m_uiLe->leLongDay->clear(); - m_uiLe->leLongHHMMSS->clear(); - m_uiLe->leLongMonth->clear(); - m_uiLe->leLongYear->clear(); - m_uiLe->leLongTZ->clear(); - m_curTimeMSec = 0; - } - if (m_uiTouch) - { - m_uiTouch->spDay->clear(); - m_uiTouch->spHour->clear(); - m_uiTouch->spMinutes->clear(); - m_uiTouch->spSeconds->clear(); - m_uiTouch->spYear->clear(); - } - emit dateTimeChanged(); - -} - -void LtsDateTimeEdit::onFocusOutDay() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongDay->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setDay(dt.date().day()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutHHMMSS() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongHHMMSS->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyDay() || - !isEmptyMonth() || - !(isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setHHMMSS(dt.time().hour(), dt.time().minute(), dt.time().second()); - } - } - } - value += QString(6 - value.length(), QLatin1Char('0')); - m_uiLe->leLongHHMMSS->setText(value); - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutTZ() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongTZ->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - !isEmptyDay() || - !isEmptyYear()) - setTimezone(DTG::timezone()); - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutMonth() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongMonth->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyDay() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setMonth(dt.date().month()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutYear() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongYear->text(); - - if (value.length() == 1) - { - value = QLatin1Char('0') + value; - m_uiLe->leLongYear->setText(value); - } - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyDay()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setYear(dt.date().year()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutBackspace() -{ - m_backspacePressed = true; -} - -void LtsDateTimeEdit::onFocusOut() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - m_recursionAvoid = true; - onFocusOutDay(); - onFocusOutHHMMSS(); - onFocusOutMonth(); - onFocusOutTZ(); - onFocusOutYear(); - m_recursionAvoid = false; -} - -void LtsDateTimeEdit::onHmsWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (m_uiLe->leLongHHMMSS->text().size() != 6) - return; - - fixupHHMMSS(); - - int selStart = 0; - switch (cursorPos) - { - case 0: - case 1: - case 2: - if (bForward) - onForwardHourClicked(); - else - onBackHourClicked(); - selStart = 0; - break; - case 3: - case 4: - if (bForward) - onForwardMinuteClicked(); - else - onBackMinuteClicked(); - selStart = 2; - break; - case 5: - case 6: - if (bForward) - onForwardSecondClicked(); - else - onBackSecondClicked(); - selStart = 4; - break; - default: - return; - } - m_uiLe->leLongHHMMSS->setCursorPosition(cursorPos); - m_uiLe->leLongHHMMSS->setSelection(selStart, 2); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onDayWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isDayAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneDayMSec, true); - m_uiLe->leLongDay->setCursorPosition(cursorPos); - m_uiLe->leLongDay->setSelection(0, 2); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onMonthWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isMonthAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addMonths(ofs)); - setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); - m_uiLe->leLongMonth->setCursorPosition(cursorPos); - m_uiLe->leLongMonth->setSelection(0, 3); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onYearWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isYearAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addYears(ofs)); - setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); - m_uiLe->leLongYear->setCursorPosition(cursorPos); - m_uiLe->leLongYear->setSelection(0, 2); - emit dateTimeChanged(); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit.h b/Lts/Common/LtsWidgets/lts_date_time_edit.h deleted file mode 100755 index 13047d3f5..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit.h +++ /dev/null @@ -1,213 +0,0 @@ - - - -#pragma once - - -#include "lts_widgets_export.h" -#include "qcheckbox.h" -#include "qpushbutton.h" - -#include -#include - - -namespace Ui -{ -class LtsDateTimeEditLe; -class LtsDateTimeEdit; -} - -class QLineEdit; -class QShortcut; - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward) - Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone) - Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons) - Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider) - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) - Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton) - Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable) - Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY toggled) - -public: - - using CurrentTimeCallback = std::function; - - explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool checkable = false); - ~LtsDateTimeEdit(); - - bool setupUiTouch(); - - - qint64 msecsSinceEpoch() const; - bool isEmpty() const; - bool isValidDate() const; - - void setAutoForward(const bool af) { m_autoForward = af; } - bool getAutoForward() const { return m_autoForward; } - - void setEditTimezone(const bool edit); - bool getEditTimezone() const { return m_editTimezone; } - - void setWithButtons(const bool buttons); - bool getWithButtons() const { return m_withButtons; } - - void setWithSlider(const bool slider); - bool getWithSlider() const { return m_withSlider; } - - void setReadOnly(const bool readOnly); - bool isReadOnly() const { return m_readOnly; } - - void setCheckable(bool checkable); - bool isCheckable() const { return m_checkable; } - void setChecked(bool bChecked); - bool isChecked() const; - - void setWithNowButton(bool nowButton); - bool withNowButton() const { return m_withNowButton; } - - - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const { return m_minTimeMSec; } - - - void setMaximumTimeMSec(qint64 msec); - qint64 maximumTimeMSec() const { return m_maxTimeMSec; } - - void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); } - - void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb); - - bool setTimezone(char tz); - char getTimezone() const; - qint64 getTimezoneAsSecs() const; - - void setSubWidgetsProperties(const QString &name, const QVariant &value); - -Q_SIGNALS: - void dateTimeChanged(); - void toggled(bool on); - -public Q_SLOTS: - bool setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible = true); - void clear(); - -private: - void clearTimezone(); - - void setMinMaxMonth(int minMonth, int maxMonth); - - bool setMonth(int month); - int getMonth() const; - - bool setDay(int day); - int getDay() const; - - bool setHHMMSS(int hour, int minute, int second); - int getHour() const; - int getMinute() const; - int getSecond() const; - - bool setYear(int year); - int getYear() const; - - bool isEmptyDay() const; - bool isEmptyHHMMSS() const; - bool isEmptyTimezone() const; - bool isEmptyMonth() const; - bool isEmptyYear() const; - - bool hasFocus() const; - - bool isDataAcceptable(); - - void showInvalidData(bool setFocus); - - bool isLineEditDataAcceptable(QLineEdit *le) const; - - bool isDayAcceptable() const; - bool isHHMMSSAcceptable() const; - bool isMonthAcceptable() const; - bool isYearAcceptable() const; - bool isTzAcceptable() const; - - QCheckBox* checkBox() const; - QPushButton* pbNow() const; - -private Q_SLOTS: - void setFixedDateTime(); - void setOffsetDateTime(); - - void setSliderValue(int val); - void valueDayChanged(const QString &str); - void fixupHHMMSS(); - void valueMonthChanged(const QString &str); - void valueMonthChanged2(const QString &str); - void valueTimezoneChangedStr(const QString &str); - void valueTimezoneChangedInt(int newTimezone); - void valueYearChanged(const QString &str); - - void onBackHourClicked(); - void onBackMinuteClicked(); - void onBackSecondClicked(); - void onForwardSecondClicked(); - void onForwardMinuteClicked(); - void onForwardHourClicked(); - - void onCheckBoxChanged(); - void disableControls(); - void enableControls(); - void enaDisaControls(bool bEnable); - - void onFocusOutHHMMSS(); - void onFocusOutTZ(); - void onFocusOutDay(); - void onFocusOutMonth(); - void onFocusOutYear(); - - void onFocusOutBackspace(); - void onFocusOut(); - - void onHmsWheelEvent(bool bForward, int cursorPos); - void onDayWheelEvent(bool bForward, int cursorPos); - void onMonthWheelEvent(bool bForward, int cursorPos); - void onYearWheelEvent(bool bForward, int cursorPos); - -private: - Ui::LtsDateTimeEditLe *m_uiLe{nullptr}; - Ui::LtsDateTimeEdit *m_uiTouch{nullptr}; - QShortcut *m_scFixed = nullptr; - QShortcut *m_scOffset = nullptr; - - CurrentTimeCallback m_getCurrentTimeCallback = nullptr; - - qint64 m_minTimeMSec = 0; - qint64 m_maxTimeMSec = 0; - - qint64 m_curTimeMSec = -1; - - bool m_editTimezone = true; - bool m_autoForward = true; - bool m_withButtons = false; - bool m_withSlider = false; - bool m_readOnly = false; - bool m_checkable = false; - bool m_withNowButton = false; - - int m_minMonth{0}; - int m_maxMonth{0}; - - - bool m_backspacePressed = false; - bool m_recursionAvoid = false; - bool m_dateIsAcceptable = false; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp b/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp deleted file mode 100755 index 2bdf1bed6..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "lts_date_time_edit_p.h" - -#include -#include -#include -#include - - -namespace LtsWidgets -{ - - - - -void LineEdit::clear() -{ - setStyleSheet(QString()); - QLineEdit::clear(); -} - -bool LineEdit::isEmpty() const -{ - return text().isEmpty(); -} - -void LineEdit::keyPressEvent(QKeyEvent *event) -{ - const bool wasEmpty = isEmpty(); - QLineEdit::keyPressEvent(event); - int keyCode = event->key(); - if (keyCode == Qt::Key_Backspace && wasEmpty) - { - emit focusOutBackspace(); - focusPreviousChild(); - event->accept(); - } -} - -void LineEdit::contextMenuEvent(QContextMenuEvent *e) -{ - Q_UNUSED(e); - -} - -void LineEdit::focusOutEvent(QFocusEvent *event) -{ - QLineEdit::focusOutEvent(event); - emit focusOut(); -} - - - - -DigitsLineEdit::DigitsLineEdit(QWidget *parent) - : LineEdit(parent) -{ - setAlignment(Qt::AlignRight); - QRegularExpression rx(QLatin1String("[-]{0,1}[0-9 ]*")); - setValidator(new QRegularExpressionValidator(rx, this)); -} - -void DigitsLineEdit::setExactNumDigits(int num) -{ - m_exactNumDigits = num; - - QRegularExpression rx(QString::asprintf("^[0-9 ]{%d}$", num)); - setValidator(new QRegularExpressionValidator(rx, this)); -} - -void DigitsLineEdit::setValue(int val) -{ - QString str; - - if (m_exactNumDigits == -1) - str.setNum(val); - else - str = QStringLiteral("%1").arg(val, m_exactNumDigits, 10, QLatin1Char('0')); - - setText(str); -} - -int DigitsLineEdit::getValue() const -{ - if (text().isEmpty()) - return 0; - return text().remove(QLatin1Char(' ')).toInt(); -} - -void DigitsLineEdit::setCursorPosition(int curPos) -{ - - curPos = curPos < 0 ? 1 : curPos; - QLineEdit::setCursorPosition(curPos); -} - -void DigitsLineEdit::wheelEvent(QWheelEvent *event) -{ - LineEdit::wheelEvent(event); - const QString txt = text(); - if (m_exactNumDigits > 0 && txt.length() != m_exactNumDigits) - return; - event->accept(); - int cursorPos = cursorPosition(); - emit wheelEventReceived(event->delta() > 0, cursorPos); -} - -void DigitsLineEdit::keyPressEvent(QKeyEvent *event) -{ - LineEdit::keyPressEvent(event); - if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) - { - const bool bUp = event->key() == Qt::Key_Up; - const int cursorPos = cursorPosition(); - emit wheelEventReceived(bUp, cursorPos); - } -} - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_p.h b/Lts/Common/LtsWidgets/lts_date_time_edit_p.h deleted file mode 100755 index daf8bcf4a..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_p.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -namespace LtsWidgets -{ - -class LineEdit : public QLineEdit -{ - Q_OBJECT -public: - using QLineEdit::QLineEdit; - - void clear(); - bool isEmpty() const; -protected: - void contextMenuEvent(QContextMenuEvent *e) override; - void focusOutEvent(QFocusEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; -Q_SIGNALS: - void focusOutBackspace(); - void focusOut(); -}; - -class DigitsLineEdit final : public LineEdit -{ - Q_OBJECT -public: - DigitsLineEdit(QWidget *parent = nullptr); - - void setExactNumDigits(int num); - void setCursorPosition(int curPos); - void setValue(int val); - int getValue() const; -Q_SIGNALS: - void wheelEventReceived(bool bForward, int cursorPos); -protected: - void wheelEvent(QWheelEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; -private: - int m_exactNumDigits = -1; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp b/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp deleted file mode 100755 index d34b1fbea..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp +++ /dev/null @@ -1,1792 +0,0 @@ - - - -#include "lts_date_time_edit.h" -#include "lts_date_time_edit_p.h" - -#include "ui_lts_date_time_edit_touch.h" - - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr qint64 s_oneSecondMSec = 1000LL; -static constexpr qint64 s_oneMinuteMSec = 60 * s_oneSecondMSec; -static constexpr qint64 s_oneHourMSec = 60 * s_oneMinuteMSec; -static constexpr qint64 s_oneDayMSec = 24 * s_oneHourMSec; - -namespace Ui -{ -class LtsDateTimeEditLe -{ -public: - LtsWidgets::DigitsLineEdit *leLongDay = nullptr; - LtsWidgets::DigitsLineEdit *leLongHHMMSS = nullptr; - LtsWidgets::LineEdit *leLongTZ = nullptr; - LtsWidgets::DigitsLineEdit *leLongMonth = nullptr; - LtsWidgets::DigitsLineEdit *leLongYear = nullptr; - QCheckBox *checkBox = nullptr; - QGridLayout *gridLayout = nullptr; - QPushButton *pbBBB = nullptr; - QPushButton *pbBB = nullptr; - QPushButton *pbB = nullptr; - QPushButton *pbF = nullptr; - QPushButton *pbFF = nullptr; - QPushButton *pbFFF = nullptr; - QPushButton *pbNow = nullptr; - QSlider *slTime = nullptr; - - static void setFixedSize(QLineEdit *le, int charCount) - { - QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(false); - - const QFontMetricsF fm(le->font()); - const auto textMargins = le->textMargins(); - le->setSizePolicy(sizePolicy); - charCount += 3; -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - const qreal w = fm.width(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); -#else - const qreal w = fm.horizontalAdvance(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); -#endif - le->setMinimumWidth(w); - le->setMaximumWidth(w); - } - - void setupUi(QWidget *parent) - { - gridLayout = new QGridLayout(parent); - gridLayout->setMargin(0); - - int iStart = 3; - - checkBox = new QCheckBox; - gridLayout->addWidget(checkBox, 0, iStart++, 1, 1); - - auto horizontalLayout = new QHBoxLayout; - horizontalLayout->setSpacing(0); - horizontalLayout->setContentsMargins(0, 0, 0, 0); - - leLongDay = new LtsWidgets::DigitsLineEdit(parent); - setFixedSize(leLongDay, 2); - leLongDay->setObjectName(QStringLiteral("leLongDay")); - horizontalLayout->addWidget(leLongDay); - - leLongHHMMSS = new LtsWidgets::DigitsLineEdit(parent); - leLongHHMMSS->setExactNumDigits(6); - setFixedSize(leLongHHMMSS, 6); - leLongHHMMSS->setObjectName(QStringLiteral("leLongHHMMSS")); - horizontalLayout->addWidget(leLongHHMMSS); - - leLongTZ = new LtsWidgets::LineEdit(parent); - leLongTZ->setAlignment(Qt::AlignCenter); - setFixedSize(leLongTZ, 1); - leLongTZ->setObjectName(QStringLiteral("leLongTZ")); - horizontalLayout->addWidget(leLongTZ); - - leLongMonth = new LtsWidgets::DigitsLineEdit(parent); - leLongMonth->setAlignment(Qt::AlignCenter); - setFixedSize(leLongMonth, 3); - leLongMonth->setObjectName(QStringLiteral("leLongMonth")); - horizontalLayout->addWidget(leLongMonth); - - leLongYear = new LtsWidgets::DigitsLineEdit(parent); - leLongYear->setExactNumDigits(2); - setFixedSize(leLongYear, 2); - leLongYear->setObjectName(QStringLiteral("leLongYear")); - horizontalLayout->addWidget(leLongYear); - - gridLayout->addLayout(horizontalLayout, 0, iStart++, 1, 1); - - pbNow = new QPushButton(QApplication::translate("LtsDateTimeEdit", "Now", nullptr)); - pbNow->setObjectName(QStringLiteral("pbNow")); - gridLayout->addWidget(pbNow, 0, iStart++, 1, 1); - - QWidget::setTabOrder(checkBox, leLongDay); - QWidget::setTabOrder(leLongDay, leLongHHMMSS); - QWidget::setTabOrder(leLongHHMMSS, leLongTZ); - QWidget::setTabOrder(leLongTZ, leLongMonth); - QWidget::setTabOrder(leLongMonth, leLongYear); - QWidget::setTabOrder(leLongYear, pbNow); - - parent->setFocusProxy(leLongDay); - } - - static QPushButton *addButton(const QString &text, QWidget *parent) - { - QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - auto pb = new QPushButton(parent); - pb->setMinimumWidth(27); - pb->setMaximumWidth(27); - pb->setText(text); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pb->sizePolicy().hasHeightForWidth()); - pb->setSizePolicy(sizePolicy); - return pb; - } - - void addButtons(QWidget *parent) - { - pbBBB = addButton(QLatin1String("<<<"), parent); - pbBB = addButton(QLatin1String("<<"), parent); - pbB = addButton(QLatin1String("<"), parent); - gridLayout->addWidget(pbBBB, 0, 0, 1, 1); - gridLayout->addWidget(pbBB, 0, 1, 1, 1); - gridLayout->addWidget(pbB, 0, 2, 1, 1); - - int iStart = 5; - pbF = addButton(QLatin1String(">"), parent); - pbFF = addButton(QLatin1String(">>"), parent); - pbFFF = addButton(QLatin1String(">>>"), parent); - gridLayout->addWidget(pbF, 0, iStart++, 1, 1); - gridLayout->addWidget(pbFF, 0, iStart++, 1, 1); - gridLayout->addWidget(pbFFF, 0, iStart++, 1, 1); - } - - void addSlider(QWidget *parent) - { - slTime = new QSlider(parent); - slTime->setOrientation(Qt::Horizontal); - gridLayout->addWidget(slTime, 1, 0, 1, 7); - } -}; -} - - -namespace LtsWidgets -{ - - - - -void setItemText(LtsWidgets::LineEdit *lineEdit, const QString &text) -{ - if (!lineEdit) - return; - lineEdit->setText(text); -} -void setItemText(QComboBox *comboxBox, const QString &text) -{ - if (!comboxBox) - return; - comboxBox->setCurrentIndex(comboxBox->findText(text)); -} -void setItemData(QComboBox *comboxBox, int data) -{ - if (!comboxBox) - return; - comboxBox->setCurrentIndex(comboxBox->findData(data)); -} -void setItemValue(QSpinBox *spinBox, int value) -{ - if (!spinBox) - return; - spinBox->setValue(value); -} - - -QString getItemText(LtsWidgets::LineEdit *lineEdit) -{ - if (!lineEdit) - return {}; - return lineEdit->text(); -} -QString getItemText(QComboBox *comboxBox) -{ - if (!comboxBox || (comboxBox->count() <= 0)) - return {}; - const QString &curTest = comboxBox->currentText(); - if (curTest.isEmpty()) - return {}; - return curTest.at(0).toUpper(); -} -int getItemData(QComboBox *comboxBox) -{ - if (!comboxBox || (comboxBox->count() <= 0)) - return {}; - int data = comboxBox->currentData().toInt(); - return data; -} -int getItemValue(QSpinBox *spinBox) -{ - if (!spinBox) - return 0; - return spinBox->value(); -} - - - - - - -LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool withCheckBox) - : QWidget(parent) - - , m_scFixed(new QShortcut(QKeySequence(Qt::ShiftModifier + Qt::Key_F2), this, SLOT(setFixedDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_scOffset(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(setOffsetDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_withCheckBox(withCheckBox) - , m_withNowButton(withNowButton) -{ - -#ifdef TABLET_MODE - - setupUiTouch(); - return; -#else - - m_uiLe = new Ui::LtsDateTimeEditLe; - m_uiLe->setupUi(this); - - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - setFocusPolicy(Qt::StrongFocus); - setContentsMargins(QMargins()); - - QCompleter *completer = new QCompleter(DTG::months(), this); - completer->setCaseSensitivity(Qt::CaseInsensitive); - completer->setCompletionMode(QCompleter::InlineCompletion); - - QRegularExpression rx(DTG::months().join(QLatin1Char('|'))); - QRegularExpressionValidator *val = new QRegularExpressionValidator(rx, this); - m_uiLe->leLongMonth->setCompleter(completer); - m_uiLe->leLongMonth->setValidator(val); - - connect(m_uiLe->leLongDay, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueDayChanged); - connect(m_uiLe->leLongMonth, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueMonthChanged2); - connect(m_uiLe->leLongTZ, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueTimezoneChangedStr); - connect(m_uiLe->leLongYear, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueYearChanged); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutHHMMSS); - connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutDay); - connect(m_uiLe->leLongMonth, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutMonth); - connect(m_uiLe->leLongTZ, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutTZ); - connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutYear); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongMonth, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongTZ, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); - - connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onHmsWheelEvent); - connect(m_uiLe->leLongDay, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onDayWheelEvent); - connect(m_uiLe->leLongMonth, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onMonthWheelEvent); - connect(m_uiLe->leLongYear, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onYearWheelEvent); - - connect(m_uiLe->checkBox, &QCheckBox::toggled, this, &LtsDateTimeEdit::onCheckBoxChanged); - connect(m_uiLe->pbNow, &QPushButton::clicked, this, &LtsDateTimeEdit::setOffsetDateTime); - - setTimezone(DTG::timezone()); - - bool temp = m_withCheckBox; - m_withCheckBox = !m_withCheckBox; - setWithCheckBox(temp); - - temp = m_withNowButton; - m_withNowButton = !m_withNowButton; - setWithNowButton(temp); - - onCheckBoxChanged(); -#endif -} - -LtsDateTimeEdit::~LtsDateTimeEdit() -{ - delete m_uiLe; -} - -bool LtsDateTimeEdit::setupUiTouch() -{ - m_uiTouch = new Ui::LtsDateTimeEdit; - m_uiTouch->setupUi(this); - setFocusPolicy(Qt::StrongFocus); - - for (int i = 65; i < 91; i++ ) - { - if (QChar(i).toUpper() == QChar::fromLatin1('I')) - continue; - m_uiTouch->cbxTimeZone->addItem(QChar(i), QChar(i)); - } - - connect(m_uiTouch->pbNow, &QPushButton::clicked, - this, &LtsDateTimeEdit::setOffsetDateTime); - connect(m_uiTouch->cbxMonth, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spDay, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spHour, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spMinutes, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spSeconds, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - connect(m_uiTouch->spYear, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::dateTimeChanged); - - connect(m_uiTouch->checkBox, &QCheckBox::toggled, - this, &LtsDateTimeEdit::onCheckBoxChanged); - connect(m_uiTouch->cbxTimeZone, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::valueTimezoneChangedInt); - - - - connect(m_uiTouch->cbxMonth, &QComboBox::currentTextChanged, - this, &LtsDateTimeEdit::valueMonthChanged); - - - setMinMaxMonth(1,12); - - setTimezone(DTG::timezone()); - - - bool temp = m_withCheckBox; - m_withCheckBox = !m_withCheckBox; - setWithCheckBox(temp); - - temp = m_withNowButton; - m_withNowButton = !m_withNowButton; - setWithNowButton(temp); - - onCheckBoxChanged(); - - - return true; -} - - -void LtsDateTimeEdit::setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb) -{ - m_getCurrentTimeCallback = cb; -} - -void LtsDateTimeEdit::setEditTimezone(const bool edit) -{ - m_editTimezone = edit; - if (m_uiLe) - m_uiLe->leLongTZ->setEnabled(m_editTimezone); - if (m_uiTouch) - m_uiTouch->cbxTimeZone->setEnabled(m_editTimezone); -} - -void LtsDateTimeEdit::setWithButtons(const bool buttons) -{ - m_withButtons = buttons; - if (buttons) - { - if (!m_uiLe->pbBBB) - { - m_uiLe->addButtons(this); - connect(m_uiLe->pbBBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackHourClicked); - connect(m_uiLe->pbBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackMinuteClicked); - connect(m_uiLe->pbB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackSecondClicked); - connect(m_uiLe->pbF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardSecondClicked); - connect(m_uiLe->pbFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardMinuteClicked); - connect(m_uiLe->pbFFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardHourClicked); - disableControls(); - } - } - else - { - delete m_uiLe->pbBBB; - delete m_uiLe->pbBB; - delete m_uiLe->pbB; - delete m_uiLe->pbFFF; - delete m_uiLe->pbFF; - delete m_uiLe->pbF; - - m_uiLe->pbBBB = nullptr; - m_uiLe->pbBB = nullptr; - m_uiLe->pbB = nullptr; - m_uiLe->pbFFF = nullptr; - m_uiLe->pbFF = nullptr; - m_uiLe->pbF = nullptr; - } -} - -void LtsDateTimeEdit::setWithSlider(const bool slider) -{ - m_withSlider = slider; - - if (!m_uiLe) - return; - - if (slider) - { - m_uiLe->addSlider(this); - m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000); - m_uiLe->slTime->setMaximum(m_maxTimeMSec > 0 ? m_maxTimeMSec / 1000 : time(nullptr)); - connect(m_uiLe->slTime, &QSlider::sliderMoved, this, &LtsDateTimeEdit::setSliderValue); - disableControls(); - } - else - { - delete m_uiLe->slTime; - m_uiLe->slTime = nullptr; - } -} - -void LtsDateTimeEdit::setReadOnly(const bool readOnly) -{ - if (m_readOnly == readOnly) - return; - m_readOnly = readOnly; - - if (m_uiLe) - { - m_uiLe->leLongDay->setReadOnly(m_readOnly); - m_uiLe->leLongHHMMSS->setReadOnly(m_readOnly); - m_uiLe->leLongMonth->setReadOnly(m_readOnly); - m_uiLe->leLongTZ->setReadOnly(m_readOnly); - m_uiLe->leLongYear->setReadOnly(m_readOnly); - } - if (m_uiTouch) - { - m_uiTouch->spDay->setReadOnly(m_readOnly); - m_uiTouch->spHour->setReadOnly(m_readOnly); - m_uiTouch->spMinutes->setReadOnly(m_readOnly); - m_uiTouch->spSeconds->setReadOnly(m_readOnly); - m_uiTouch->spYear->setReadOnly(m_readOnly); - m_uiTouch->cbxTimeZone->setEnabled(m_readOnly); - } -} - -void LtsDateTimeEdit::setWithCheckBox(bool checkBoxEnabled) -{ - if (m_withCheckBox == checkBoxEnabled) - return; - - m_withCheckBox = checkBoxEnabled; - if (m_uiLe) - { - m_uiLe->checkBox->setVisible(m_withCheckBox); - if (!m_withCheckBox) - m_uiLe->checkBox->setChecked(true); - } - if (m_uiTouch) - { - m_uiTouch->checkBox->setVisible(m_withCheckBox); - if (!m_withCheckBox) - m_uiTouch->checkBox->setChecked(true); - } -} - -void LtsDateTimeEdit::setCheckBoxChecked(bool bChecked) -{ - if (!m_uiLe) - return; - m_uiLe->checkBox->setChecked(bChecked); -} - -bool LtsDateTimeEdit::isCheckBoxChecked() const -{ - if (!m_uiLe) - return false; - return m_uiLe->checkBox->isChecked(); -} - -void LtsDateTimeEdit::setWithNowButton(bool nowButton) -{ - if (m_withNowButton == nowButton) - return; - - m_withNowButton = nowButton; - if (m_uiLe) - m_uiLe->pbNow->setVisible(m_withNowButton); - if (m_uiTouch) - m_uiTouch->pbNow->setVisible(m_withNowButton); -} - -bool LtsDateTimeEdit::setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible) -{ - if (msec == 0 && m_withCheckBox) - { - if (m_uiLe) - m_uiLe->checkBox->setChecked(false); - m_dateIsAcceptable = true; - return true; - } - if (m_uiLe) - m_uiLe->checkBox->setChecked(true); - - m_dateIsAcceptable = false; - if (msec == 0) - return false; - - - - - - - if (!setToNearestPossible) - { - if (!isDataAcceptable()) - return false; - } - else - { - if (msec < m_minTimeMSec && m_minTimeMSec > 0) - msec = m_minTimeMSec; - if (msec > m_maxTimeMSec && m_maxTimeMSec > 0) - msec = m_maxTimeMSec; - } - - m_curTimeMSec = msec; - - const qint64 offsetTimeZoneMsec = getTimezoneAsSecs() * 1000; - const QDateTime datetime(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec + offsetTimeZoneMsec, Qt::UTC)); - const QDate date(datetime.date()); - const QTime time(datetime.time()); - - const int day = date.day(); - const int month = date.month(); - const int year = date.year(); - const int hour = time.hour(); - const int minute = time.minute(); - const int second = time.second(); - - - if (!setYear(year) || - !setMonth(month) || - !setDay(day) || - !setHHMMSS(hour, minute, second)) - return false; - - if (!isDataAcceptable()) - return false; - - enableControls(); - - emit dateTimeChanged(); - - return true; -} - - -qint64 LtsDateTimeEdit::msecsSinceEpoch() const -{ - if (isEmpty()) - return 0; - - if (m_uiLe && !m_uiLe->checkBox->isChecked()) - return 0; - if (m_uiTouch && !m_uiTouch->checkBox->isChecked()) - return 0; - - auto that = const_cast(this); - if (!that->isDataAcceptable()) - return 0; - - return m_curTimeMSec; -} - -bool LtsDateTimeEdit::setTimezone(char tz) -{ - tz = toupper(tz); - if (!DTG::isTimezoneValid(tz)) - return false; - - if (m_uiLe) - setItemText(m_uiLe->leLongTZ, QString(QLatin1Char(tz))); - if (m_uiTouch) - setItemText(m_uiTouch->cbxTimeZone, QString(QLatin1Char(tz))); - - setMsecsSinceEpoch(m_curTimeMSec); - - return true; -} - -char LtsDateTimeEdit::getTimezone() const -{ - QString str; - if (m_uiLe) - str = getItemText(m_uiLe->leLongTZ); - if (m_uiTouch) - str = getItemText(m_uiTouch->cbxTimeZone); - - if (str.isEmpty()) - return DTG::timezone(); - return str[0].toUpper().toLatin1(); -} - -qint64 LtsDateTimeEdit::getTimezoneAsSecs() const -{ - const char tzGUI = getTimezone(); - return DTG::timezone2Secs(tzGUI); -} - -void LtsDateTimeEdit::clearTimezone() -{ - if (m_uiLe) - m_uiLe->leLongTZ->clear(); - if (m_uiTouch) - m_uiTouch->cbxTimeZone->setCurrentIndex(0); -} - -void LtsDateTimeEdit::setMinMaxMonth(int minMonth, int maxMonth) -{ - if (!m_uiTouch) - return; - - if (m_minMonth != minMonth) - m_minMonth = minMonth; - if (m_maxMonth != maxMonth) - m_maxMonth = maxMonth; - - int currentMonth = m_uiTouch->cbxMonth->currentData().toInt(); - m_uiTouch->cbxMonth->blockSignals(true); - m_uiTouch->cbxMonth->clear(); - for (int i =1; i<= 12 ; ++i) - { - if (i < m_minMonth) - continue; - if (i > m_maxMonth) - break; - - const QStringList monthSl = DTG::months(); - m_uiTouch->cbxMonth->addItem(monthSl[i-1], i); - - - - } - - - m_uiTouch->cbxMonth->setCurrentIndex(m_uiTouch->cbxMonth->findData(currentMonth)); - m_uiTouch->cbxMonth->blockSignals(false); -} - - -void LtsDateTimeEdit::setSubWidgetsProperties(const QString &name, const QVariant &value) -{ - if (m_uiLe) - { - m_uiLe->leLongDay->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongHHMMSS->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongMonth->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongYear->setProperty(name.toStdString().c_str(), value); - m_uiLe->leLongTZ->setProperty(name.toStdString().c_str(), value); - } - if (m_uiTouch) - { - m_uiTouch->spDay->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spHour->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spMinutes->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spSeconds->setProperty(name.toStdString().c_str(), value); - m_uiTouch->spYear->setProperty(name.toStdString().c_str(), value); - } -} - -bool LtsDateTimeEdit::setMonth(int month) -{ - const auto months(DTG::months()); - if (month < 1 || month > months.size()) - return false; - - if (m_uiLe) - setItemText(m_uiLe->leLongMonth, months.at(month - 1)); - if (m_uiTouch) - setItemData(m_uiTouch->cbxMonth, month); - return true; -} - -int LtsDateTimeEdit::getMonth() const -{ - if (m_uiLe) - { - const QString str = getItemText(m_uiLe->leLongMonth); - if (str.length() != 3) - return -1; - - int idx = DTG::months().indexOf(str); - if (idx < 0) - return -1; - return idx + 1; - } - if (m_uiTouch) - { - int month = getItemData(m_uiTouch->cbxMonth); - return month; - } - return 0; -} - -bool LtsDateTimeEdit::setDay(int day) -{ - if (day < 1 || day > 31) - return false; - - QString oldDay; - if (m_uiLe) - oldDay = m_uiLe->leLongDay->text(); - int oldDayInt = 0; - if (m_uiTouch) - oldDayInt = getItemValue(m_uiTouch->spDay); - - if (m_uiLe) - setItemText(m_uiLe->leLongDay, QStringLiteral("%1").arg(day, 2, 10, QLatin1Char('0'))); - if (m_uiTouch) - setItemValue(m_uiTouch->spDay, day); - - if (!isDayAcceptable()) - { - if (m_uiLe) - setItemText(m_uiLe->leLongDay, oldDay); - if (m_uiTouch) - setItemValue(m_uiTouch->spDay, oldDayInt); - return false; - } - - return true; -} - -int LtsDateTimeEdit::getDay() const -{ - if (m_uiLe) - return m_uiLe->leLongDay->getValue(); - if (m_uiTouch) - return getItemValue(m_uiTouch->spDay); - return 0; -} - -bool LtsDateTimeEdit::setHHMMSS(int hour, int minute, int second) -{ - if (hour < 0 || hour > 23 || - minute < 0 || minute > 59 || - second < 0 || second > 59) - return false; - - if (m_uiLe) - m_uiLe->leLongHHMMSS->setValue(hour * 10000 + minute * 100 + second); - if (m_uiTouch) - { - setItemValue(m_uiTouch->spHour , hour); - setItemValue(m_uiTouch->spMinutes, minute); - setItemValue(m_uiTouch->spSeconds, second); - } - - return true; -} - -int LtsDateTimeEdit::getHour() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return time / 10000; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spHour); - } - return 0; -} - -int LtsDateTimeEdit::getMinute() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return (time / 100) % 100; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spMinutes); - } - return 0; -} - -int LtsDateTimeEdit::getSecond() const -{ - if (m_uiLe) - { - const int time = m_uiLe->leLongHHMMSS->getValue(); - return time % 100; - } - if (m_uiTouch) - { - return getItemValue(m_uiTouch->spSeconds); - } - return 0; -} - -bool LtsDateTimeEdit::setYear(int year) -{ - int finalYear = -1; - if (year >= 0 && year <= 99) - finalYear = year; - else if (year >= 2000 && year <= 2099) - finalYear = year - 2000; - - if (finalYear >= 0) - { - if (m_uiLe) - m_uiLe->leLongYear->setValue(finalYear); - if (m_uiTouch) - setItemValue(m_uiTouch->spYear, finalYear); - return true; - } - - return false; -} - -int LtsDateTimeEdit::getYear() const -{ - if (m_uiLe) - return 2000 + m_uiLe->leLongYear->getValue(); - if (m_uiTouch) - return 2000 + m_uiTouch->spYear->value(); - return 2000; -} - -bool LtsDateTimeEdit::isEmpty() const -{ - return (isEmptyDay() || - isEmptyHHMMSS() || - isEmptyMonth() || - isEmptyTimezone() || - isEmptyYear()); -} - -bool LtsDateTimeEdit::isValidDate() const -{ - return m_dateIsAcceptable; -} - - -bool LtsDateTimeEdit::isEmptyDay() const -{ - if (m_uiLe) - return m_uiLe->leLongDay->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyHHMMSS() const -{ - if (m_uiLe) - return m_uiLe->leLongHHMMSS->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyMonth() const -{ - if (m_uiLe) - return m_uiLe->leLongMonth->text().isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyTimezone() const -{ - if (m_uiLe) - return m_uiLe->leLongTZ->text().isEmpty(); - return false; -} - -bool LtsDateTimeEdit::isEmptyYear() const -{ - if (m_uiLe) - return m_uiLe->leLongYear->isEmpty(); - return false; -} - -bool LtsDateTimeEdit::hasFocus() const -{ - if (!m_uiLe) - return false; - return (m_uiLe->leLongDay->hasFocus() || - m_uiLe->leLongHHMMSS->hasFocus() || - m_uiLe->leLongMonth->hasFocus() || - m_uiLe->leLongTZ->hasFocus() || - m_uiLe->leLongYear->hasFocus()); -} - -bool LtsDateTimeEdit::isDataAcceptable() -{ - m_dateIsAcceptable = false; - - bool state = - isDayAcceptable() && - isHHMMSSAcceptable() && - isMonthAcceptable() && - isYearAcceptable() && - QDate::isValid(getYear(), getMonth(), getDay()) && - QTime::isValid(getHour(), getMinute(), getSecond()); - - if (m_editTimezone && state) - state = isTzAcceptable(); - - showInvalidData(false); - - if (!state) - { - disableControls(); - return false; - } - - qint64 msec = QDateTime(QDate(getYear(), getMonth(), getDay()), QTime(getHour(), getMinute(), getSecond()), Qt::UTC).toMSecsSinceEpoch(); - msec -= DTG::timezone2Secs(getTimezone()) * 1000LL; - if (m_minTimeMSec > 0 && m_maxTimeMSec > 0 && m_minTimeMSec < m_maxTimeMSec) - { - if (msec < m_minTimeMSec || msec > m_maxTimeMSec) - return false; - } - - m_curTimeMSec = msec; - enableControls(); - m_dateIsAcceptable = true; - return true; -} - -bool LtsDateTimeEdit::isDayAcceptable() const -{ - int value = getDay(); - int max = 31; - if (isMonthAcceptable()) - { - const int month = getMonth(); - switch (month) - { - case 2: - { - max = 28; - if (isYearAcceptable()) - { - if (getYear() % 4 == 0 && (getYear() % 100 != 0 || getYear() % 400 == 0)) - - max = 29; - } - break; - } - case 4: - case 6: - case 9: - case 11: - max = 30; - break; - default: - break; - } - } - return (value > 0 && value <= max); -} - -bool LtsDateTimeEdit::isHHMMSSAcceptable() const -{ - int hh = getHour(); - if (hh < 0 || hh > 23) - return false; - - int mm = getMinute(); - if (mm < 0 || mm > 59) - return false; - - int ss = getSecond(); - if (ss < 0 || ss > 59) - return false; - - return true; -} - -bool LtsDateTimeEdit::isMonthAcceptable() const -{ - int value = getMonth(); - return (value > 0 && value <= 12); -} - -bool LtsDateTimeEdit::isYearAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongYear); - return true; -} - -bool LtsDateTimeEdit::isTzAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongTZ); - return true; -} - -void LtsDateTimeEdit::showInvalidData(bool setFocus) -{ - if (!m_uiLe) - return; - - bool focus = setFocus; - - auto check = [&](QLineEdit * le, bool isValid) - { - if (isValid) - le->setStyleSheet(QString()); - else - { - le->setStyleSheet(QLatin1String("background-color: yellow;")); - if (focus) - { - le->setFocus(); - focus = false; - } - } - }; - - check(m_uiLe->leLongDay, isDayAcceptable()); - check(m_uiLe->leLongHHMMSS, isHHMMSSAcceptable()); - if (m_editTimezone) - check(m_uiLe->leLongTZ, isTzAcceptable()); - check(m_uiLe->leLongMonth, isMonthAcceptable()); - check(m_uiLe->leLongYear, isYearAcceptable()); -} - -bool LtsDateTimeEdit::isLineEditDataAcceptable(QLineEdit *le) const -{ - const QValidator *pValidator = le->validator(); - - if (!pValidator) - return true; - - QString text = le->text(); - - int pos; - QValidator::State state = pValidator->validate(text, pos); - - if (state == QValidator::Acceptable || state == QValidator::Intermediate) - return true; - - return false; -} - -void LtsDateTimeEdit::setFixedDateTime() -{ - setMsecsSinceEpoch(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("FixedTime"), 0).toLongLong()); -} - -void LtsDateTimeEdit::setOffsetDateTime() -{ - if (!m_getCurrentTimeCallback) - return; - - qint64 offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong(); - - setMsecsSinceEpoch((m_getCurrentTimeCallback() / 1000) * 1000 + offset); -} - -void LtsDateTimeEdit::setSliderValue(int val) -{ - setMsecsSinceEpoch(val * 1000LL); -} - -void LtsDateTimeEdit::valueDayChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongDay->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString tag(str); - - if (tag.length() > 2) - { - tag = tag.left(2); - changed = true; - } - - bool ok; - int t = tag.toInt(&ok, 10); - - if (ok) - { - if (t > 31) - { - tag.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - if (changed) - { - m_uiLe->leLongDay->setText(tag); - m_uiLe->leLongDay->setCursorPosition(cp); - } - - if (tag.length() == 2) - { - showInvalidData(false); - - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::fixupHHMMSS() -{ - if (!m_uiLe) - return; - - int curpos = m_uiLe->leLongHHMMSS->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString uhrzeit(m_uiLe->leLongHHMMSS->text()); - - - - - - - QString hh; - QString mm; - QString ss; - - if (uhrzeit.length() > 1) - hh = uhrzeit.left(2); - else - hh = uhrzeit + QLatin1Char('0'); - - if (uhrzeit.length() > 0) - { - if (uhrzeit.length() == 1) - hh = uhrzeit.mid(0, 1) + QLatin1Char('0'); - else if (uhrzeit.length() >= 2) - { - hh = uhrzeit.mid(0, 2); - if (uhrzeit.length() > 2) - { - if (uhrzeit.length() == 3) - mm = uhrzeit.mid(2, 1) + QLatin1Char('0'); - else if (uhrzeit.length() >= 4) - { - mm = uhrzeit.mid(2, 2); - if (uhrzeit.length() == 5) - ss = uhrzeit.mid(4, 1) + QLatin1Char('0'); - else if (uhrzeit.length() == 6) - ss = uhrzeit.mid(4, 2); - } - } - } - } - - bool ok; - - int h = hh.toInt(&ok, 10); - if (ok) - { - if (h > 23) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - int m = mm.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2) && (m > 59)) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - int s = ss.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2 && mm.length() == 2) && (s > 59)) - { - uhrzeit.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - } - - if (changed) - { - m_uiLe->leLongHHMMSS->setText(uhrzeit); - m_uiLe->leLongHHMMSS->setCursorPosition(cp); - } - - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::valueMonthChanged2(const QString &str) -{ - if (!m_uiLe) - return; - if (str.length() == 3 && m_uiLe->leLongMonth->selectedText().isEmpty()) - focusNextPrevChild(true); -} - -void LtsDateTimeEdit::valueMonthChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongMonth->cursorPosition(); - int cp = curpos; - - bool changed = false; - - QString monStr = str; - - const auto months = DTG::months(); - for (int i = 0; i < months.size(); ++i) - { - const QString &mon = months.at(i); - if (mon.startsWith(monStr, Qt::CaseInsensitive)) - { - monStr = mon; - changed = true; - break; - } - } - - if (!changed) - { - monStr.remove(curpos - 1, 1); - if (cp >= curpos) - cp--; - changed = true; - } - - if (changed) - { - m_uiLe->leLongMonth->setText(monStr); - - - if (cp > 0) - m_uiLe->leLongMonth->setCursorPosition(cp); - else - m_uiLe->leLongMonth->setCursorPosition(1); - } - - if (monStr.size() == 3) - { - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::valueTimezoneChangedStr(const QString &str) -{ - if (str.length() > 1) - { - clearTimezone(); - return; - } - if (str.length() == 0) - return; - QChar c = str.at(0); - if (!c.isLetter() || c.toUpper() == QLatin1Char('J')) - { - clearTimezone(); - return; - } - if (c.isLower()) - setTimezone(c.toUpper().toLatin1()); - - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); -} - -void LtsDateTimeEdit::valueTimezoneChangedInt(int newTimezone) -{ - Q_UNUSED(newTimezone); - if (!m_uiTouch) - return; - const QString &curText = m_uiTouch->cbxTimeZone->currentText(); - if (curText.isEmpty()) - return; - - const QChar tz = curText.at(0); - setTimezone(tz.toUpper().toLatin1()); -} - -void LtsDateTimeEdit::valueYearChanged(const QString &str) -{ - if (!m_uiLe) - return; - int curpos = m_uiLe->leLongYear->cursorPosition(); - int cp = curpos; - - bool changed = false; - - const QString &year = str; - - QString yy; - if (year.length() > 2) - { - yy = year.left(2); - changed = true; - } - - if (changed) - { - m_uiLe->leLongYear->setText(yy); - m_uiLe->leLongYear->setCursorPosition(cp); - } - - if (year.length() == 2) - { - showInvalidData(false); - if (m_autoForward) - focusNextPrevChild(true); - } -} - -void LtsDateTimeEdit::onBackHourClicked() -{ - if (!setMsecsSinceEpoch(m_curTimeMSec - s_oneHourMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onBackMinuteClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneMinuteMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onBackSecondClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneSecondMSec)) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::onForwardSecondClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneSecondMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onForwardMinuteClicked() -{ - const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; - if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneMinuteMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onForwardHourClicked() -{ - if (!setMsecsSinceEpoch(m_curTimeMSec + s_oneHourMSec)) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::onCheckBoxChanged() -{ - QVector widgets; - bool bEnable = false; - - if (m_uiLe) - { - widgets = {m_uiLe->leLongDay, m_uiLe->leLongHHMMSS, m_uiLe->leLongTZ, - m_uiLe->leLongMonth, m_uiLe->leLongYear}; - bEnable = m_uiLe->checkBox->isChecked(); - } - - if (m_uiTouch) - { - widgets = {m_uiTouch->spDay, m_uiTouch->spHour, m_uiTouch->spMinutes, m_uiTouch->spSeconds, m_uiTouch->spYear, m_uiTouch->cbxMonth, m_uiTouch->cbxTimeZone, m_uiTouch->pbNow}; - bEnable = m_uiTouch->checkBox->isChecked(); - } - - for (auto w : widgets) - { - if (w) - w->setEnabled(bEnable); - } - enaDisaControls(bEnable); -} - -void LtsDateTimeEdit::disableControls() -{ - enaDisaControls(false); -} - -void LtsDateTimeEdit::enableControls() -{ - if (!m_uiLe) - return; - - enaDisaControls(true); - if (m_withSlider && - m_minTimeMSec != 0 && - m_maxTimeMSec > 0 && - m_maxTimeMSec > m_minTimeMSec) - { - m_uiLe->slTime->setEnabled(true); - m_uiLe->slTime->setValue(m_curTimeMSec / 1000LL); - } -} - -void LtsDateTimeEdit::enaDisaControls(bool bEnable) -{ - if (!m_uiLe) - return; - const QVector widgets({m_uiLe->pbBBB, m_uiLe->pbBB, m_uiLe->pbB, - m_uiLe->pbF, m_uiLe->pbFF, m_uiLe->pbFFF, - m_uiLe->slTime}); - for (auto w : widgets) - { - if (w) - w->setEnabled(bEnable); - } -} - -void LtsDateTimeEdit::setMinimumTimeMSec(qint64 msec) -{ - if (m_minTimeMSec == msec) - return; - m_minTimeMSec = msec; - if (m_minTimeMSec > 0 && m_uiLe && m_uiLe->slTime) - { - m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000LL); - if (m_maxTimeMSec > m_minTimeMSec) - m_uiLe->slTime->setEnabled(true); - } - if (m_curTimeMSec < m_minTimeMSec) - setMsecsSinceEpoch(m_minTimeMSec); -} - -void LtsDateTimeEdit::setMaximumTimeMSec(qint64 msec) -{ - if (m_maxTimeMSec == msec) - return; - m_maxTimeMSec = msec; - if (!m_uiLe || !m_uiLe->slTime) - return; - - m_uiLe->slTime->setMaximum(m_maxTimeMSec / 1000LL); - if (m_minTimeMSec > 0 && m_maxTimeMSec > m_minTimeMSec) - m_uiLe->slTime->setEnabled(true); - if (m_maxTimeMSec > 0 && m_curTimeMSec > m_maxTimeMSec) - setMsecsSinceEpoch(m_maxTimeMSec); -} - -void LtsDateTimeEdit::clear() -{ - if (m_uiLe) - { - m_uiLe->leLongDay->clear(); - m_uiLe->leLongHHMMSS->clear(); - m_uiLe->leLongMonth->clear(); - m_uiLe->leLongYear->clear(); - m_uiLe->leLongTZ->clear(); - m_curTimeMSec = 0; - } - if (m_uiTouch) - { - m_uiTouch->spDay->clear(); - m_uiTouch->spHour->clear(); - m_uiTouch->spMinutes->clear(); - m_uiTouch->spSeconds->clear(); - m_uiTouch->spYear->clear(); - } - emit dateTimeChanged(); - -} - -void LtsDateTimeEdit::onFocusOutDay() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongDay->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setDay(dt.date().day()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutHHMMSS() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongHHMMSS->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyDay() || - !isEmptyMonth() || - !(isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setHHMMSS(dt.time().hour(), dt.time().minute(), dt.time().second()); - } - } - } - value += QString(6 - value.length(), QLatin1Char('0')); - m_uiLe->leLongHHMMSS->setText(value); - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutTZ() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongTZ->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - !isEmptyDay() || - !isEmptyYear()) - setTimezone(DTG::timezone()); - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutMonth() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongMonth->text(); - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyDay() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyYear()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setMonth(dt.date().month()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutYear() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - - if (!m_recursionAvoid && !hasFocus()) - onFocusOut(); - - QString value = m_uiLe->leLongYear->text(); - - if (value.length() == 1) - { - value = QLatin1Char('0') + value; - m_uiLe->leLongYear->setText(value); - } - - if (value.length() == 0 && !m_backspacePressed) - { - if (!isEmptyHHMMSS() || - !isEmptyMonth() || - (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || - !isEmptyDay()) - { - if (m_getCurrentTimeCallback) - { - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); - setYear(dt.date().year()); - } - } - } - isDataAcceptable(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onFocusOutBackspace() -{ - m_backspacePressed = true; -} - -void LtsDateTimeEdit::onFocusOut() -{ - if (!m_uiLe) - return; - if (m_backspacePressed) - { - m_backspacePressed = false; - return; - } - m_recursionAvoid = true; - onFocusOutDay(); - onFocusOutHHMMSS(); - onFocusOutMonth(); - onFocusOutTZ(); - onFocusOutYear(); - m_recursionAvoid = false; -} - -void LtsDateTimeEdit::onHmsWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (m_uiLe->leLongHHMMSS->text().size() != 6) - return; - - fixupHHMMSS(); - - int selStart = 0; - switch (cursorPos) - { - case 0: - case 1: - case 2: - if (bForward) - onForwardHourClicked(); - else - onBackHourClicked(); - selStart = 0; - break; - case 3: - case 4: - if (bForward) - onForwardMinuteClicked(); - else - onBackMinuteClicked(); - selStart = 2; - break; - case 5: - case 6: - if (bForward) - onForwardSecondClicked(); - else - onBackSecondClicked(); - selStart = 4; - break; - default: - return; - } - m_uiLe->leLongHHMMSS->setCursorPosition(cursorPos); - m_uiLe->leLongHHMMSS->setSelection(selStart, 2); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onDayWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isDayAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneDayMSec, true); - m_uiLe->leLongDay->setCursorPosition(cursorPos); - m_uiLe->leLongDay->setSelection(0, 2); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onMonthWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isMonthAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addMonths(ofs)); - setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); - m_uiLe->leLongMonth->setCursorPosition(cursorPos); - m_uiLe->leLongMonth->setSelection(0, 3); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::onYearWheelEvent(bool bForward, int cursorPos) -{ - if (!m_uiLe) - return; - if (!isYearAcceptable()) - return; - const int ofs = bForward ? 1 : -1; - const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addYears(ofs)); - setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); - m_uiLe->leLongYear->setCursorPosition(cursorPos); - m_uiLe->leLongYear->setSelection(0, 2); - emit dateTimeChanged(); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h b/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h deleted file mode 100755 index 5965d37f9..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h +++ /dev/null @@ -1,206 +0,0 @@ - - - -#pragma once - - -#include "lts_widgets_export.h" - -#include -#include - - -namespace Ui -{ -class LtsDateTimeEditLe; -class LtsDateTimeEdit; -} - -class QLineEdit; -class QShortcut; - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward) - Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone) - Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons) - Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider) - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) - Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton) - Q_PROPERTY(bool withCheckBox READ withCheckBox WRITE setWithCheckBox) - -public: - - using CurrentTimeCallback = std::function; - - explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool withCheckBox = false); - ~LtsDateTimeEdit(); - - bool setupUiTouch(); - - - qint64 msecsSinceEpoch() const; - bool isEmpty() const; - bool isValidDate() const; - - void setAutoForward(const bool af) { m_autoForward = af; } - bool getAutoForward() const { return m_autoForward; } - - void setEditTimezone(const bool edit); - bool getEditTimezone() const { return m_editTimezone; } - - void setWithButtons(const bool buttons); - bool getWithButtons() const { return m_withButtons; } - - void setWithSlider(const bool slider); - bool getWithSlider() const { return m_withSlider; } - - void setReadOnly(const bool readOnly); - bool isReadOnly() const { return m_readOnly; } - - void setWithCheckBox(bool checkBoxEnabled); - bool withCheckBox() const { return m_withCheckBox; } - void setCheckBoxChecked(bool bChecked); - bool isCheckBoxChecked() const; - - void setWithNowButton(bool nowButton); - bool withNowButton() const { return m_withNowButton; } - - - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const { return m_minTimeMSec; } - - - void setMaximumTimeMSec(qint64 msec); - qint64 maximumTimeMSec() const { return m_maxTimeMSec; } - - void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); } - - void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb); - - bool setTimezone(char tz); - char getTimezone() const; - qint64 getTimezoneAsSecs() const; - - void setSubWidgetsProperties(const QString &name, const QVariant &value); - -Q_SIGNALS: - void dateTimeChanged(); - -public Q_SLOTS: - bool setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible = true); - void clear(); - -private: - void clearTimezone(); - - void setMinMaxMonth(int minMonth, int maxMonth); - - bool setMonth(int month); - int getMonth() const; - - bool setDay(int day); - int getDay() const; - - bool setHHMMSS(int hour, int minute, int second); - int getHour() const; - int getMinute() const; - int getSecond() const; - - bool setYear(int year); - int getYear() const; - - bool isEmptyDay() const; - bool isEmptyHHMMSS() const; - bool isEmptyTimezone() const; - bool isEmptyMonth() const; - bool isEmptyYear() const; - - bool hasFocus() const; - - bool isDataAcceptable(); - - void showInvalidData(bool setFocus); - - bool isLineEditDataAcceptable(QLineEdit *le) const; - - bool isDayAcceptable() const; - bool isHHMMSSAcceptable() const; - bool isMonthAcceptable() const; - bool isYearAcceptable() const; - bool isTzAcceptable() const; - -private Q_SLOTS: - void setFixedDateTime(); - void setOffsetDateTime(); - - void setSliderValue(int val); - void valueDayChanged(const QString &str); - void fixupHHMMSS(); - void valueMonthChanged(const QString &str); - void valueMonthChanged2(const QString &str); - void valueTimezoneChangedStr(const QString &str); - void valueTimezoneChangedInt(int newTimezone); - void valueYearChanged(const QString &str); - - void onBackHourClicked(); - void onBackMinuteClicked(); - void onBackSecondClicked(); - void onForwardSecondClicked(); - void onForwardMinuteClicked(); - void onForwardHourClicked(); - - void onCheckBoxChanged(); - void disableControls(); - void enableControls(); - void enaDisaControls(bool bEnable); - - void onFocusOutHHMMSS(); - void onFocusOutTZ(); - void onFocusOutDay(); - void onFocusOutMonth(); - void onFocusOutYear(); - - void onFocusOutBackspace(); - void onFocusOut(); - - void onHmsWheelEvent(bool bForward, int cursorPos); - void onDayWheelEvent(bool bForward, int cursorPos); - void onMonthWheelEvent(bool bForward, int cursorPos); - void onYearWheelEvent(bool bForward, int cursorPos); - -private: - Ui::LtsDateTimeEditLe *m_uiLe{nullptr}; - Ui::LtsDateTimeEdit *m_uiTouch{nullptr}; - QShortcut *m_scFixed = nullptr; - QShortcut *m_scOffset = nullptr; - - CurrentTimeCallback m_getCurrentTimeCallback = nullptr; - - qint64 m_minTimeMSec = 0; - qint64 m_maxTimeMSec = 0; - - qint64 m_curTimeMSec = -1; - - bool m_editTimezone = true; - bool m_autoForward = true; - bool m_withButtons = false; - bool m_withSlider = false; - bool m_readOnly = false; - bool m_withCheckBox = false; - bool m_withNowButton = false; - - int m_minMonth{0}; - int m_maxMonth{0}; - - - bool m_backspacePressed = false; - bool m_recursionAvoid = false; - bool m_dateIsAcceptable = false; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp b/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp deleted file mode 100755 index 0f143f714..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp +++ /dev/null @@ -1,389 +0,0 @@ - - - -#include "lts_date_time_edit_touch.h" -#include "ui_lts_date_time_edit_touch.h" - -#include -#include -#include -#include - -namespace LtsWidgets -{ - -LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool withCheckBox) - : QWidget(parent) - , m_withCheckBox(withCheckBox) - , m_withNowButton(withNowButton) - , m_tzOffset(0) - , m_minMonth(1) - , m_maxMonth(12) - , m_scFixed(new QShortcut(QKeySequence(Qt::ShiftModifier + Qt::Key_F2), this, SLOT(onSetFixedDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_scOffset(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(onSetOffsetDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) - , m_getCurrentTimeCallback(nullptr) - , m_ui(new Ui::LtsDateTimeEdit) -{ - m_ui->setupUi(this); - setFocusPolicy(Qt::StrongFocus); - - - for (int i = 65; i < 91; i++ ) - { - if (QChar(i).toUpper() == QChar::fromLatin1('I')) - continue; - m_ui->cbxTimeZone->addItem(QChar(i), QChar(i)); - - } - - connect(m_ui->pbNow, &QPushButton::clicked, - this, &LtsDateTimeEdit::onSetOffsetDateTime); - connect(m_ui->cbxMonth, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::onDateTimeChanged); - connect(m_ui->spDay, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::onDateTimeChanged); - connect(m_ui->spHour, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::onDateTimeChanged); - connect(m_ui->spMinutes, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::onDateTimeChanged); - connect(m_ui->spSeconds, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::onDateTimeChanged); - connect(m_ui->spYear, &QSpinBox::editingFinished, - this, &LtsDateTimeEdit::onDateTimeChanged); - - connect(m_ui->checkBox, &QCheckBox::toggled, - this, &LtsDateTimeEdit::onCheckBoxChanged); - connect(m_ui->cbxTimeZone, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::onTimeZoneChanged); - - connect(m_ui->cbxMonth, static_cast(&QComboBox::currentIndexChanged), - this, &LtsDateTimeEdit::onMonthChanged); - - m_tzOffset = DTG::timezone2Secs(QLatin1Char(DTG::timezone()).toLatin1()); - - - m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findText(QString(QLatin1Char(DTG::timezone())))); - onTimeZoneChanged(); - setMinMaxMonth(1,12); - - - int offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong()/1000; - m_ui->pbNow->setToolTip(tr("Set to current time plus %n second(s) offset", "", offset)); - - updateDateTimeType(); - - m_ui->checkBox->setVisible(m_withCheckBox); - m_ui->pbNow->setVisible(m_withNowButton); -} - -LtsDateTimeEdit::~LtsDateTimeEdit() -{ - delete m_ui; -} - -void LtsDateTimeEdit::updateDateTimeType() -{ - switch (DTG::dateTimeType()) - { - case DTG::DateTimeType::DTG: - break; - case DTG::DateTimeType::DDMMYYYY_HHMMSS_Z: - break; - case DTG::DateTimeType::DDMMYYYY_Z_HHMMSS: - break; - default: - break; - } -} - -bool LtsDateTimeEdit::setTimezone(char tz) -{ - QChar tzLocal = QChar(QLatin1Char(tz)).toUpper(); - if (tzLocal.toLatin1() == 'J') - tzLocal = QLatin1Char('Z'); - m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findData(tzLocal)); - int offset = DTG::timezone2Secs(tzLocal.toLatin1()); - if (offset != m_tzOffset) - { - const auto ms = msecsSinceEpoch(); - m_tzOffset = offset; - setMsecsSinceEpoch(ms+m_tzOffset); - } - return true; -} - -char LtsDateTimeEdit::getTimezone() const -{ - QChar tz = m_ui->cbxTimeZone->currentText().at(0).toUpper(); - - - if (tz.toLatin1() == 'J') - tz = QLatin1Char('Z'); - - if (tz.isNull()) - return DTG::timezone(); - return tz.toLatin1(); -} - - -void LtsDateTimeEdit::setMinMaxMonth(int minMonth, int maxMonth ) -{ - if (m_minMonth != minMonth) - m_minMonth = minMonth; - if (m_maxMonth != maxMonth) - m_maxMonth = maxMonth; - - int currentMonth = m_ui->cbxMonth->currentData().toInt(); - m_ui->cbxMonth->blockSignals(true); - m_ui->cbxMonth->clear(); - for (int i =1; i<= 12 ; i++) - { - if (i < m_minMonth) - continue; - if (i > m_maxMonth) - break; - QDate date(1777,i,1); - m_ui->cbxMonth->addItem(date.toString(QStringLiteral("MMM")),i); - - } - m_ui->cbxMonth->setCurrentIndex(m_ui->cbxMonth->findData(currentMonth)); - m_ui->cbxMonth->blockSignals(false); -} - - -void LtsDateTimeEdit::setDateTime(const QDateTime &datetime) -{ - if (!datetime.isValid() && m_withCheckBox) - { - m_ui->checkBox->setChecked(false); - return; - } - - QDateTime dt = datetime.toUTC().addSecs(m_tzOffset); - dt.setOffsetFromUtc(m_tzOffset); - - - QDateTime oldDateTime = dateTime(); - if (dt == oldDateTime) - return; - - blockSignals(true); - - m_ui->spDay->setValue(dt.date().day()); - m_ui->cbxMonth->setCurrentIndex(m_ui->cbxMonth->findData(dt.date().month())); - - - - - m_ui->spYear->setValue(dt.date().year()-2000); - - m_ui->spHour->setValue(dt.time().hour()); - m_ui->spMinutes->setValue(dt.time().minute()); - m_ui->spSeconds->setValue(dt.time().second()); - - blockSignals(false); - - const QDateTime dtNew = dateTime(); - if (dtNew != oldDateTime) - emit dateTimeChanged(); -} - - -void LtsDateTimeEdit::onDateTimeChanged() -{ - emit dateTimeChanged(); - - QDateTime dateTime; - dateTime = dateTime.addYears(m_ui->spYear->value()); - dateTime = dateTime.addMonths(m_ui->cbxMonth->currentData().toInt()); - dateTime = dateTime.addDays(m_ui->spDay->value()); - dateTime = dateTime.addSecs(m_ui->spHour->value()*60*60 + m_ui->spMinutes->value()*60 + m_ui->spSeconds->value()); - if (dateTime.isValid()) - { - m_dateTime = dateTime; - - } -} - -void LtsDateTimeEdit::onTimeZoneChanged() -{ - if (m_ui->cbxMonth->currentText().isEmpty()) - { - return; - } - QChar tz = m_ui->cbxTimeZone->currentText().at(0); - if (tz.isLower()) - tz = tz.toUpper(); - if (tz.toLatin1() == 'J') - tz = QLatin1Char('Z'); - m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findData(tz)); - int offset = DTG::timezone2Secs(tz.toLatin1()); - if (offset != m_tzOffset) - { - const auto ms = msecsSinceEpoch(); - m_tzOffset = offset; - setMsecsSinceEpoch(ms+m_tzOffset); - } -} - -void LtsDateTimeEdit::onMonthChanged(int month) -{ - - Q_UNUSED(month); -} - -void LtsDateTimeEdit::setWithCheckBox(bool checkBoxEnabled) -{ - if (m_withCheckBox == checkBoxEnabled) - return; - - m_withCheckBox = checkBoxEnabled; - m_ui->checkBox->setVisible(m_withCheckBox); - if (!m_withCheckBox) - m_ui->checkBox->setChecked(true); - emit withCheckBoxChanged(m_withCheckBox); -} - -bool LtsDateTimeEdit::isCheckBoxChecked() const -{ - return m_ui->checkBox->isChecked(); -} - -void LtsDateTimeEdit::setWithNowButton(bool nowButton) -{ - if (m_withNowButton == nowButton) - return; - - m_withNowButton = nowButton; - m_ui->pbNow->setVisible(m_withNowButton); - emit withNowButtonChanged(m_withNowButton); -} - -void LtsDateTimeEdit::onCheckBoxChanged(bool checked) -{ - m_ui->spDay->setEnabled(checked); - m_ui->spHour->setEnabled(checked); - m_ui->spMinutes->setEnabled(checked); - m_ui->spSeconds->setEnabled(checked); - m_ui->spYear->setEnabled(checked); - m_ui->cbxMonth->setEnabled(checked); - m_ui->cbxTimeZone->setEnabled(checked); - m_ui->pbNow->setEnabled(checked); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::setMsecsSinceEpoch(qint64 mSecsSinceEpoch) -{ - if (mSecsSinceEpoch == 0 && m_withCheckBox) - { - m_ui->checkBox->setChecked(false); - return; - } - m_ui->checkBox->setChecked(true); - setDateTime(QDateTime::fromMSecsSinceEpoch(mSecsSinceEpoch).toUTC()); -} - -qint64 LtsDateTimeEdit::msecsSinceEpoch() const -{ - const QDateTime dt(dateTime()); - if (dt.isNull()) - return 0; - return dt.toMSecsSinceEpoch(); -} - -void LtsDateTimeEdit::clear() -{ - m_ui->spDay->clear(); - m_ui->spHour->clear(); - m_ui->spMinutes->clear(); - m_ui->spSeconds->clear(); - m_ui->spYear->clear(); - emit dateTimeChanged(); -} - -void LtsDateTimeEdit::setCheckBoxChecked(bool bChecked) -{ - m_ui->checkBox->setChecked(bChecked); -} - -QDateTime LtsDateTimeEdit::dateTime() const -{ - if (!m_ui->checkBox->isChecked()) - return QDateTime(); - - QDateTime final; - int year = 0; - if (m_ui->spYear->value() < 1000) - { - year=m_ui->spYear->value()+2000; - } - else - { - year=m_ui->spYear->value(); - } - QDate date(year,m_ui->cbxMonth->currentData().toInt(), m_ui->spDay->value()); - final.setDate(date); - final = final.addSecs(m_ui->spHour->value()*60*60 + m_ui->spMinutes->value()*60 + m_ui->spSeconds->value()); - - if (!final.isValid()) - return QDateTime(); - - - return QDateTime(final.date(), final.time(), Qt::OffsetFromUTC, m_tzOffset).toUTC(); -} - -void LtsDateTimeEdit::setMaximumTimeMSec(qint64 msec) -{ - const auto maximumDate(QDateTime::fromMSecsSinceEpoch(msec).date()); - m_ui->spYear->setMaximum(maximumDate.year()-2000); - - - - -} - -qint64 LtsDateTimeEdit::minimumTimeMSec() const -{ - QDate date; - date.setDate(m_ui->spYear->minimum(), m_maxMonth, m_ui->spMinutes->minimum() ); - return QDateTime(date).toMSecsSinceEpoch(); -} - -void LtsDateTimeEdit::setMinimumTimeMSec(qint64 msec) -{ - const auto minimumDate(QDateTime::fromMSecsSinceEpoch(msec).date()); - - m_ui->spYear->setMinimum(minimumDate.year()-2000); - -} - -void LtsDateTimeEdit::setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb) -{ - m_getCurrentTimeCallback = cb; -} - -void LtsDateTimeEdit::onSetFixedDateTime() -{ - setMsecsSinceEpoch(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("FixedTime"), 0).toLongLong()); -} - -void LtsDateTimeEdit::onSetOffsetDateTime() -{ - if (!m_getCurrentTimeCallback) - return; - qint64 offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong(); - - setMsecsSinceEpoch((m_getCurrentTimeCallback() / 1000) * 1000 + offset); -} - - -void LtsDateTimeEdit::setSubWidgetsProperties(QString name, QVariant value) -{ - m_ui->spDay->setProperty(name.toStdString().c_str(), value); - m_ui->spHour->setProperty(name.toStdString().c_str(), value); - m_ui->spMinutes->setProperty(name.toStdString().c_str(), value); - m_ui->spSeconds->setProperty(name.toStdString().c_str(), value); - m_ui->spYear->setProperty(name.toStdString().c_str(), value); - -} -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h b/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h deleted file mode 100755 index 9ec977edb..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h +++ /dev/null @@ -1,140 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include -#include - -class QDateTime; - -namespace Ui -{ -class LtsDateTimeEdit; -} - -class QShortcut; - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward) - Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone) - Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons) - Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider) - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) - Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton) - Q_PROPERTY(bool withCheckBox READ withCheckBox WRITE setWithCheckBox) - -public: - using CurrentTimeCallback = std::function; - explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool withCheckBox = false); - ~LtsDateTimeEdit(); - - qint64 msecsSinceEpoch() const; - bool isEmpty() const { return msecsSinceEpoch() == 0; } - - void setAutoForward(const bool af) { m_autoForward = af; } - bool getAutoForward() const { return m_autoForward; } - - void setEditTimezone(const bool edit) { m_editTimezone = edit; } - bool getEditTimezone() const { return m_editTimezone; } - - void setWithButtons(const bool buttons) { m_withButtons = buttons; } - bool getWithButtons() const { return m_withButtons; } - - void setWithSlider(const bool slider) { m_withSlider = slider; } - bool getWithSlider() const { return m_withSlider; } - - void setReadOnly(const bool readOnly) { m_readOnly = readOnly; } - bool isReadOnly() const { return m_readOnly; } - - bool withCheckBox() const { return m_withCheckBox; } - void setCheckBoxChecked(bool bChecked); - bool isCheckBoxChecked() const; - - bool withNowButton() const { return m_withNowButton; } - void setDateTime(const QDateTime &datetime); - - - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const; - - - void setMaximumTimeMSec(qint64 msec); - qint64 maximumTimeMSec() const { return m_maxTimeMSec; }; - - void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); } - - void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb); - void updateDateTimeType(); - - bool setTimezone(char tz); - char getTimezone() const; - - - void setSubWidgetsProperties(QString name, QVariant value); - - bool isValidDate() const - { - - return m_dateTime.isValid(); - } - -private: - QDateTime dateTime() const; - void setMinMaxMonth(int minMonth, int maxMonth); - -public slots: - void setWithCheckBox(bool checkBoxEnabled); - void setWithNowButton(bool nowButton); - void setMsecsSinceEpoch(qint64 mSecsSinceEpoch); - void clear(); - - void onSetFixedDateTime(); - void onSetOffsetDateTime(); - -public slots: - void onDateTimeChanged(); - -signals: - void dateTimeChanged(); - void withCheckBoxChanged(bool checkBoxEnabled); - void withNowButtonChanged(bool nowButton); - -private slots: - void onCheckBoxChanged(bool checked); - void onTimeZoneChanged(); - void onMonthChanged(int month); - -private: - - qint64 m_minTimeMSec = 0; - qint64 m_maxTimeMSec = 0; - qint64 m_curTimeMSec = -1; - - bool m_editTimezone = true; - bool m_autoForward = true; - bool m_withButtons = false; - bool m_withSlider = false; - bool m_readOnly = false; - bool m_withCheckBox = false; - bool m_withNowButton = false; - - int m_tzOffset; - int m_minMonth; - int m_maxMonth; - QShortcut *m_scFixed; - QShortcut *m_scOffset; - QDateTime m_dateTime; - CurrentTimeCallback m_getCurrentTimeCallback; - Ui::LtsDateTimeEdit *m_ui; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui b/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui deleted file mode 100755 index fe28a140c..000000000 --- a/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui +++ /dev/null @@ -1,181 +0,0 @@ - - - LtsDateTimeEdit - - - - 0 - 0 - 558 - 31 - - - - - 0 - 0 - - - - - 558 - 31 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - true - - - true - - - true - - - - - - - 1 - - - 31 - - - - - - - - 5 - 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - - 23 - - - - - - - 59 - - - - - - - 59 - - - - - - - - 5 - 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - - - 50 - 16777215 - - - - - - - - - 5 - 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - - - 100 - 16777215 - - - - - - - - 0 - - - 99 - - - - - - - Now - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_dialog.cpp b/Lts/Common/LtsWidgets/lts_dialog.cpp deleted file mode 100755 index a03c1085e..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog.cpp +++ /dev/null @@ -1,810 +0,0 @@ - - - -#include "lts_dialog.h" -#include "lts_progress_dialog_helper.h" -#include "lts_message_dialog.h" -#include "lts_dialog_helper.h" -#include "lts_dialog_button_box.h" -#include "lts_base_layout_widget.h" -#include "lts_date_time_edit.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 -#include - -#include - - -namespace LtsWidgets -{ -int LtsDialog::s_object_count = 0; - - -class OverlayContainer : public QWidget -{ - Q_OBJECT -public: - OverlayContainer(QWidget *parent); - -protected: - void paintEvent(QPaintEvent *e) override; -}; - - -OverlayContainer::OverlayContainer(QWidget *parent) - : QWidget(parent) -{ - setAttribute(Qt::WA_TranslucentBackground); -} - - -void OverlayContainer::paintEvent(QPaintEvent *e) -{ - QPainter p(this); - p.fillRect(rect(), { 80, 80, 80, 200 }); - QWidget::paintEvent(e); -} - - - - -LtsDialog::LtsDialog(QWidget *parent, ButtonCombination buttonCombination, bool closeable) - : QWidget() - , m_changesMade(false) - , m_firstShow(true) - , m_baseLayoutWidget(nullptr) - , m_isClosable(closeable) - , m_noLockTopic(false) - , m_unchangeableDialog(false) - , m_dialogHelper(new LtsDialogHelper(nullptr)) - - - - , m_buttonBox(nullptr) - , m_uiRequirements(new UiRequirementList()) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsWidget.LtsDialog")) -{ - Q_UNUSED(parent); - - s_object_count++; - - m_dialogHelper->setDialog(this); - m_buttonBox = m_dialogHelper->getButtonBox(); - - - - - - - - - - - - - - - - addDebugMenuButton(); - - - - - - connect(m_buttonBox->getButton(DialogButton::Close), &QPushButton::clicked, this, &LtsDialog::slotClose); - - switch(buttonCombination) - { - case ButtonCombination::OkCancel: - { - m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); - connect(m_buttonBox->getButton(DialogButton::Ok), &QPushButton::clicked, this, &LtsDialog::slotOkay); - break; - } - - case ButtonCombination::OkApplyCancel: - { - m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); - connect(m_buttonBox->getButton(DialogButton::Ok), &QPushButton::clicked, this, &LtsDialog::slotOkay); - connect(m_buttonBox->getButton(DialogButton::Apply), &QPushButton::clicked, this, &LtsDialog::slotApply); - m_buttonBox->getButton(DialogButton::Apply)->setVisible(true); - break; - } - - case ButtonCombination::CloseOnly: - { - m_buttonBox->getButton(DialogButton::Ok)->setVisible(false); - setUnchangeable(); - break; - } - - default: - case ButtonCombination::SeamlessDialog: - { - m_buttonBox->setVisible(false); - setContentsMargins(0,0,0,0); - m_dialogHelper->setContentsMargins(0,0,0,0); - - QLayout *l = m_dialogHelper->layout(); - - if (l) - { - l->setContentsMargins(0,0,0,0); - l->setMargin(0); - } - else - { - qWarning() << "LtsDialog::c_tor: Dialog Helper has no layout to set margins"; - } - - break; - } - } - - if (!closeable) - { - m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setVisible(false); - m_buttonBox->getButton(LtsWidgets::DialogButton::Close)->setVisible(false); - } -} - - -LtsDialog::~LtsDialog() -{ - delete m_uiRequirements; - - if (m_overlayWidget) - m_overlayWidget->setParent(nullptr); - - - - - s_object_count--; -} - - -void LtsDialog::setOverlay(QWidget *widget) -{ - if (m_overlayContainer) - { - if (m_overlayWidget) - { - m_overlayWidget->hide(); - m_overlayWidget->setParent(nullptr); - m_overlayWidget->hide(); - m_overlayWidget = nullptr; - - } - - m_overlayContainer->hide(); - m_overlayContainer->setParent(nullptr); - m_overlayContainer->hide(); - m_overlayContainer->deleteLater(); - m_overlayContainer = nullptr; - } - - - OverlayContainer *overlayContainer = new OverlayContainer(m_dialogHelper); - - connect(m_dialogHelper.data(), &LtsDialogHelper::signalSizeChanged, - overlayContainer, [overlayContainer](const QSize &size) - { - overlayContainer->setGeometry(QRect(QPoint(0, 0), size)); - }); - - overlayContainer->setGeometry(QRect(QPoint(0, 0), m_dialogHelper->size())); - m_overlayContainer = overlayContainer; - - - QHBoxLayout *l = new QHBoxLayout; - m_overlayContainer->setLayout(l); - - widget->setAutoFillBackground(true); - l->addWidget(widget, 0, Qt::AlignCenter); - widget->show(); - - connect(widget, &QWidget::destroyed, - m_overlayContainer.data(), &QObject::deleteLater); - - m_overlayWidget = widget; - - m_overlayContainer->show(); - m_overlayContainer->raise(); -} - - -void LtsDialog::unsetOverlay() -{ - if (m_overlayWidget) - m_overlayWidget->setParent(nullptr); - - if (!m_overlayContainer) - return; - - m_overlayContainer->setParent(nullptr); - m_overlayContainer->deleteLater(); -} - - -LtsDialogHelper *LtsDialog::getHelper() -{ - return m_dialogHelper; -} - - -LtsBaseLayoutWidget* LtsDialog::baseLayoutWidget() -{ - return m_baseLayoutWidget; -} - - -void LtsDialog::setOnlineHelpFilename(const QString &fileName) -{ - m_onlineHelpFileName = fileName; - QPushButton *btn = m_buttonBox->getButton(DialogButton::Help); - - if (btn == nullptr) - return; - - if (LtsSessionData::LtsSessionData::getInstance()->isTablet() && strcmp(PROJECTBUILD, "guez") == 0) - return; - - connect(btn, &QPushButton::clicked, - this, [this]() - { - emit signalShowOnlineHelp(m_onlineHelpFileName); - }); - - btn->setVisible(true); -} - - -void LtsDialog::addDebugMenuButton() -{ -#ifdef LTS_DEBUG - QMenu *menu = new QMenu(this); - - menu->addAction(QStringLiteral("simulate changes"), this, &LtsDialog::slotChangesMade); - menu->addAction(QStringLiteral("unchange"), this, &LtsDialog::__unchange__); - menu->addAction(QStringLiteral("retranslate"), this, &LtsDialog::retranslate); - menu->addAction(QStringLiteral("force unlock"), this, [this]() { setUnchangeable(false); }); - menu->addAction(QStringLiteral("set unchangeable"), this, [this]() { setUnchangeable(); }); - menu->addAction(QStringLiteral("try lock"), this, &LtsDialog::tryLockTopic); - menu->addAction(QStringLiteral("set closable"), this, [this]() { m_isClosable = true; }); - menu->addAction(QStringLiteral("set unclosable"), this, [this]() { m_isClosable = false; }); - menu->addAction(QStringLiteral("clear requirements"), this, [this] { m_uiRequirements->clear(); }); - menu->addAction(QStringLiteral("enable all button box buttons"), this, [this]() - { - const auto children = m_buttonBox->findChildren(); - for (QPushButton *pb : children) - pb->setEnabled(true); - }); - menu->addAction(QStringLiteral("show button box button .."), this, [this]() - { - QString buttonObjectName = QInputDialog::getText(this, QString(), QStringLiteral("Enter button object name")); - if (buttonObjectName.isEmpty()) - return; - - const QList buttons = m_buttonBox->findChildren(buttonObjectName); - - int btnCount = buttons.count(); - if (btnCount == 0) - { - LtsMessageDialog::showMessage(this, QStringLiteral("button not found")); - return; - } - - if (btnCount > 1) - { - QStringList buttonStringList; - buttonStringList.reserve(buttons.size()); - for (QPushButton *pb : buttons) - buttonStringList += pb->objectName(); - - LtsMessageDialog::showMessage(this, QStringLiteral("button ambiguous: %1").arg(buttonStringList.join(QStringLiteral(", ")))); - return; - } - - buttons.at(0)->setVisible(true); - }); - - QPushButton *debugBtn = m_buttonBox->getButton(DialogButton::_debug_); - debugBtn->setMenu(menu); - debugBtn->setVisible(true); -#endif -} - - -void LtsDialog::updateWindowTitle() -{ - QString completeTitle = m_title; - - if (m_changesMade) - completeTitle.insert(0, QStringLiteral("\u2022")); - - if (!m_topicLockedBy.isEmpty()) - completeTitle.push_back(QString(QStringLiteral(" - READ-ONLY (%1)")).arg(m_topicLockedBy)); - - QWidget::setWindowTitle(completeTitle); - if (m_dialogHelper != nullptr) - m_dialogHelper->setWindowTitle(completeTitle); -} - - -void LtsDialog::setWindowTitle(const QString &title) -{ - m_title = title; - updateWindowTitle(); -} - - -void LtsDialog::setBaseLayoutWidget(LtsBaseLayoutWidget *baseLayoutWidget) -{ - m_baseLayoutWidget = baseLayoutWidget; -} - -void LtsDialog::setIdentifier(const QString &identifier) -{ - m_identifier = identifier; - tableColumnOrganiserSetting(); -} - - -void LtsDialog::addConnections(QWidget *widget) -{ - if (addChangesMadeSignal(widget, &QLineEdit::textChanged)) return; - if (addChangesMadeSignal(widget, &QDateTimeEdit::dateTimeChanged)) return; - if (addChangesMadeSignal(widget, &QComboBox::currentTextChanged)) return; - if (addChangesMadeSignal(widget, &QCheckBox::stateChanged)) return; - if (addChangesMadeSignal(widget, static_cast(&QSpinBox::valueChanged))) return; - if (addChangesMadeSignal(widget, static_cast(&QDoubleSpinBox::valueChanged))) return; - if (addChangesMadeSignal(widget, &QSlider::valueChanged)) return; - if (addChangesMadeSignal(widget, &QPlainTextEdit::textChanged)) return; - if (addChangesMadeSignal(widget, &QTextEdit::textChanged)) return; - if (addChangesMadeSignal(widget, &QAbstractButton::toggled)) return; - if (addChangesMadeSignal(widget, &LtsDateTimeEdit::dateTimeChanged)) return; -} - - -void LtsDialog::setUnchangeable(bool unchangeable) -{ - m_unchangeableDialog = unchangeable; -} - - -bool LtsDialog::addDialogFixed(LtsDialog *dialog, const char *menuGroupUntranslated, const QString &identifier, const LayoutArea &layoutArea, bool setClosable, bool focusNewTab) -{ - return addDialog(dialog, tr(menuGroupUntranslated) , identifier, layoutArea, setClosable, focusNewTab); -} - - -bool LtsDialog::addDialog(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea, bool setClosable, bool focusNewTab) -{ - if (!m_baseLayoutWidget) - { - qCritical() << "LtsDialog: addDialog: no base layout set - cannot add Dialog !"; - return false; - } - - return m_baseLayoutWidget->addWidget(dialog, menuGroup, identifier, layoutArea, setClosable, focusNewTab); -} - - -bool LtsDialog::amIFocused() const -{ - if (!m_baseLayoutWidget) - { - qCritical() << "LtsDialog: addDialog: no base layout set - cannot check for focus!"; - return false; - } - - return m_baseLayoutWidget->isDialogFocused(this); -} - - -bool LtsDialog::focusDialog(const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea) -{ - if (!m_baseLayoutWidget) - { - qCritical() << "LtsDialog: addDialog: no base layout set - cannot focus Dialog !"; - return false; - } - - return m_baseLayoutWidget->focusTab(menuGroup, identifier, layoutArea); -} - - -void LtsDialog::showEvent(QShowEvent *event) -{ - emit visibilityChanged(true); - - bool tmp = m_unchangeableDialog; - - if (!m_firstShow) - { - QWidget::showEvent(event); - return; - } - - m_unchangeableDialog = true; - m_firstShow = false; - - - - - - QList listenWidgets = findChildren(); - - for (auto i = listenWidgets.begin(); i != listenWidgets.end(); ++i) - { - - - - QWidget *widget = *i; - - QVariant isConst = widget->property("const"); - if (isConst.isValid() && isConst.toBool()) - continue; - - if (!tmp) - addConnections(widget); - -#ifdef TABLET_MODE - LtsWidgets::addTouchScroll(widget); -#endif - } - - QWidget::showEvent(event); - m_unchangeableDialog = tmp; -} - - -void LtsDialog::tryLockTopic() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - if (!m_topicInfo.isValid()) - return; - - if (m_topicInfo.isLocked()) - { - WARNING_LOG(m_logger) << LOGFUNCTION << " tried to lock with an already locked topic info, return"; - return; - } - - if (m_noLockTopic) - return; - - - - - - - - - bool bIsConnected = isSignalConnected(QMetaMethod::fromSignal(&LtsDialog::signalLockTopic)); - - DEBUG_LOG(m_logger) << LOGFUNCTION - << " signalLockTopic(): " - << ", TopicName =" << m_topicInfo.topicName() - << ", entity =" << m_topicInfo.entityId() - << ", signalLockTopic isConnected=" << bIsConnected; - - if (bIsConnected) - { - emit signalLockTopic(m_topicInfo); - - m_topicInfo.setLocked(); - } - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -void LtsDialog::initTopicLockHandler(const QString &topicName, int entityId) -{ - - - m_topicInfo.setTopicName(topicName); - m_topicInfo.setEntity(entityId); - m_topicInfo.setValid(); -} - - -void LtsDialog::tryLockTopicNew() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START"; - tryLockTopic(); - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -void LtsDialog::tryLockTopicNew(const QString &topicName, int entityId) -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START" - << ", topicName='" << topicName << "'" - << ", entityId=" << entityId; - - - - if (m_topicInfo.entityId() != entityId) - { - DEBUG_LOG(m_logger) << LOGFUNCTION << " topicName='" << topicName << "'" - << " change from " << m_topicInfo.entityId() << " to " << entityId; - m_topicInfo.setLocked(false); - } - - - m_topicInfo.setTopicName(topicName); - m_topicInfo.setEntity(entityId); - m_topicInfo.setValid(); - - tryLockTopic(); - TRACE_LOG(m_logger) << LOGFUNCTION << " END" - << ", topicName='" << topicName << "'" - << ", entityId=" << entityId; -} - -void LtsDialog::triggerTopicLock() -{ - TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " !!trigger!!"; - tryLockTopic(); - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - - -void LtsDialog::translate(const QString &title) -{ - bool isAlreadyUnchangeable = unchangeable(); - - if (!isAlreadyUnchangeable) - setUnchangeable(true); - - setWindowTitle(title); - - - - - - if (!isAlreadyUnchangeable) - setUnchangeable(false); - - { - bool isNowAlreadyUnchangeable = unchangeable(); - - QPushButton *b = m_buttonBox->getButton(DialogButton::ReadOnly); - if (b) - { - connect(b, &QPushButton::clicked, this, &LtsDialog::onReadOnlyButtonClicked, Qt::UniqueConnection); - - - b->setVisible(isNowAlreadyUnchangeable && !m_topicLockedBy.isEmpty()); - if (b->isVisible()) - b->setToolTip(tr("Locked by: %1").arg(m_topicLockedBy)); - else - b->setToolTip({}); - } - } -} - - - -void LtsDialog::slotLock(const QString &lockedBy) -{ - setUnchangeable(true); - - m_topicLockedBy = lockedBy; - - lock(); - retranslate(); -} - - -void LtsDialog::slotUnlock() -{ - setUnchangeable(false); - - m_topicLockedBy.clear(); - - unlock(); - retranslate(); -} - - -void LtsDialog::lock() -{ - setWidgetsReadonly(true); - setWidgetsReadonly(true); - - setWidgetsReadonly(true); - setWidgetsReadonly(true); - setWidgetsReadonly(true); - setWidgetsReadonly(true); - - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); - setWidgetsDisabled(true); -} - - -void LtsDialog::unlock() -{ - restoreReadonly(); - restoreReadonly(); - - restoreReadonly(); - restoreReadonly(); - restoreReadonly(); - restoreReadonly(); - - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); -} - - -void LtsDialog::slotChangesMade() -{ - if (m_unchangeableDialog) - return; - - m_changesMade = true; - updateWindowTitle(); - - m_buttonBox->getButton(DialogButton::Ok)->setEnabled(true); - m_buttonBox->getButton(DialogButton::Apply)->setEnabled(true); - - emit signalChangesMade(); -} - - -void LtsDialog::resetChanges() -{ - m_changesMade = false; - - updateWindowTitle(); - - - - m_buttonBox->getButton(DialogButton::Ok)->setEnabled(false); - m_buttonBox->getButton(DialogButton::Apply)->setEnabled(false); - emit signalChangesReset(); -} - - -bool LtsDialog::_apply() -{ - if (m_uiRequirements->checkRequirements(this) && apply()) - { - resetChanges(); - return true; - } - - return false; -} - - -Button LtsDialog::askApplyDiscardCancel() -{ - return LtsMessageDialog::askApplyDiscardCancel(this); -} - - -void LtsDialog::slotApply() -{ - _apply(); -} - - -void LtsDialog::slotOkay() -{ - if (_apply()) - _close(); -} - - -void LtsDialog::slotHide() -{ - _hide(); -} - - -void LtsDialog::slotClose() -{ - - if ((m_changesMade) && (!m_unchangeableDialog)) - { - switch (askApplyDiscardCancel()) - { - case Button::Apply: - slotOkay(); - break; - case Button::Discard: - _discard(); - break; - default: - break; - } - } - else - { - _close(); - } -} - -void LtsDialog::onReadOnlyButtonClicked() -{ - if (m_topicLockedBy.isEmpty()) - return; - LtsWidgets::LtsMessageDialog::showMessage(this, tr("Locked by: %1").arg(m_topicLockedBy)); -} - - -void LtsDialog::closeRequest() -{ - slotClose(); -} - - -void LtsDialog::_hide() -{ - if (m_dialogHelper != nullptr) - { - emit signalHide(m_dialogHelper); - - } -} - - -void LtsDialog::_close() -{ - resetChanges(); - aboutToClose(); - if (m_dialogHelper != nullptr) - m_dialogHelper->close(); -} - -void LtsDialog::_discard() -{ - dialogCanceled(); - _close(); -} - -} - - - - -#include "lts_dialog.moc" diff --git a/Lts/Common/LtsWidgets/lts_dialog.h b/Lts/Common/LtsWidgets/lts_dialog.h deleted file mode 100755 index 193642869..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog.h +++ /dev/null @@ -1,473 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" -#include "ui_requirement.h" -#include "lts_dialog_button_box.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace LtsWidgets -{ -class TopicInfo -{ -public: - int entityId() const { return m_entityId; } - const QString &topicName() const { return m_topicName; } - bool isLocked() const { return m_locked; } - bool isValid() const { return m_valid; } - - void setEntity(int entityId) { m_entityId = entityId; } - void setTopicName(const QString &topicName) { m_topicName = topicName; } - void setLocked(bool locked = true) { m_locked = locked; } - void setValid() { m_valid = true; } - -private: - QString m_topicName; - int m_entityId{0}; - bool m_locked{false}; - bool m_valid{false}; -}; - - -class LtsBaseLayoutWidget; -class LtsDialogHelper; -class LtsProgressDialogHelper; -class LtsDialogButtonBox; -class LtsToolBarArea; -class OverlayContainer; - - -enum class ButtonCombination -{ - OkApplyCancel, - - OkCancel, - - - - CloseOnly, - SeamlessDialog, -}; - - - - -class LTSWIDGETS_EXPORT LtsDialog : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool unchangeable READ unchangeable WRITE setUnchangeable) -public: - static int s_object_count; - - - - explicit LtsDialog(QWidget *parent = nullptr, ButtonCombination buttonCombination = ButtonCombination::CloseOnly, bool closeable = true); - ~LtsDialog(); - - - - LtsDialogHelper *getHelper(); - - bool hasUnsavedChanges() const { return m_changesMade || m_currentlyBlocking; } - - - - virtual void retranslate() = 0; - - void closeRequest(); - void setUnchangeable(bool unchangeable = true); - bool unchangeable() const { return m_unchangeableDialog; } - - - - bool addDialog(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea = LayoutArea::eDialogTab, bool setClosable = true, bool focusNewTab = true); - - - Q_DECL_DEPRECATED bool addDialogFixed(LtsDialog *dialog, const char *menuGroupUntranslated, const QString &identifier, const LayoutArea &layoutArea = LayoutArea::eDialogTab, bool setClosable = true, bool focusNewTab = true); - bool focusDialog(const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea); - bool amIFocused() const; - void setBaseLayoutWidget(LtsBaseLayoutWidget *baseLayoutWidget); - void __unchange__() { resetChanges(); } - void setClosable(bool closable) { m_isClosable = closable; } - bool isClosable() const { return m_isClosable; } - virtual void aboutToClose() {} - void setMenuGroup(const QString &menuGroup) { m_menuGroup = menuGroup; } - const QString &menuGroup() const { return m_menuGroup; } - void setIdentifier(const QString &identifier); - virtual void tableColumnOrganiserSetting(){}; - const QString &identifier() const { return m_identifier; } - - void setOverlay(QWidget *widget); - void unsetOverlay(); - - - - static inline void setMinWidthForWidgetHelper(QWidget *w) - { - int width = w->sizeHint().width(); - w->setMinimumWidth(width); - } - - QString windowTitle() const - { - return m_title; - } - - - - - - - - - - - - - - - void tryLockTopicNew(); - - - - virtual void tryLockTopicNew (const QString &topicName, int entityId = -1); - - - void triggerTopicLock(); - - void setOnlineHelpFilename(const QString &fileName); - void setCurrentlyBlocking(bool currentlyBlocking = true) { m_currentlyBlocking = currentlyBlocking; } - -signals: - - - void startMainSelect(LtsWidgets::LayoutArea); - - - - void endMainSelect(LtsWidgets::LayoutArea); - - - - void signalChangesMade(); - - void signalHide(QObject *object); - - - - void signalChangesReset(); - - void signalFocused(); - void visibilityChanged(bool visible); - void signalLockTopic(const LtsWidgets::TopicInfo &topicInfo); - void signalShowOnlineHelp(const QString &fileName); - -protected: - - - void translate(const QString &title); - - - - template - - - void translate(T *ui, const QString &title) - { - translate(title); - if (ui) - ui->retranslateUi(this); - - m_uiRequirements->retranslate(); - } - - void hideEvent(QHideEvent *event) override - { - QWidget::hideEvent(event); - emit visibilityChanged(false); - } - - template - - - void translate(T *ui, const QString &custom, bool isNew, const QString &title) - { - QString newTitle; - if (!isNew) - { - if (unchangeable()) - { - if (custom.isEmpty()) - newTitle = (tr("Show %1").arg(title)); - else - newTitle = (tr("Show %1: %2").arg(title, custom)); - } - else - { - if (custom.isEmpty()) - newTitle = (tr("Edit %1").arg(title)); - else - newTitle = (tr("Edit %1: %2").arg(title, custom)); - - - tryLockTopicNew(); - } - } - else - { - newTitle = (tr("New %1").arg(title)); - m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->hide(); - m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setText(tr("Create")); - } - - translate(ui, newTitle); - } - - - template - - - void translate(T* ui, const QString &customUniqueTopicName, int topicId, const QString &custom, const QString &title) - { - Q_UNUSED(customUniqueTopicName); - - initTopicLockHandler(customUniqueTopicName, topicId); - - translate(ui, custom , - (topicId < 1 ), - title); - } - - - template - - - void translate(T *ui, const S &data, const QString &custom, const QString &title) - { - translate(ui, QString::fromUtf8(typeid(data).name()), data.id(), custom , title); - } - - - template - - - void translate(T *ui, const S &data, const QString &title) - { - translate(ui, data, QString::fromStdString(data.name()), title); - } - - - - virtual void dialogCanceled() {} - LtsBaseLayoutWidget* baseLayoutWidget(); - void setNoTopicLock() { m_noLockTopic = true; } - -private: - friend class LtsDialogHelper; - - bool m_changesMade; - bool m_currentlyBlocking { false }; - bool m_firstShow; - QPointer m_baseLayoutWidget; - bool m_isClosable; - TopicInfo m_topicInfo; - -public: - QString m_topicLockedBy; - -private: - bool m_noLockTopic; - bool m_unchangeableDialog; - QPointer m_dialogHelper; - - QString m_title; - QString m_onlineHelpFileName; - QString m_menuGroup; - QString m_identifier; - - - QPointer m_overlayContainer; - QPointer m_overlayWidget; - - bool _apply(); - void _close(); - void _discard(); - void _hide(); - void resetChanges(); - void addDebugMenuButton(); - void tryLockTopic(); - void initTopicLockHandler(const QString &topicName, int entityId); - - - - void setWindowTitle(const QString &title); - void updateWindowTitle(); - void addConnections(QWidget *widget); - - template - bool addChangesMadeSignal(QWidget *widget, SignalFunc signal) - { - WidgetType *concreteWidget = qobject_cast(widget); - - if (!concreteWidget) - return false; - - connect(concreteWidget, signal, this, &LtsDialog::slotChangesMade); - return true; - } - - - template - void setWidgetsReadonly(bool readonly) - { - const auto children = findChildren(); - for (WidgetType *widget : children ) - { - widget->setProperty("lastReadOnlyState", widget->isReadOnly()); - widget->setReadOnly(readonly); - } - } - - - template - void setWidgetsDisabled(bool disabled) - { - const auto children = findChildren(); - for (WidgetType *widget : children) - { - widget->setProperty("lastEnabledState", widget->isEnabled()); - widget->setEnabled(!disabled); - } - } - - template - void restoreReadonly() - { - const auto children = findChildren(); - for (WidgetType *widget : children) - { - QVariant lastState = widget->property("lastReadOnlyState"); - if (lastState.isValid() && !lastState.isNull() && lastState.canConvert(QMetaType::Bool)) - widget->setReadOnly(lastState.toBool()); - } - } - - template - void restoreEnabled() - { - const auto children = findChildren(); - for (WidgetType *widget : children) - { - QVariant lastState = widget->property("lastEnabledState"); - if (lastState.isValid() && !lastState.isNull() && lastState.canConvert(QMetaType::Bool)) - widget->setEnabled(lastState.toBool()); - } - } - -public slots: - - - void slotChangesMade(); - - void slotHide(); - void slotLock(const QString &lockedBy); - void slotUnlock(); - -private slots: - void slotApply(); - void slotOkay(); - void slotClose(); - - void onReadOnlyButtonClicked(); - -protected: - virtual void lock(); - virtual void unlock(); - - virtual bool apply() { return true; } - void showEvent(QShowEvent *event) override; - Button askApplyDiscardCancel(); - - LtsDialogButtonBox *m_buttonBox; - UiRequirementList *m_uiRequirements; - - NGL::INGLLogger &m_logger; -}; - - - -template -T *initDialog(T *dialog) -{ - if (!dialog->init()) - { - dialog->deleteLater(); - return nullptr; - } - return dialog; -} - - -template -T *initDialog(T *dialog, F&&... f) -{ - if (!dialog->init(std::forward(f)...)) - { - dialog->deleteLater(); - return nullptr; - } - return dialog; -} - - -template -T *createThrowableDialog(F&&... f) -{ - T *dlg = nullptr; - - try - { - dlg = new T(std::forward(f)...); - } - catch (const char *s) - { - fprintf(stderr, "Dialog could not be created: %s\n", s); - return nullptr; - } - catch (const QString &msg) - { - fprintf(stderr, "Dialog could not be created: %s\n", msg.toStdString().c_str()); - return nullptr; - } - catch (...) - { - fprintf(stderr, "Dialog could not be created: unknown error\n"); - return nullptr; - } - - return dlg; -} - - -} diff --git a/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp b/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp deleted file mode 100755 index 1bd215710..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp +++ /dev/null @@ -1,217 +0,0 @@ - - - -#include "lts_dialog_button_box.h" -#include "ui_lts_dialog_button_box.h" - -#include "lts_message_dialog.h" -#include -#include - -namespace LtsWidgets -{ -LtsDialogButtonBox::LtsDialogButtonBox(QWidget *parent) - : QWidget(parent) - , m_isTablet(LtsSessionData::LtsSessionData::getInstance()->isTablet()) - , m_ui(new Ui::LtsDialogButtonBox) -{ - m_ui->setupUi(this); - _setVisibilityForAll(false); - - - QMenu *libraryMenu = new QMenu(m_ui->pbLibrary); - libraryMenu->addAction(QIcon(QLatin1String(":/style/library_load.svg")), - tr("Load"), this, &LtsDialogButtonBox::signalLibraryLoadClicked); - libraryMenu->addAction(QIcon(QLatin1String(":/style/library_save.svg")), - tr("Save"), this, &LtsDialogButtonBox::signalLibrarySaveClicked); - m_ui->pbLibrary->setMenu(libraryMenu); - - - m_ui->pbOk->setVisible(true); - m_ui->pbClose->setVisible(true); - -} - - -LtsDialogButtonBox::~LtsDialogButtonBox() -{ - delete m_ui; -} - - -void LtsDialogButtonBox::_setVisibilityForAll(bool visible) -{ - const auto buttons = findChildren(); - for (QPushButton *button : buttons) - button->setVisible(visible); -} - - -void LtsDialogButtonBox::setText(DialogButton button, const QString &text) -{ - - - QPushButton *btn = getButton(button); - - if (btn) - btn->setText(text); -} - - -void LtsDialogButtonBox::setToolTip(DialogButton button, const QString &text) -{ - QPushButton *btn = getButton(button); - - if (btn) - btn->setToolTip(text); -} - - -void LtsDialogButtonBox::setIcon(DialogButton button, const QIcon &icon) -{ - QPushButton *btn = getButton(button); - - if (btn) - btn->setIcon(icon); -} - - -void LtsDialogButtonBox::setMenu(DialogButton button, QMenu *menu) -{ - QPushButton *btn = getButton(button); - - if (btn) - btn->setMenu(menu); -} - - -QPushButton *LtsDialogButtonBox::getButton(DialogButton button) const -{ - switch (button) - { - case DialogButton::ReadOnly: - return m_ui->pbReadOnly; - case DialogButton::Ok: - return m_ui->pbOk; - case DialogButton::Apply: - return m_ui->pbApply; - case DialogButton::Close: - return m_ui->pbClose; - case DialogButton::Yes: - return m_ui->pbYes; - case DialogButton::No: - return m_ui->pbNo; - case DialogButton::Remove: - return m_ui->pbRemove; - case DialogButton::Hide: - return m_ui->pbHide; - case DialogButton::Add: - return m_ui->pbAdd; - case DialogButton::Help: - return m_ui->pbHelp; - case DialogButton::Edit: - return m_ui->pbEdit; - case DialogButton::Save: - return m_ui->pbSave; - case DialogButton::Load: - return m_ui->pbLoad; - case DialogButton::Show: - return m_ui->pbShow; - case DialogButton::Import: - return m_ui->pbImport; - case DialogButton::Export: - return m_ui->pbExport; - case DialogButton::Library: - return m_ui->pbLibrary; - case DialogButton::CenterMap: - return m_ui->pbCenterMap; - case DialogButton::ZoomMapTo: - return m_ui->pbZoomMapTo; - case DialogButton::Print: - return m_ui->pbPrint; -#ifdef LTS_DEBUG - case DialogButton::_debug_: - return m_ui->_pbChanges; -#endif - } - Q_ASSERT(false); - return nullptr; -} - - -void LtsDialogButtonBox::slotRemoveClicked() -{ - if (LtsMessageDialog::showMessage(this, - tr("Are you sure you want to delete the selected entry(ies)?"), - tr("Remove selected entry(ies)"), - Behaviour::Modal, - Button::Yes | Button::No) - == Button::Yes) - emit signalRemoveClicked(); -} - - -void LtsDialogButtonBox::setButtonsVisible(QPushButton *pb) -{ - if (!pb || (pb == m_ui->pbPrint && m_isTablet)) - return; - m_ui->line->setVisible(true); - pb->setVisible(true); -} - - -void LtsDialogButtonBox::setButtonVisible(DialogButton button, bool visible) -{ - QPushButton *pb = getButton(button); - if (!pb) - return; - if (m_isTablet && pb == m_ui->pbPrint) - { - pb->setVisible(false); - return; - } - pb->setVisible(visible); -} - - -bool LtsDialogButtonBox::isButtonVisible(DialogButton button) const -{ - const QPushButton *pb = getButton(button); - return (pb ? pb->isVisible() : false); -} - - -void LtsDialogButtonBox::setButtonEnabled(DialogButton button, bool enabled) -{ - QPushButton *pb = getButton(button); - - switch (button) - { - case DialogButton::Close: - case DialogButton::Ok: - case DialogButton::Apply: - - - { -#ifdef LTS_DEBUG - LtsMessageDialog::showMessage(this, QStringLiteral("Hier stimmt etwas nicht. Bitte MIKE bescheid geben WAS geklickt oder gemacht wurde als dieser Dialog auftauchte, damit ich es fixen kann. Danke."), QStringLiteral(">>> STOP <<<")); -#endif - break; - } - - default: - break; - } - - if (pb) - pb->setEnabled(enabled); -} - - -bool LtsDialogButtonBox::isButtonEnabled(DialogButton button) const -{ - auto *that = const_cast(this); - const QPushButton *pb = that->getButton(button); - return (pb ? pb->isEnabled() : false); -} -} diff --git a/Lts/Common/LtsWidgets/lts_dialog_button_box.h b/Lts/Common/LtsWidgets/lts_dialog_button_box.h deleted file mode 100755 index d56da40ac..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_button_box.h +++ /dev/null @@ -1,196 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" -#include "lts_message_dialog.h" - -#include -#include - -namespace Ui -{ -class LtsDialogButtonBox; -} - -namespace LtsWidgets -{ -enum class DialogButton -{ - - ReadOnly, - Ok, - Apply, - Close, - Yes, - No, - - - Remove, - Hide, - Add, - Help, - Edit, - Save, - Load, - Show, - Import, - Export, - CenterMap, - ZoomMapTo, - Library, - Print, - -#ifdef LTS_DEBUG - _debug_, -#endif -}; - - - - - - - -class LTSWIDGETS_EXPORT LtsDialogButtonBox : public QWidget -{ - Q_OBJECT - -public: - explicit LtsDialogButtonBox(QWidget *parent); - ~LtsDialogButtonBox(); - - - - template - void connectButton(DialogButton button, const typename QtPrivate::FunctionPointer::Object *receiver, - Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) - { - - { - switch (button) - { -#ifdef LTS_DEBUG - case DialogButton::_debug_: -#endif - case DialogButton::Close: - case DialogButton::Ok: - case DialogButton::ReadOnly: - case DialogButton::Apply: - case DialogButton::Help: - { - LtsMessageDialog::showMessage(this, - QStringLiteral("Bitte den fuer das Plugin verantwortlichen kontaktieren:
" - "m_buttonBox nicht mit Ok, Apply, Help, _debug_ oder Close verbinden
" - "Note: Bitte nur m_buttonBox verwenden, keine LtsDialogButtonBox selber erstellen" - "Ok & Apply werden automatisch zur virtual Methode 'apply()' im LtsDialog weitergeleitet.
" - "Close schließt einfach den Dialog - im Destruktor oder in 'aboutToClose()' ist der 'Close Code' gut aufgehoben.
" - "
" - "FIXEN !!!!!"), - QStringLiteral("--- STOP --- !!! FIXEN !!! ---")); - break; - } - - default: - break; - } - } - - - QPushButton *theSender = getButton(button); - - if (!theSender) - return; - - - switch (button) - { - case DialogButton::Remove: - { - connect(this, &LtsDialogButtonBox::signalRemoveClicked, receiver, slot, type); - connect(theSender, &QPushButton::clicked, this, &LtsDialogButtonBox::slotRemoveClicked); - - break; - } - case DialogButton::Export: - case DialogButton::Import: - { - if (m_isTablet) - { - theSender->setVisible(false); - return; - } - Q_FALLTHROUGH(); - } - default: - connect(theSender, &QPushButton::clicked, receiver, slot, type); - break; - } - - setButtonsVisible(theSender); - } - template - bool connectLibraryButton(const typename QtPrivate::FunctionPointer::Object *receiver, - Func1 slotLoad, - Func2 slotSave, - Qt::ConnectionType type = Qt::AutoConnection) - { - QPushButton *theSender = getButton(DialogButton::Library); - if (!theSender) - return false; - if (m_isTablet) - { - theSender->setVisible(false); - return true; - } - setButtonsVisible(theSender); - type = static_cast(type | Qt::UniqueConnection); - return (connect(this, &LtsDialogButtonBox::signalLibraryLoadClicked, receiver, slotLoad, type) && - connect(this, &LtsDialogButtonBox::signalLibrarySaveClicked, receiver, slotSave, type)); - } - - - - void setText(DialogButton button, const QString &text); - - void setToolTip(DialogButton button, const QString &text); - void setIcon(DialogButton button, const QIcon &icon); - void setMenu(DialogButton button, QMenu *menu); - - - - void setButtonEnabled(DialogButton button, bool enabled = true); - bool isButtonEnabled(DialogButton button) const; - - void setButtonVisible(DialogButton button, bool visible = true); - bool isButtonVisible(DialogButton button) const; - - - - QPushButton *getButton(DialogButton button) const; - - - - void _setVisibilityForAll(bool visible); - -signals: - void signalRemoveClicked(); - void signalHelpClicked(); - void signalLibraryLoadClicked(); - void signalLibrarySaveClicked(); - -private slots: - void slotRemoveClicked(); - -private: - const bool m_isTablet; - - - - void setButtonsVisible(QPushButton *pb); - - Ui::LtsDialogButtonBox *m_ui; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_dialog_button_box.ui b/Lts/Common/LtsWidgets/lts_dialog_button_box.ui deleted file mode 100755 index 32152ae7f..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_button_box.ui +++ /dev/null @@ -1,326 +0,0 @@ - - - LtsDialogButtonBox - - - - 0 - 0 - 1706 - 46 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - [debug] - - - - - - - - - - - :/style/help_index.svg:/style/help_index.svg - - - - - - - Print - - - - :/style/freq_plan.svg:/style/freq_plan.svg - - - - - - - Library - - - - :/style/library.svg:/style/library.svg - - - - - - - Import - - - - :/style/export_documents.svg:/style/export_documents.svg - - - - - - - Export - - - - :/style/import_documents.svg:/style/import_documents.svg - - - - - - - Center Map - - - - :/style/center_to_object.svg:/style/center_to_object.svg - - - - - - - Zoom to - - - - :/style/zoom_map_to.svg:/style/zoom_map_to.svg - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - ReadOnly - - - - :/style/warning.svg:/style/warning.svg - - - - - - - OK - - - - :/style/dialog-ok.svg:/style/dialog-ok.svg - - - - - - - Apply - - - - :/style/dialog-ok.svg:/style/dialog-ok.svg - - - - - - - Load - - - - :/style/export_documents.svg:/style/export_documents.svg - - - - - - - Save - - - - :/style/import_documents.svg:/style/import_documents.svg - - - - - - - Add - - - - :/style/document-new.svg:/style/document-new.svg - - - - - - - Edit - - - - :/style/document-edit.svg:/style/document-edit.svg - - - - - - - Show - - - - :/style/filter_map_settings.svg:/style/filter_map_settings.svg - - - - - - - Yes - - - - :/style/dialog-ok.svg:/style/dialog-ok.svg - - - - - - - No - - - - :/style/dialog-close.svg:/style/dialog-close.svg - - - - - - - Remove - - - - :/style/media-vcr-nopause.svg:/style/media-vcr-nopause.svg - - - - - - - Hide - - - - :/style/show_building_configuration.svg:/style/show_building_configuration.svg - - - - - - - Close - - - - :/style/dialog-close.svg:/style/dialog-close.svg - - - Ctrl+W - - - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_dialog_helper.cpp b/Lts/Common/LtsWidgets/lts_dialog_helper.cpp deleted file mode 100755 index c9c6820eb..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_helper.cpp +++ /dev/null @@ -1,172 +0,0 @@ - - - -#include "lts_dialog_helper.h" -#include "lts_dialog.h" -#include "ui_lts_dialog_helper.h" - -#include -#include -#include - - -namespace LtsWidgets -{ -class LDHScrollBar : public QScrollBar -{ - Q_OBJECT -public: - using QScrollBar::QScrollBar; - - void showEvent(QShowEvent *e) override - { - QScrollBar::showEvent(e); - emit scrollbarVisiblityChanged(true); - } - void hideEvent(QHideEvent *e) override - { - QScrollBar::hideEvent(e); - emit scrollbarVisiblityChanged(false); - } -Q_SIGNALS: - void scrollbarVisiblityChanged(bool visible); -}; - - -LtsDialogHelper::LtsDialogHelper(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::LtsDialogHelper()) - , m_dialog(nullptr) -{ - m_ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - auto hScroll = new LDHScrollBar(this); - auto vScroll = new LDHScrollBar(this); - m_ui->scrollArea->setHorizontalScrollBar(hScroll); - m_ui->scrollArea->setVerticalScrollBar(vScroll); - connect(hScroll, &LDHScrollBar::scrollbarVisiblityChanged, - this, &LtsDialogHelper::onHScrollBarVisibilityChanged); - connect(vScroll, &LDHScrollBar::scrollbarVisiblityChanged, - this, &LtsDialogHelper::onVScrollBarVisibilityChanged); - -#ifdef TABLET_MODE - QList scrollWidgets = findChildren(); - - for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i) - { - QWidget *widget = *i; - - QVariant isConst = widget->property("const"); - if (isConst.isValid() && isConst.toBool()) - continue; - - LtsWidgets::addTouchScroll(widget); - } -#endif - m_ui->buttonBox->setMinimumHeight(m_ui->buttonBox->sizeHint().height()); -} - - -LtsDialogHelper::~LtsDialogHelper() -{ - m_ui->scrollArea->horizontalScrollBar()->disconnect(this); - m_ui->scrollArea->verticalScrollBar()->disconnect(this); - delete m_ui; -} - - -LtsDialogButtonBox *LtsDialogHelper::getButtonBox() -{ - return m_ui->buttonBox; -} - - -void LtsDialogHelper::setDialog(LtsDialog *dialog) -{ - if (m_dialog != nullptr) - return; - - m_dialog = dialog; - m_dialog->setContentsMargins(QMargins()); - if (m_dialog->layout()) - m_dialog->layout()->setContentsMargins(QMargins()); - m_ui->scrollArea->setWidget(m_dialog); - - - { - connect(dialog, &LtsDialog::destroyed, this, [this]() - { - m_dialog = nullptr; - deleteLater(); - }); - } - - - -} - -LtsDialog *LtsDialogHelper::getDialog() -{ - return m_dialog; -} - -void LtsDialogHelper::retranslate() -{ - if (m_dialog == nullptr) - return; - - m_dialog->setContentsMargins(QMargins()); - if (m_dialog->layout()) - m_dialog->layout()->setContentsMargins(QMargins()); - - bool oldChangesFlag = m_dialog->m_changesMade; - - - - m_dialog->retranslate(); - - if (oldChangesFlag) - m_dialog->slotChangesMade(); - else - m_dialog->resetChanges(); -} - - -void LtsDialogHelper::resizeEvent(QResizeEvent *e) -{ - emit signalSizeChanged(e->size()); -} - - -void LtsDialogHelper::changeEvent(QEvent *e) -{ - QWidget::changeEvent(e); - if (e->type() == QEvent::LanguageChange) - retranslate(); -} - - -void LtsDialogHelper::onHScrollBarVisibilityChanged(bool visible) -{ - if (m_dialog == nullptr || !m_dialog->layout()) - return; - - QMargins m = m_dialog->layout()->contentsMargins(); - m.setBottom(visible ? -1 : 0); - m_dialog->layout()->setContentsMargins(m); -} - - -void LtsDialogHelper::onVScrollBarVisibilityChanged(bool visible) -{ - if (m_dialog == nullptr || !m_dialog->layout()) - return; - - QMargins m = m_dialog->layout()->contentsMargins(); - m.setRight(visible ? -1 : 0); - m_dialog->layout()->setContentsMargins(m); -} - -} - -#include "lts_dialog_helper.moc" diff --git a/Lts/Common/LtsWidgets/lts_dialog_helper.h b/Lts/Common/LtsWidgets/lts_dialog_helper.h deleted file mode 100755 index c171e3532..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_helper.h +++ /dev/null @@ -1,51 +0,0 @@ - - - -#pragma once - -#include -#include - - -namespace Ui -{ -class LtsDialogHelper; -} - - -namespace LtsWidgets -{ -class LtsDialogButtonBox; -class LtsDialog; - - - -class LtsDialogHelper : public QWidget -{ - Q_OBJECT - -public: - explicit LtsDialogHelper(QWidget *parent = nullptr); - ~LtsDialogHelper(); - - LtsDialogButtonBox *getButtonBox(); - void setDialog(LtsDialog *dialog); - LtsDialog *getDialog(); - bool compare(const LtsDialog *dialog) const { return (dialog == m_dialog); } - void retranslate(); - -signals: - void signalSizeChanged(const QSize &size); - -private: - void resizeEvent(QResizeEvent *e) override; - void changeEvent(QEvent *e) override; - void onHScrollBarVisibilityChanged(bool visible); - void onVScrollBarVisibilityChanged(bool visible); - - Ui::LtsDialogHelper *m_ui; - LtsDialog *m_dialog; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_dialog_helper.ui b/Lts/Common/LtsWidgets/lts_dialog_helper.ui deleted file mode 100755 index ef1550ab4..000000000 --- a/Lts/Common/LtsWidgets/lts_dialog_helper.ui +++ /dev/null @@ -1,70 +0,0 @@ - - - LtsDialogHelper - - - - 0 - 0 - 456 - 306 - - - - - 0 - 0 - - - - - - - - - - QFrame::NoFrame - - - Qt::ScrollBarAsNeeded - - - true - - - - - - - - 0 - 0 - - - - - 0 - 40 - - - - - 16777215 - 150 - - - - - - - - - LtsWidgets::LtsDialogButtonBox - QWidget -
LtsWidgets/lts_dialog_button_box.h
- 1 -
-
- - -
diff --git a/Lts/Common/LtsWidgets/lts_drag_icon.cpp b/Lts/Common/LtsWidgets/lts_drag_icon.cpp deleted file mode 100755 index 9dad4ce61..000000000 --- a/Lts/Common/LtsWidgets/lts_drag_icon.cpp +++ /dev/null @@ -1,489 +0,0 @@ - - - -#include "lts_drag_icon.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsWidgets -{ - -class LtsToolButton : public QToolButton -{ - Q_OBJECT -public: - explicit LtsToolButton(QWidget *parent) - : QToolButton(parent) - , m_sameWidgetDrop(false) - , m_isDraggable(true) - , m_startPos(QPoint()) - { - setAutoFillBackground(false); - setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - } - - virtual ~LtsToolButton() = default; - - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void setSameWidgetDrop(bool drop) { m_sameWidgetDrop = drop; } - bool sameWidgetDrop() const { return m_sameWidgetDrop; } - void setDraggable(bool isDraggable) { m_isDraggable = isDraggable; } - void dropEvent(QDropEvent *event) override - { - emit newDropEvent(event); - QToolButton::dropEvent(event); - } - void addMimeData(const QString &mimeType, const QByteArray &data); - bool removeMimeData(const QString &mimeType); - void removeAllMimeData() { m_mimeData.clear(); } - -protected: - void initButton(); - -signals: - void newDropEvent(QDropEvent *e); - void dropSuc(); - -private: - void performDrag(QMouseEvent *event); - bool m_sameWidgetDrop; - bool m_isDraggable; - QPoint m_startPos; - QMap m_mimeData; - -}; - - - -LtsDragIcon::LtsDragIcon(QWidget *parent) - : QWidget(parent) - , m_lay(nullptr) - , m_toolButton(nullptr) - , m_icon() - , m_dragLabel(new QLabel(this)) - , m_text() - , m_style(ButtonStyle::eAll) - , m_forceFixedSize(false) - , m_isDraggable(true) - , m_dropHandlerKey(0L) - , m_buttonSize(ButtonSize::eDefault) - , m_showDragIcon(true) -{ - setAutoFillBackground(false); - initButton(); -} - - -void LtsDragIcon::setStyleToButton() -{ - if (nullptr == m_toolButton) return; - - switch (m_style) - { - case ButtonStyle::eIconOnly: - { - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - setFixedSize(QSize(static_cast(m_buttonSize), static_cast(m_buttonSize))); - m_toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly); - } break; - - case ButtonStyle::eTextOnly: - { - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - setFixedSize( QSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX) ); - m_toolButton->setToolButtonStyle(Qt::ToolButtonTextOnly); - layout()->setSizeConstraint( QLayout::SetDefaultConstraint ); - } break; - - case ButtonStyle::eTextNextToIcon: - { - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - setFixedSize( QSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX) ); - m_toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - layout()->setSizeConstraint( QLayout::SetDefaultConstraint ); - } break; - - default: break; - } - -} - - -void LtsDragIcon::initButton() -{ - setMinimumSize(static_cast(m_buttonSize), static_cast(m_buttonSize)); - - delete m_toolButton; - delete m_lay; - delete layout(); - - m_toolButton = new LtsToolButton(this); - m_toolButton->setDraggable(m_isDraggable); - connect(m_toolButton, &LtsToolButton::clicked, this, &LtsDragIcon::clicked); - connect(m_toolButton, &LtsToolButton::toggled, this, &LtsDragIcon::toggled); - connect(m_toolButton, &LtsToolButton::newDropEvent, this, &LtsDragIcon::newDropEvent); - connect(m_toolButton, &LtsToolButton::dropSuc, this, &LtsDragIcon::dropSuc); - - m_toolButton->setIcon(modifyIcon(m_icon)); - m_toolButton->setText(m_text); - generateToolTip(); - m_toolButton->setIconSize(QSize(static_cast(m_buttonSize), static_cast(m_buttonSize))); - - - QVBoxLayout *box = new QVBoxLayout(this); - box->setMargin(0); - box->setSpacing(0); - box->addWidget(m_toolButton); - - this->setLayout(box); - - m_toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - - if (m_forceFixedSize) - { - m_toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - setFixedSize(QSize(static_cast(m_buttonSize), static_cast(m_buttonSize))); - } - else - { - m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - } - - setStyleToButton(); -} - -void LtsDragIcon::addToButtonGroup(QButtonGroup *btnGrp, int btnGrpId) -{ - btnGrp->addButton(m_toolButton, btnGrpId); -} - - -void LtsDragIcon::setIcon(const QIcon &icon) -{ - m_icon = icon; - (m_toolButton) ? m_toolButton->setIcon(modifyIcon(m_icon)) : initButton(); -} - - -void LtsDragIcon::setMenu(QMenu *menu) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->setMenu(menu); -} - -QMenu *LtsDragIcon::menu() const -{ - if (nullptr == m_toolButton) - return nullptr; - - return m_toolButton->menu(); -} - - -void LtsDragIcon::setText(const QString &text) -{ - m_text = text; - (m_toolButton) ? generateToolTip() : initButton(); -} - -void LtsDragIcon::setContext(const QString &context) -{ - m_context = context; - generateToolTip(); -} - -void LtsDragIcon::generateToolTip() -{ - if (!m_toolButton || m_text.isEmpty()) - return; - - QString text(m_text); - if (!m_context.isEmpty()) - text.prepend(m_context + QLatin1String(" - ")); - - m_toolButton->setText(m_text); - m_toolButton->setToolTip(text); -} - -bool LtsDragIcon::isCheckable() -{ - return (m_toolButton) ? m_toolButton->isCheckable() : false; -} - -void LtsDragIcon::setDraggable(bool draggable) -{ - m_isDraggable = draggable; - m_toolButton->setDraggable(m_isDraggable); -} - -void LtsDragIcon::setCheckable(bool checkable) -{ - m_toolButton->setCheckable(checkable); -} - -bool LtsDragIcon::isChecked() const -{ - return (m_toolButton) ? m_toolButton->isChecked() : false; -} - -void LtsDragIcon::setChecked(bool checked) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->setChecked(checked); -} - -void LtsDragIcon::setSameWidgetDrop(bool drop) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->setSameWidgetDrop(drop); -} - -bool LtsDragIcon::sameWidgetDrop() const -{ - if (nullptr == m_toolButton) - return false; - return m_toolButton->sameWidgetDrop(); -} - -void LtsDragIcon::addMimeData(const QString &mimeType, const QByteArray &data) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->addMimeData(mimeType, data); -} - -bool LtsDragIcon::removeMimeData(const QString &mimeType) -{ - if (nullptr == m_toolButton) - initButton(); - return m_toolButton->removeMimeData(mimeType); -} - -void LtsDragIcon::removeAllMimeData() -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->removeAllMimeData(); -} - -void LtsDragIcon::setAcceptDrops(bool on) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->setAcceptDrops(on); -} - -void LtsDragIcon::dragEnterEvent(QDragEnterEvent *e) -{ - if (m_toolButton) - m_toolButton->dragEnterEvent(e); -} -void LtsDragIcon::dropEvent(QDropEvent *e) -{ - QWidget::dropEvent(e); -} - -void LtsDragIcon::setButtonStyle(ButtonStyle style) -{ - m_style = style; - setStyleToButton(); -} - -void LtsDragIcon::setIconSize(ButtonSize buttonSize) -{ - m_buttonSize = buttonSize; - (m_toolButton) ? m_toolButton->setIconSize(QSize(static_cast(m_buttonSize), static_cast(m_buttonSize))) : initButton(); -} - -void LtsDragIcon::setForceFixedSize(bool force) -{ - m_forceFixedSize = force; - initButton(); -} - -void LtsDragIcon::setButtonVisible(bool visible) -{ - if (nullptr == m_toolButton) - initButton(); - m_toolButton->setVisible(visible); -} - -bool LtsDragIcon::buttonVisible() const -{ - if (nullptr == m_toolButton) - return false; - return m_toolButton->isVisible(); -} - -void LtsDragIcon::setAction(QAction *action) -{ - QIcon icon = action->icon(); - if(!icon.isNull()) - m_icon = icon; - m_toolButton->setDefaultAction(action); - this->setObjectName(action->objectName()); -} - -void LtsDragIcon::changeEvent(QEvent *event) -{ - if (event->type() == QEvent::EnabledChange) - emit enabledChanged(isEnabled()); -} - - - -void LtsToolButton::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) - m_startPos = event->pos(); - QToolButton::mousePressEvent(event); -} - - -void LtsToolButton::mouseReleaseEvent(QMouseEvent *event) -{ - QToolButton::mouseReleaseEvent(event); -} - -void LtsToolButton::mouseMoveEvent(QMouseEvent *event) -{ - if (!m_isDraggable) - return; - if (event->buttons() & Qt::LeftButton) { - int distance = (event->pos() - m_startPos).manhattanLength(); - if (distance >= 5) - { - this->setDown(false); - performDrag(event); - return; - } - } - QToolButton::mouseMoveEvent(event); -} - -void LtsToolButton::performDrag(QMouseEvent *) -{ - QWidget *item = this; - if (item) - { - - LtsDragIcon *dragIcon = nullptr; - QString name; - - while (dragIcon == nullptr) - { - if ((!item->objectName().isEmpty()) && (name.isNull())) - { - name = item->objectName(); - } - item = item->parentWidget(); - if (nullptr == item) return; - dragIcon = dynamic_cast(item); - } - QMimeData *mimeData = new QMimeData(); - mimeData->setText(name); - for(auto it = m_mimeData.cbegin(); it != m_mimeData.cend(); ++it) - mimeData->setData(it.key(), it.value()); - - - if (dragIcon->dropHandlerKey() != 0L) - { - QByteArray data = QByteArray(QByteArray::number(dragIcon->dropHandlerKey())); - mimeData->setData(QLatin1String("DropHandlerPtr"),data); - } - - QDrag *drag = new QDrag(item); - - drag->setMimeData(mimeData); - - QAction *action = defaultAction(); - - if ((action) && (dragIcon->showDragIcon())) - { - drag->setPixmap(action->icon().pixmap(16,16) ); - } - else - { - drag->setPixmap(dragIcon->icon().pixmap(16, 16)); - } - Qt::DropAction drop = drag->exec(Qt::CopyAction | Qt::MoveAction); - if (drop == Qt::MoveAction || drop == Qt::CopyAction ) - emit dropSuc(); - } - -} - -void LtsToolButton::dragEnterEvent(QDragEnterEvent *event) -{ - QWidget *source = - qobject_cast(event->source()); - if (source && source != this) { - event->setDropAction(Qt::MoveAction); - event->accept(); - } - QWidget::dragEnterEvent(event); -} - -void LtsToolButton::addMimeData(const QString &mimeType, const QByteArray &data) -{ - m_mimeData.insert(mimeType,data); -} - -bool LtsToolButton::removeMimeData(const QString &mimeType) -{ - return (m_mimeData.remove(mimeType) > 0); -} - -#define SIZE 40 - -QIcon LtsDragIcon::modifyIcon(const QIcon &icon) -{ - if (m_modification.isEmpty()) - return icon; - - QPixmap iconImage(icon.pixmap(SIZE*0.8, SIZE*0.8)); - QPainter painter(&iconImage); - - painter.save(); - painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform); - painter.setBrush(QColor("red")); - painter.setPen(QPen(QColor("red"), 3)); - - QFont f; - f.setFamily(QStringLiteral("Monospace")); - f.setPointSize(12); - f.setBold(true); - painter.setFont(f); - - painter.drawText(QRect(0, 0, 28, 28), m_modification); - painter.restore(); - - return iconImage; -} - -} - - -#include "lts_drag_icon.moc" - diff --git a/Lts/Common/LtsWidgets/lts_drag_icon.h b/Lts/Common/LtsWidgets/lts_drag_icon.h deleted file mode 100755 index c4b99c892..000000000 --- a/Lts/Common/LtsWidgets/lts_drag_icon.h +++ /dev/null @@ -1,246 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_drop_handler.h" -#include "lts_widgets_export.h" - -#include -#include -#include - - -class QButtonGroup; -class QHBoxLayout; -class QLabel; -class QMenu; - -namespace LtsWidgets { - -class LtsToolButton; - - - -class LTSWIDGETS_EXPORT LtsDragIcon : public QWidget -{ -public: - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QString context READ context WRITE setContext) - Q_PROPERTY(QString identifier READ identifier WRITE setIdentifier) - Q_PROPERTY(QIcon icon READ icon WRITE setIcon) - Q_PROPERTY(LtsWidgets::Designer::ButtonSize eButtonSize READ iconSizeDesigner WRITE setIconSizeDesigner) - Q_PROPERTY(bool eForceFixedSize READ isForceFixedSize WRITE setForceFixedSize) - Q_PROPERTY(bool isCheckable READ isCheckable WRITE setCheckable) - Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) - Q_PROPERTY(bool showDragIcon READ showDragIcon WRITE setShowDragIcon) - Q_PROPERTY(bool isDraggable READ isDraggable WRITE setDraggable) - - - - Q_PROPERTY(LtsWidgets::Designer::ButtonStyle eStyle READ buttonStyleDesigner WRITE setButtonStyleDesigner) - - - - - void setButtonStyleDesigner(Designer::ButtonStyle t) { setButtonStyle(static_cast(t)); } - - - - Designer::ButtonStyle buttonStyleDesigner() const { return static_cast(buttonStyle()); } - - - - void setIconSizeDesigner(Designer::ButtonSize t) { setIconSize(static_cast(t)); } - - - - Designer::ButtonSize iconSizeDesigner() const { return static_cast(iconSize()); } - -public: - - - explicit LtsDragIcon(QWidget *parent); - - virtual ~LtsDragIcon() = default; - - - - void setMenu(QMenu *menu); - - - - QMenu* menu() const; - - - - void setText(const QString &text); - - - - QString text() const { return m_text; } - - - - void setContext(const QString &context); - - - - QString context() const { return m_context; } - - - - void setCheckable(bool checkable); - - - - bool isCheckable(); - - - - - void setDraggable(bool draggable); - - - - bool isDraggable() const { return m_isDraggable; } - - - - void setChecked(bool checked); - - - - bool isChecked() const; - - - - void setShowDragIcon(bool showDragIcon) { m_showDragIcon = showDragIcon; } - bool showDragIcon() const { return m_showDragIcon; } - - - - void setSameWidgetDrop(bool drop); - - - - bool sameWidgetDrop() const; - - - - void setButtonStyle(ButtonStyle style); - - - - ButtonStyle buttonStyle() const { return m_style; } - - - - void setIconSize(ButtonSize buttonSize); - - - - ButtonSize iconSize() const { return m_buttonSize; } - - - - void setButtonVisible(bool visible); - - - - bool buttonVisible() const; - - - - void setIcon(const QIcon &icon); - - - - QIcon icon() const { return m_icon; } - - - - void setForceFixedSize(bool force); - - - - bool isForceFixedSize() const { return m_forceFixedSize; } - - - - void setAction(QAction *action); - - - - void addToButtonGroup(QButtonGroup *btnGrp, int btnGrpId); - - - - void setDropHandlerKey(qulonglong key) { m_dropHandlerKey = key; } - - - - void addMimeData(const QString &mimeType, const QByteArray &data); - bool removeMimeData(const QString &mimeType); - void removeAllMimeData(); - - - - void setAcceptDrops(bool on); - - qulonglong dropHandlerKey() const { return m_dropHandlerKey; } - - - void setIdentifier(const QString &id) { m_identifier = id; } - QString identifier() const { return m_identifier; } - - void setModification(const QString &id) { m_modification = id; setIcon(m_icon); } - QString modification() const { return m_modification; } - void removeModification() { m_modification = QString(); setIcon(m_icon);} - -signals: - - - void clicked(); - - - - void toggled(bool); - void newDropEvent(QDropEvent *e); - void dropSuc(); - - void enabledChanged(bool); - -protected: - void dragEnterEvent(QDragEnterEvent *e) override; - void dropEvent(QDropEvent *e) override; - void initButton(); - -private: - void setStyleToButton(); - QIcon modifyIcon(const QIcon &icon); - void generateToolTip(); - -protected slots: - void changeEvent(QEvent *event) override; - -private: - QHBoxLayout *m_lay; - LtsToolButton *m_toolButton; - QIcon m_icon; - QLabel *m_dragLabel; - QString m_text; - QString m_identifier; - QString m_modification; - ButtonStyle m_style; - bool m_forceFixedSize; - bool m_isDraggable; - qulonglong m_dropHandlerKey; - ButtonSize m_buttonSize; - bool m_showDragIcon; - QString m_context; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_drop_handler.h b/Lts/Common/LtsWidgets/lts_drop_handler.h deleted file mode 100755 index e1df572b2..000000000 --- a/Lts/Common/LtsWidgets/lts_drop_handler.h +++ /dev/null @@ -1,31 +0,0 @@ - - - -#pragma once - - -class QMimeData; - - -namespace LtsWidgets -{ - - - class LtsDropHandler - { - public: - - LtsDropHandler() = default; - virtual ~LtsDropHandler() = default; - - LtsDropHandler( const LtsDropHandler & ) = delete; - LtsDropHandler & operator = ( const LtsDropHandler & ) = delete; - - - - virtual void doDrop( int x, int y, const QMimeData * mimeData ) const = 0; - - }; - - -} diff --git a/Lts/Common/LtsWidgets/lts_elided_label.cpp b/Lts/Common/LtsWidgets/lts_elided_label.cpp deleted file mode 100755 index 738d94886..000000000 --- a/Lts/Common/LtsWidgets/lts_elided_label.cpp +++ /dev/null @@ -1,60 +0,0 @@ - - - -#include "lts_elided_label.h" - -#include -#include - - -namespace LtsWidgets -{ -void LtsElidedLabel::setElideMode(Qt::TextElideMode elideMode) -{ - m_elideMode = elideMode; - m_cachedText.clear(); - update(); -} - -void LtsElidedLabel::resizeEvent(QResizeEvent *e) -{ - QLabel::resizeEvent(e); - m_cachedText.clear(); -} - -void LtsElidedLabel::paintEvent(QPaintEvent *e) -{ - if (m_elideMode == Qt::ElideNone) - return QLabel::paintEvent(e); - - updateCachedTexts(); - QLabel::setText(m_cachedElidedText); - QLabel::paintEvent(e); - QLabel::setText(m_cachedText); -} - -void LtsElidedLabel::updateCachedTexts() -{ - - const auto txt = text(); - if (m_cachedText == txt) - return; - m_cachedText = txt; - const QFontMetrics fm(fontMetrics()); - m_cachedElidedText = fm.elidedText(text(), - m_elideMode, - width(), - Qt::TextShowMnemonic); - if (!m_cachedText.isEmpty()) - { - const QString showFirstCharacter = m_cachedText.at(0) + QStringLiteral("..."); -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - const int width = fm.width(showFirstCharacter); -#else - const int width = fm.horizontalAdvance(showFirstCharacter); -#endif - setMinimumWidth(width + 1); - } -} - -} diff --git a/Lts/Common/LtsWidgets/lts_elided_label.h b/Lts/Common/LtsWidgets/lts_elided_label.h deleted file mode 100755 index a106c2b2a..000000000 --- a/Lts/Common/LtsWidgets/lts_elided_label.h +++ /dev/null @@ -1,36 +0,0 @@ - - - -#pragma once - -#include -#include - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsElidedLabel : public QLabel -{ - Q_OBJECT -public: - Q_PROPERTY(Qt::TextElideMode ElideMode READ elideMode WRITE setElideMode) - - using QLabel::QLabel; - - void setElideMode(Qt::TextElideMode elideMode); - Qt::TextElideMode elideMode() const { return m_elideMode; } - -protected: - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - void updateCachedTexts(); - -private: - Qt::TextElideMode m_elideMode = Qt::ElideRight; - QString m_cachedElidedText; - QString m_cachedText; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_events.cpp b/Lts/Common/LtsWidgets/lts_events.cpp deleted file mode 100755 index 9f5372bb0..000000000 --- a/Lts/Common/LtsWidgets/lts_events.cpp +++ /dev/null @@ -1,58 +0,0 @@ - - - -#include "lts_events.h" - -namespace LtsWidgets -{ -QEvent::Type LtsLinkEvent::eventTypeId() -{ - static QEvent::Type sLinkEventId = QEvent::None; - if (sLinkEventId == QEvent::None) - sLinkEventId = static_cast(QEvent::registerEventType()); - return sLinkEventId; -} - -QEvent::Type SaveImageEvent::saveImageEventId() -{ - static QEvent::Type sSaveImageEventId = QEvent::None; - if (sSaveImageEventId == QEvent::None) - sSaveImageEventId = static_cast(QEvent::registerEventType()); - return sSaveImageEventId; -} - -QEvent::Type SetMarkerEvent::setMarkerEventId() -{ - static QEvent::Type sSetMarkerEventId = QEvent::None; - if (sSetMarkerEventId == QEvent::None) - sSetMarkerEventId = static_cast(QEvent::registerEventType()); - return sSetMarkerEventId; -} - -QEvent::Type AddMarkerEvent::addMarkerEventId() -{ - static QEvent::Type sAddMarkerEventId = QEvent::None; - if (sAddMarkerEventId == QEvent::None) - sAddMarkerEventId = static_cast(QEvent::registerEventType()); - return sAddMarkerEventId; -} - -QEvent::Type AddSnapshotEvent::addSnapshotEventId() -{ - static QEvent::Type eventId = QEvent::None; - if (eventId == QEvent::None) - eventId = static_cast(QEvent::registerEventType()); - return eventId; -} - -QEvent::Type SaveSnapshotEvent::saveSnapshotEventId() -{ - static QEvent::Type eventId = QEvent::None; - if (eventId == QEvent::None) - eventId = static_cast(QEvent::registerEventType()); - return eventId; -} - - - -} diff --git a/Lts/Common/LtsWidgets/lts_events.h b/Lts/Common/LtsWidgets/lts_events.h deleted file mode 100755 index 60cd3cd58..000000000 --- a/Lts/Common/LtsWidgets/lts_events.h +++ /dev/null @@ -1,94 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsLinkEvent : public QEvent -{ -public: - explicit LtsLinkEvent(QUrl url, QWidget *sourceBaseLayoutWidget) - : QEvent(eventTypeId()) - , m_url(std::move(url)) - , m_sourceBaseLayoutWidget(sourceBaseLayoutWidget) - {} - - static QEvent::Type eventTypeId(); - QWidget *sourceBaseLayoutWidget() const { return m_sourceBaseLayoutWidget.data(); } - QUrl url() const { return m_url; } -private: - QUrl m_url; - QPointer m_sourceBaseLayoutWidget; -}; - -class LTSWIDGETS_EXPORT SaveImageEvent : public QEvent -{ -public: - SaveImageEvent(QWidget *w, QPixmap &&pm) - : QEvent(saveImageEventId()), m_widget(w), m_pix(std::move(pm)) - {} - - const QPixmap &pixmap() const { return m_pix; } - QWidget *widget() const { return m_widget.data(); } - static QEvent::Type saveImageEventId(); -private: - QPointer m_widget; - QPixmap m_pix; -}; - -class LTSWIDGETS_EXPORT AddMarkerEvent : public QEvent -{ -public: - AddMarkerEvent() - : QEvent(addMarkerEventId()) - {} - static QEvent::Type addMarkerEventId(); -}; - -class LTSWIDGETS_EXPORT SetMarkerEvent : public QEvent -{ -public: - SetMarkerEvent(const QString &description) - : QEvent(setMarkerEventId()), m_description(description) - {} - const QString &getDescription() const { return m_description; } - void setDescription(const QString &description) { m_description = description; } - static QEvent::Type setMarkerEventId(); -private: - QString m_description; -}; - -class LTSWIDGETS_EXPORT AddSnapshotEvent : public QEvent -{ -public: - AddSnapshotEvent() - : QEvent(addSnapshotEventId()) - {} - static QEvent::Type addSnapshotEventId(); -}; - -class LTSWIDGETS_EXPORT SaveSnapshotEvent : public QEvent -{ -public: - SaveSnapshotEvent(const QString &description) - : QEvent(saveSnapshotEventId()), m_description(description) - {} - const QString &getDescription() const { return m_description; } - void setDescription(const QString &description) { m_description = description; } - static QEvent::Type saveSnapshotEventId(); -private: - QString m_description; -}; - - -} diff --git a/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp b/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp deleted file mode 100755 index e26e9451d..000000000 --- a/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp +++ /dev/null @@ -1,153 +0,0 @@ - - - -#include "lts_frameless_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -LtsFramelessDialog::LtsFramelessDialog(QWidget *parent, const Behaviour &behaviour) - : QDialog(parent) -{ - - - Qt::WindowFlags wf; - - - switch (behaviour) - { - case Behaviour::Modal: - { - -#ifndef TABLET_MODE - setModal(true); - wf |= Qt::Dialog; -#else - wf |= Qt::Window; -#endif - wf |= Qt::WindowStaysOnTopHint; - break; - } - - case Behaviour::Popup: - wf |= Qt::Popup; - break; - } - - setWindowFlags(wf); - - - QTimer *t = new QTimer(this); - t->setInterval(250); - connect(t, &QTimer::timeout, - this, [this]() - { - - - - QSize s = size(); - setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, screenRectFromPoint(QCursor::pos()))); - }); - -} - - -LtsFramelessDialog::~LtsFramelessDialog() -{ - -} - - -QRect LtsFramelessDialog::screenRectFromPoint(const QPoint &pos) const -{ - const auto screens = QGuiApplication::screens(); - for (QScreen *screen : screens) - { - QRect s = screen->geometry().adjusted(-50, -50, 50, 50); - - bool contains = s.contains(pos); - - - - - - if (contains) - return screen->geometry(); - } - - return QRect(); -} - - -void LtsFramelessDialog::showEvent(QShowEvent *event) -{ - QDialog::showEvent(event); - QTimer::singleShot(0, this, &LtsFramelessDialog::onUpdateGeometry); - - -#ifdef TABLET_MODE - QList scrollWidgets = findChildren(); - - for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i) - { - QWidget *widget = *i; - - QVariant isConst = widget->property("const"); - if (isConst.isValid() && isConst.toBool()) - continue; - - addTouchScroll(widget); - } -#endif -} - - -void LtsFramelessDialog::addTouchScroll(QWidget *widget) -{ - if (!widget) - return; - widget->setAttribute(Qt::WA_AcceptTouchEvents); - - QScrollArea *sca = qobject_cast(widget); - if (sca) - { - QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture); - return; - } -} - - -void LtsFramelessDialog::onUpdateGeometry() -{ - QRect r; - QWidget *p = parentWidget(); - - if (p) - { - QWidget *window = p->window(); - QPoint global = window->mapToGlobal(QPoint(0,0)); - QSize size = window->size(); - - r = QRect(global, size); - } - else - { - r = QGuiApplication::primaryScreen()->geometry(); - } - - QSize s = size(); - setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, r)); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_frameless_dialog.h b/Lts/Common/LtsWidgets/lts_frameless_dialog.h deleted file mode 100755 index 39e7dff86..000000000 --- a/Lts/Common/LtsWidgets/lts_frameless_dialog.h +++ /dev/null @@ -1,46 +0,0 @@ - - - -#pragma once - -#include - -#include "lts_base.h" -#include "lts_widgets_export.h" - - -namespace LtsWidgets -{ - - -enum class Behaviour -{ - Popup = 0, - Modal, -}; - - - - -class LTSWIDGETS_EXPORT LtsFramelessDialog : public QDialog -{ - Q_OBJECT - -public: - - - explicit LtsFramelessDialog(QWidget *parent = nullptr, const Behaviour &behaviour = Behaviour::Popup); - virtual ~LtsFramelessDialog(); - -public Q_SLOTS: - void onUpdateGeometry(); -private: - QRect screenRectFromPoint(const QPoint &pos) const; - void addTouchScroll(QWidget *widget); - -protected: - void showEvent(QShowEvent *event) override; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp b/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp deleted file mode 100755 index b64f6b50e..000000000 --- a/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp +++ /dev/null @@ -1,225 +0,0 @@ - - -#include "lts_global_wait_dialog.h" -#include "lts_progress_dialog_helper.h" -#include "lts_overlay_loading.h" -#include "lts_dialog.h" -#include "lts_frameless_dialog.h" - -#include -#include - - -#ifdef Q_OS_UNIX -#include -#include -#include -#endif - - -namespace LtsWidgets -{ - -LtsGlobalWaitDialog::~LtsGlobalWaitDialog() -{ - delete m_progressDialog; -} - - -LtsGlobalWaitDialog::LtsGlobalWaitDialog() - : m_waitingCount(0) - , m_visible(true) - , m_progressDialog(new LtsProgressDialogHelper(nullptr)) - , m_hideTimer(new QTimer(this)) -{ - m_hideTimer->setSingleShot(true); - m_hideTimer->setInterval(500); - connect(m_hideTimer, &QTimer::timeout, - this, &LtsGlobalWaitDialog::hideIt); -} - - -void LtsGlobalWaitDialog::unarm() -{ - m_armed = false; - hideIt(); -} - - -void LtsGlobalWaitDialog::rearm() -{ - m_armed = true; - setVisible(true); -} - - -void LtsGlobalWaitDialog::setVisible(bool visible) -{ - if (m_waitingCount > 0 && visible) - showIt(); - - if (m_waitingCount < 1 && !visible) - hideIt(); -} - - -void LtsGlobalWaitDialog::showIt() -{ - if (m_progressDialog->isShown()) - return; - - m_hideTimer->stop(); - if (m_armed) - { - m_progressDialog->slotShowWaitForReplyWindow(); - } -} - - -void LtsGlobalWaitDialog::hideIt() -{ - if (!m_progressDialog->isShown()) - return; - - m_hideTimer->stop(); - m_progressDialog->slotStopWaitForReplyWindow(); -} - - -void bt() -{ -#ifdef Q_OS_UNIX - void *callstack[128]; - const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]); - int nFrames = backtrace(callstack, nMaxFrames); - char **symbols = backtrace_symbols(callstack, nFrames); - - for (int i = 1; i < nFrames; i++) - { - QString address(QStringLiteral("0x%1").arg(QString::number(*(quintptr *)callstack[i], 16))); - QString function; - int offset = -1; - - Dl_info info; - if (dladdr(callstack[i], &info) && info.dli_sname) - { - char *demangled = nullptr; - int status = -1; - if (info.dli_sname[0] == '_') - demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); - - function = QString::fromUtf8(status == 0 ? demangled : info.dli_sname == 0 ? symbols[i] : info.dli_sname); - offset = (char *)callstack[i] - (char *)info.dli_saddr; - - free(demangled); - } - else - { - function = QString::fromUtf8(symbols[i]); - } - - - } - - free(symbols); - - - -#endif -} - - -void LtsGlobalWaitDialog::wait(QWidget *parent, const QString &customText) -{ - m_progressDialog->setParentWidget(parent); - if (m_waitingCount < 1 && m_visible) - showIt(); - - setCustomText(customText); - - m_waitingCount++; - - - - -} - - -void LtsGlobalWaitDialog::setCustomText(const QString &customText) -{ - m_progressDialog->setCustomText(customText); -} - - -void LtsGlobalWaitDialog::done() -{ - if (m_waitingCount == 0) - { - - m_hideTimer->start(); - return; - } - - Q_ASSERT(m_waitingCount > 0); - - m_waitingCount--; - - - if (m_waitingCount == 0) - m_hideTimer->start(); -} - -static LtsGlobalWaitDialog *s_gwdInstance = nullptr; -void LtsGlobalWaitDialog::cleanupGwdInstance() -{ - delete s_gwdInstance; - s_gwdInstance = nullptr; -} - -LtsGlobalWaitDialog *LtsGlobalWaitDialog::getInstance() -{ - if (!s_gwdInstance) - { - s_gwdInstance = new LtsGlobalWaitDialog; - qAddPostRoutine(&LtsGlobalWaitDialog::cleanupGwdInstance); - } - return s_gwdInstance; -} -} - - - -Waiter::Waiter(const QString &customText) -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(nullptr, customText); -} - - -Waiter::Waiter(LtsWidgets::LtsDialog *parent, const QString &customText) - : m_parent(parent) -{ - m_oldClosableState = m_parent->isClosable(); - m_parent->setClosable(false); - m_parent->setCurrentlyBlocking(true); - auto *wait = new LtsWidgets::LtsOverlayLoading; - wait->setText(customText.isEmpty() ? QObject::tr("Waiting for server reply...") : customText); - m_parent->setOverlay(wait); - m_loadingOverlay = wait; -} - - -Waiter::~Waiter() -{ - if (m_loadingOverlay == nullptr) - { - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); - return; - } - - m_parent->setClosable(m_oldClosableState); - m_parent->setCurrentlyBlocking(false); - m_loadingOverlay->deleteLater(); -} - - - diff --git a/Lts/Common/LtsWidgets/lts_global_wait_dialog.h b/Lts/Common/LtsWidgets/lts_global_wait_dialog.h deleted file mode 100755 index 67b689981..000000000 --- a/Lts/Common/LtsWidgets/lts_global_wait_dialog.h +++ /dev/null @@ -1,69 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -class QTimer; -class QWidget; - - -namespace LtsWidgets -{ -class LtsDialog; -class LtsOverlayLoading; -class LtsProgressDialogHelper; - - -class LTSWIDGETS_EXPORT LtsGlobalWaitDialog : public QObject -{ - Q_OBJECT - -public: - static LtsGlobalWaitDialog* getInstance(); - void wait(QWidget *parent, const QString &customText = QString()); - void wait(const QString &customText = QString()) { wait(nullptr, customText); } - void done(); - void setVisible(bool visible = true); - void setCustomText(const QString &customText = QString()); - void unarm(); - void rearm(); - -private: - friend struct LtsGlobalWaitDialogHandler; - - LtsGlobalWaitDialog(); - ~LtsGlobalWaitDialog(); - static void cleanupGwdInstance(); - - void showIt(); - void hideIt(); - - int m_waitingCount; - bool m_visible; - bool m_armed { true }; - LtsProgressDialogHelper *m_progressDialog; - QTimer *m_hideTimer; -}; - -} - - - - -class LTSWIDGETS_EXPORT Waiter -{ -public: - explicit Waiter(const QString &customText = {}); - explicit Waiter(LtsWidgets::LtsDialog *parent, const QString &customText = {}); - ~Waiter(); - -private: - LtsWidgets::LtsOverlayLoading *m_loadingOverlay { nullptr }; - LtsWidgets::LtsDialog *m_parent { nullptr }; - bool m_oldClosableState { false }; -}; diff --git a/Lts/Common/LtsWidgets/lts_grid_layout.cpp b/Lts/Common/LtsWidgets/lts_grid_layout.cpp deleted file mode 100755 index f4fd7a88f..000000000 --- a/Lts/Common/LtsWidgets/lts_grid_layout.cpp +++ /dev/null @@ -1,128 +0,0 @@ - - - -#include "lts_grid_layout.h" -#include - -namespace LtsWidgets -{ - -LtsGridLayout::LtsGridLayout(LtsWidgets::ButtonSize buttonSize, QWidget *parent, int maxColumn) - : QGridLayout(parent) - , m_buttonSize(buttonSize) - , m_defaultMaxColumn(maxColumn) - , m_maxColumn(maxColumn) - , m_colSize(0) - , m_autoSize(maxColumn < 0) -{ - setSizeConstraint(SizeConstraint::SetNoConstraint); - - setVerticalSpacing(1); - setMargin(0); -} - -void LtsGridLayout::add_widget(QWidget *widget) -{ - for (auto it = m_widgetList.cbegin(); it != m_widgetList.cend(); ++it) - { - if (*it == widget) - return; - } - connect(widget, &QWidget::destroyed, this, &LtsGridLayout::onWidgetDestroyed); - addWidgetInternal(widget); - m_widgetList.push_back(widget); -} - -void LtsGridLayout::setAutoSize(bool enable) -{ - m_autoSize = enable; - if (m_autoSize == false) - setMaxColumn(1); - else - { - m_maxColumn = -1; - m_oldSize = QSize(); - m_oldGeo = QRect(); - QResizeEvent e(sizeHint(), sizeHint()); - onParentResize(&e); - setGeometry(QRect(0, 0, sizeHint().width(), sizeHint().height())); - } -} - -void LtsGridLayout::setMaxColumn(int max) -{ - m_maxColumn = max; - m_defaultMaxColumn = max; - - - m_colSize = 0; - - for (QWidget *wid : qAsConst(m_widgetList)) - removeWidget(wid); - - for (QWidget *wid : qAsConst(m_widgetList)) - addWidgetInternal(wid); - -} - -void LtsGridLayout::addWidgetInternal(QWidget *widget) -{ - int current_row = 0; - int current_column = 0; - - while (QLayoutItem *item = itemAtPosition(current_row, current_column)) - { - if (!item->widget()) - { - removeItem(item); - continue; - } - - if (current_column == (m_maxColumn - 1)) - { - current_column = 0; - ++current_row; - } - else - ++current_column; - } - - if (current_row == 0) - m_colSize += 1; - - QGridLayout::addWidget(widget, current_row, current_column); -} - - -void LtsGridLayout::onParentResize(QResizeEvent *e) -{ - if (m_autoSize && m_oldSize != e->size()) - { - bool bigger = (e->size().width() > m_oldSize.width()); - m_oldSize = e->size(); - int newWidth = m_oldSize.width(); - int maxIconsInRow = newWidth / (static_cast(m_buttonSize) + 4); - - if (!bigger && newWidth <= (static_cast(m_buttonSize)*m_colSize - static_cast(m_buttonSize) / 2)) - maxIconsInRow--; - if (m_maxColumn != maxIconsInRow) - setMaxColumn(maxIconsInRow); - } -} - -void LtsGridLayout::onWidgetDestroyed() -{ - QWidget *s = qobject_cast(sender()); - for (auto it = m_widgetList.begin(); it != m_widgetList.end(); ++it) - { - if (*it == s) - { - QGridLayout::removeWidget(s); - m_widgetList.erase(it); - setMaxColumn(m_maxColumn); - break; - } - } -} - -} diff --git a/Lts/Common/LtsWidgets/lts_grid_layout.h b/Lts/Common/LtsWidgets/lts_grid_layout.h deleted file mode 100755 index 70949b856..000000000 --- a/Lts/Common/LtsWidgets/lts_grid_layout.h +++ /dev/null @@ -1,61 +0,0 @@ - - - -#pragma once -#include -#include - -#include "lts_widgets_export.h" -#include "lts_base.h" - -class QWidget; - -namespace LtsWidgets -{ - - -class LTSWIDGETS_EXPORT LtsGridLayout : public QGridLayout -{ - Q_OBJECT -public: - - - explicit LtsGridLayout(LtsWidgets::ButtonSize buttonSize, QWidget *parent = nullptr, int maxColumn = 1); - - - - void add_widget(QWidget *widget); - - - - void setAutoSize(bool enable); - - - - void setMaxColumn(int max); - - -private: - void addWidgetInternal(QWidget *widget); - -public Q_SLOTS: - void onParentResize(QResizeEvent *e); - - -private Q_SLOTS: - void onWidgetDestroyed(); -private: - - LtsWidgets::ButtonSize m_buttonSize; - int m_defaultMaxColumn; - int m_maxColumn; - int m_colSize; - QVector m_widgetList; - QRect m_oldGeo; - QSize m_oldSize; - bool m_autoSize; - - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp b/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp deleted file mode 100755 index 2c6f882ec..000000000 --- a/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp +++ /dev/null @@ -1,621 +0,0 @@ - - - -#include "lts_height_plot_widget.h" - -#include -#include -#include -#include -#include - -#include - - -static inline double getLength(const QPointF &p1, const QPointF &p2) -{ - const double x = p1.x() - p2.x(); - const double y = p1.y() - p2.y(); - return sqrt(x * x + y * y); -} - -class LtsHeightPlotWidget::Private -{ -public: - Private() - : m_length(0) - , m_minElevation(0) - , m_maxElevation(0) - , m_nullX(0) - , m_nullY(0) - , m_scaleX(0) - , m_scaleY(0) - , m_fillBrush(Qt::gray) - , m_axisPen(Qt::black) - , m_mousePosPen(Qt::blue) - , m_markerPen(Qt::magenta) - , iPositionHint(1) - { - m_axisPen.setWidth(1); - m_viewLinePen.setWidth(1); - m_textFont.setPixelSize(10); - m_markerPen.setWidth(2); - m_markerPen.setCapStyle(Qt::RoundCap); - m_mousePosPen.setStyle(Qt::DashDotDotLine); - m_viewLinePen.setStyle(Qt::DashLine); - } - void clearPixmap(); - - double getDataHeight(double pos) const; - QPointF getDataPos(double pos) const; - int findDataPointIdx(const QPointF &point) const; - void clear(); - void recalcData(); - void setData(const QVector > &data); - void setMarks(const QVector &data); - void resize(const QSize &newSize); - void repaint(); - void paintLines(QPainter &p) const; - - QVector< QPair< QPointF, double > > m_data; - QVector< QPointF > m_marks; - QVector< QPair< double, double > > m_heightList; - - double m_length; - double m_minElevation; - double m_maxElevation; - double m_nullX; - double m_nullY; - double m_scaleX; - double m_scaleY; - QPainterPath m_drawPoints; - QBrush m_fillBrush; - QPen m_axisPen; - QPen m_viewLinePen; - QPen m_mousePosPen; - QPen m_markerPen; - QFont m_textFont; - QPointF m_mousePos; - QPointF m_mouseLeftClickPos; - - QSize m_curSize; - QTransform m_trans1; - QTransform m_trans2; - QTransform m_transInverted; - QTransform m_transCombined; - - QPixmap m_pixmap; - - mutable int iPositionHint; -}; - -double LtsHeightPlotWidget::Private::getDataHeight(double pos) const -{ - if (m_heightList.count() <= 1) - return 0; - - if (pos <= 0.) - return m_heightList[0].second; - if (pos >= m_heightList[m_heightList.count() - 1].first) - return m_heightList[m_heightList.count() - 1].second; - - if (iPositionHint >= m_heightList.size()) - iPositionHint = 1; - if (pos <= m_heightList[iPositionHint].first) - { - while (iPositionHint > 1 && pos <= m_heightList[iPositionHint - 1].first) - --iPositionHint; - } - else - { - while (iPositionHint < m_heightList.count() - 1 && pos > m_heightList[iPositionHint].first) - ++iPositionHint; - } - const int iFound = iPositionHint; - - const double x = m_heightList[iFound].first - m_heightList[iFound - 1].first; - const double y = m_heightList[iFound].second - m_heightList[iFound - 1].second; - const double m = y / x; - const double n = m_heightList[iFound].second - m * m_heightList[iFound].first; - return m * pos + n; -} - -QPointF LtsHeightPlotWidget::Private::getDataPos(double pos) const -{ - if (m_heightList.count() <= 1) - return QPointF(); - - if (pos <= 0.) - return m_data[0].first; - if (pos >= m_heightList[m_heightList.count() - 1].first) - return m_data[m_heightList.count() - 1].first; - - if (pos <= m_heightList[iPositionHint].first) - { - while (iPositionHint > 1 && pos <= m_heightList[iPositionHint - 1].first) - --iPositionHint; - } - else - { - while (iPositionHint < m_heightList.count() - 1 && pos > m_heightList[iPositionHint].first) - ++iPositionHint; - } - const int iFound = iPositionHint; - - const double diff = m_heightList[iFound].first - m_heightList[iFound - 1].first; - double percentile = (pos - m_heightList[iFound - 1].first) / diff; - const QPointF &p1 = m_data[iFound - 1].first; - const QPointF p2 = m_data[iFound].first - m_data[iFound - 1].first; - return QPointF(p1.x() + p2.x() * percentile, p1.y() + p2.y() * percentile); -} - -int LtsHeightPlotWidget::Private::findDataPointIdx(const QPointF &point) const -{ - for (int i = 0; i < m_heightList.count() - 1; ++i) - { - - const double x = m_data[i + 1].first.x() - m_data[i].first.x(); - const double y = m_data[i + 1].first.y() - m_data[i].first.y(); - const double m = y / x; - const double n = m_data[i].first.y() - m * m_data[i].first.x(); - - - const double yRes = m * point.x() + n; - if (fabs(yRes - point.y()) < 1.) - { - - const double len = getLength(m_data[ i ].first, point); - if (len <= m_heightList[i].first) - return i; - } - } - return -1; -} - -void LtsHeightPlotWidget::Private::clear() -{ - m_data.clear(); - m_marks.clear(); - m_length = 0; - m_minElevation = 0; - m_maxElevation = 0; - m_scaleX = 0; - m_scaleY = 0; - m_drawPoints = QPainterPath(); - m_heightList.clear(); - m_curSize = QSize(); - - iPositionHint = 1; -} - -void LtsHeightPlotWidget::Private::clearPixmap() -{ - m_pixmap = QPixmap(); -} - -inline double log5(double x) -{ - return log10(x) / log10(5.); -} - -void LtsHeightPlotWidget::Private::recalcData() -{ - m_heightList.clear(); - m_drawPoints = QPainterPath(); - - if (m_data.isEmpty()) - { - clear(); - return; - } - - double length = 0; - double minElevation = m_data.at(0).second; - double maxElevation = minElevation; - - for (int i = 1; i < m_data.count(); ++i) - { - length += getLength(m_data.at(i - 1).first, m_data.at(i).first); - minElevation = std::min(minElevation, m_data.at(i).second); - maxElevation = std::max(maxElevation, m_data.at(i).second); - } - - if (minElevation < -10000.0) minElevation = -10000.0; - if (maxElevation < -10000.0) maxElevation = -10000.0; - - if (minElevation > 10000.0) minElevation = 10000.0; - if (maxElevation > 10000.0) maxElevation = 10000.0; - - if (qFuzzyCompare(minElevation, maxElevation)) - { - minElevation -= 1.0; - maxElevation += 1.0; - } - - - double n2 = pow(5, floor(log5(fabs(minElevation))) - 1); - if (qFuzzyIsNull(n2)) - n2 = 0.1; - - m_minElevation = lrint(floor(minElevation / n2) * n2); - m_maxElevation = lrint(ceil(maxElevation / n2) * n2); - m_scaleY = pow(10, floor(log10(m_maxElevation - m_minElevation)) - 1); - if (m_scaleY < 0.1) - m_scaleY = 0.1; - m_nullY = m_minElevation; - if (m_minElevation < 0 && m_maxElevation > 0) - m_nullY = 0; - - - const double n = pow(10, floor(log10(length))) / 2; - m_length = lrint(ceil(length / n) * n); - m_scaleX = pow(10, floor(log10(length)) - 1); - if (m_scaleX < 0.1) - m_scaleX = 0.1; - m_nullX = 0; - - double len = 0; - m_drawPoints.moveTo(len, m_data.at(0).second); - m_heightList.append(QPair(len, m_data.at(0).second)); - for (int i = 1; i < m_data.count(); ++i) - { - len += getLength(m_data.at(i - 1).first, m_data.at(i).first); - m_drawPoints.lineTo(len, m_data.at(i).second); - - m_heightList.append(QPair(len, m_data.at(i).second)); - - } - m_drawPoints.lineTo(len, m_minElevation); - m_drawPoints.lineTo(0, m_minElevation); - m_drawPoints.setFillRule(Qt::WindingFill); -} - -void LtsHeightPlotWidget::Private::setData(const QVector< QPair< QPointF, double > > &data) -{ - if (data.isEmpty()) - { - clear(); - return; - } - QSize tmp = m_curSize; - clear(); - - m_data = data; - - recalcData(); - resize(tmp); -} - - -void LtsHeightPlotWidget::Private::setMarks(const QVector< QPointF > &data) -{ - m_marks = data; - - recalcData(); - repaint(); -} - -void LtsHeightPlotWidget::Private::resize(const QSize &newSize) -{ - if (newSize == m_curSize) - return; - - m_curSize = newSize; - - const int border = 25; - const double width = m_curSize.width(); - const double height = m_curSize.height(); - - const double minY = m_minElevation - m_scaleY; - const double maxY = m_maxElevation + m_scaleY; - const double wertebereichY = maxY - minY; - - const double minX = 0 - m_scaleX; - const double maxX = m_length + m_scaleX; - const double wertebereichX = maxX - minX; - - m_trans1.reset(); - m_trans2.reset(); - - - m_trans1.translate(10, height); - - m_trans1.scale(1, -1); - - m_trans1.translate(border, border); - - m_trans1.scale(1, -1); - - - m_trans2.scale((width - 2 * border) / (wertebereichX), - (height - 2 * border) / (wertebereichY)); - - m_trans2.translate(0, minY); - - m_trans2.scale(1, -1); - - m_transInverted = m_trans1.inverted() * m_trans2.inverted(); - m_transCombined = m_trans2 * m_trans1; - - repaint(); -} - -void LtsHeightPlotWidget::Private::repaint() -{ - if (!m_curSize.isValid()) - return; - if (!m_heightList.count()) - return; - - QImage img(m_curSize, QImage::Format_ARGB32); - img.fill(Qt::transparent); - QPainter p(&img); - const QPainter::RenderHints hints = QPainter::HighQualityAntialiasing | - QPainter::SmoothPixmapTransform | - QPainter::TextAntialiasing | - QPainter::Antialiasing; - p.setRenderHints(hints); - p.setTransform(m_trans1); - - - p.setPen(QPen(m_fillBrush, 0.1)); - p.setBrush(m_fillBrush); - p.drawPath(m_trans2.map(m_drawPoints)); - p.setRenderHint(QPainter::Antialiasing, false); - - - p.setPen(m_markerPen); - for (int i = 0; i < m_marks.count(); ++i) - { - int posIdx = findDataPointIdx(m_marks.at(i)); - if (posIdx < 0) - continue; - double len = getLength(m_data.at(posIdx).first, m_marks.at(i)); - len += m_heightList.at(posIdx).first; - const double height = getDataHeight(len); - const QLineF l(len, m_minElevation, len, height); - - - p.drawLine(m_trans2.map(l)); - } - - - p.setPen(m_axisPen); - p.setFont(m_textFont); - - - p.drawLine(m_trans2.map(QPointF(m_nullX, m_minElevation - m_scaleY)), - m_trans2.map(QPointF(m_nullX, m_maxElevation + m_scaleY))); - - - for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY) - { - const qreal y = m_trans2.map(QPointF(0, i)).y(); - p.drawLine(QLineF(-2, y, 2, y)); - } - - - for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY * 5) - { - const qreal y = m_trans2.map(QPointF(0, i)).y(); - p.drawLine(QLineF(-4, y, 4, y)); - } - - QFontMetricsF fm(m_textFont); - for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY * 5) - { - if (qFuzzyCompare(i, m_nullY)) - continue; - const qreal y = m_trans2.map(QPointF(m_nullX, i)).y(); - const QPointF p1(-20, y); - const QString str = QString::number(i); - QRectF r = fm.boundingRect(str); - r.setWidth(fm.width(str)); - r.moveTo(p1.x() - r.width() / 2., p1.y() - r.height() / 2.); - p.drawText(r, str, Qt::AlignHCenter | Qt::AlignVCenter); - } - - - p.drawLine(m_trans2.map(QPointF(-m_scaleX, m_nullY)), - m_trans2.map(QPointF(m_scaleX + m_length, m_nullY))); - - - for (double i = m_scaleX; i <= m_length; i += m_scaleX) - { - const QPointF point = m_trans2.map(QPointF(i, m_nullY)); - p.drawLine(QLineF(point.x(), point.y() - 2, point.x(), point.y() + 2)); - } - - - for (double i = 0; i <= m_length; i += m_scaleX * 5) - { - const QPointF point = m_trans2.map(QPointF(i, m_nullY)); - p.drawLine(QLineF(point.x(), point.y() - 4, point.x(), point.y() + 4)); - } - - for (double i = 0; i <= m_length; i += m_scaleX * 5) - { - if (qFuzzyCompare(i, m_nullX)) - continue; - const QPointF point = m_trans2.map(QPointF(i, m_nullY)); - const QPointF p1(point.x(), point.y() + 15); - const QString str = QString::number(i); - QRectF r = fm.boundingRect(str); - r.setWidth(fm.width(str)); - r.moveTo(p1.x() - r.width() / 2., p1.y() - r.height() / 2.); - p.drawText(r, str, Qt::AlignHCenter | Qt::AlignVCenter); - } - p.end(); - m_pixmap = QPixmap::fromImage(img); -} - -void LtsHeightPlotWidget::Private::paintLines(QPainter &p) const -{ - if (m_heightList.count() > 0) - { - - QPointF pos = m_mousePos; - if (pos.x() < 0) - pos.setX(0); - if (pos.x() > m_heightList.at(m_heightList.count() - 1).first) - pos.setX(m_heightList.at(m_heightList.count() - 1).first); - - - - const double curHeight = getDataHeight(pos.x()); - p.setPen(m_mousePosPen); - p.drawLine(m_transCombined.map(QLineF(m_nullX, curHeight, pos.x(), curHeight))); - p.drawLine(m_transCombined.map(QLineF(pos.x(), m_nullY, pos.x(), curHeight))); - - - if (!m_mouseLeftClickPos.isNull()) - { - const double y = curHeight > pos.y() ? curHeight : pos.y(); - p.drawLine(m_transCombined.map(QLineF(m_nullX, y, pos.x(), y))); - p.drawLine(m_transCombined.map(QLineF(pos.x(), m_nullY, pos.x(), y))); - - p.setPen(m_viewLinePen); - const QPointF &c = m_mouseLeftClickPos; - p.drawLine(m_transCombined.map(QLineF(m_nullX, c.y(), c.x(), c.y()))); - p.drawLine(m_transCombined.map(QLineF(c.x(), m_nullY, c.x(), c.y()))); - - - const QLineF line(m_mouseLeftClickPos, QPointF(pos.x(), y)); - p.drawLine(m_transCombined.map(line)); - } - } -} - -LtsHeightPlotWidget::LtsHeightPlotWidget(QWidget *parent) - : QWidget(parent) - , d(new Private) -{ - setMinimumSize(300, 300); - resize(size()); - setMouseTracking(true); -} - -LtsHeightPlotWidget::~LtsHeightPlotWidget() -{ - delete d; -} - -void LtsHeightPlotWidget::clearView() -{ - d->clearPixmap(); - update(); -} - -void LtsHeightPlotWidget::setData(const QVector< QPair< QPointF, double > > &data) -{ - d->setData(data); - update(); -} - -void LtsHeightPlotWidget::setMarks(const QVector &data) -{ - d->setMarks(data); -} - -void LtsHeightPlotWidget::setBackgroundBrush(const QBrush &brush) -{ - QPalette pal = palette(); - pal.setBrush(QPalette::Background, brush); - setPalette(pal); -} - -void LtsHeightPlotWidget::setTextFont(const QFont &f) -{ - d->m_textFont = f; - d->repaint(); -} - -void LtsHeightPlotWidget::setAxisPen(const QPen &pen) -{ - d->m_axisPen = pen; - d->repaint(); -} - -void LtsHeightPlotWidget::setPositionPen(const QPen &pen) -{ - d->m_mousePosPen = pen; -} - -void LtsHeightPlotWidget::setViewLinePen(const QPen &pen) -{ - d->m_viewLinePen = pen; -} - -void LtsHeightPlotWidget::setContourBrush(const QBrush &brush) -{ - d->m_fillBrush = brush; - d->repaint(); -} - -void LtsHeightPlotWidget::setMarkerPen(const QPen &pen) -{ - d->m_markerPen = pen; - d->repaint(); -} - -void LtsHeightPlotWidget::resizeEvent(QResizeEvent *e) -{ - d->resize(size()); - QWidget::resizeEvent(e); -} - -void LtsHeightPlotWidget::paintEvent(QPaintEvent *e) -{ - Q_UNUSED(e); - QPainter p(this); - - const QPainter::RenderHints hints = QPainter::SmoothPixmapTransform | - QPainter::TextAntialiasing | - QPainter::Antialiasing; - p.setRenderHints(hints); - - - p.drawPixmap(0, 0, d->m_pixmap); - - - d->paintLines(p); - p.end(); -} - -void LtsHeightPlotWidget::mouseMoveEvent(QMouseEvent *e) -{ - QWidget::mouseMoveEvent(e); - if (!d->m_heightList.count()) - return; - - const QPointF pos = d->m_transInverted.map(e->localPos()); - emit mousePositionChanged(d->getDataPos(pos.x()), d->getDataHeight(pos.x()), pos.y()); - d->m_mousePos = pos; - update(); -} - -void LtsHeightPlotWidget::mouseReleaseEvent(QMouseEvent *e) -{ - QWidget::mouseReleaseEvent(e); - if (!d->m_heightList.count()) - return; - if (e->button() == Qt::LeftButton) - { - const QPointF &pos = d->m_mousePos; - if (pos.x() < 0) - return; - if (pos.x() > d->m_heightList.at(d->m_heightList.count() - 1).first) - return; - - const double curHeight = d->getDataHeight(pos.x()); - d->m_mouseLeftClickPos = QPointF(pos.x(), curHeight > pos.y() ? curHeight : pos.y()); - emit startLineOfSight(d->getDataPos(pos.x())); - } - else if (e->button() == Qt::RightButton) - { - d->m_mouseLeftClickPos = QPointF(); - emit stopLineOfSight(); - update(); - } -} - diff --git a/Lts/Common/LtsWidgets/lts_height_plot_widget.h b/Lts/Common/LtsWidgets/lts_height_plot_widget.h deleted file mode 100755 index 926599f43..000000000 --- a/Lts/Common/LtsWidgets/lts_height_plot_widget.h +++ /dev/null @@ -1,69 +0,0 @@ - - - -#pragma once - -#include -#include -#include -#include - -#include "lts_widgets_export.h" - -class LTSWIDGETS_EXPORT LtsHeightPlotWidget final : public QWidget -{ - Q_OBJECT -public: - explicit LtsHeightPlotWidget(QWidget *parent = nullptr); - virtual ~LtsHeightPlotWidget(); - - void clearView(); - - - - void setData(const QVector > &data); - - - void setMarks(const QVector &data); - - - - void setBackgroundBrush(const QBrush &brush); - - - void setTextFont(const QFont &f); - - - void setAxisPen(const QPen &pen); - - - void setPositionPen(const QPen &pen); - - - void setViewLinePen(const QPen &pen); - - - void setContourBrush(const QBrush &brush); - - - void setMarkerPen(const QPen &pen); - -Q_SIGNALS: - - - void mousePositionChanged(const QPointF &curPoint, double curHeight, double mouseHeight); - - - void startLineOfSight(const QPointF &startPoint); - - - void stopLineOfSight(); -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - - class Private; - Private *const d; -}; diff --git a/Lts/Common/LtsWidgets/lts_image_editor.cpp b/Lts/Common/LtsWidgets/lts_image_editor.cpp deleted file mode 100755 index c811a4187..000000000 --- a/Lts/Common/LtsWidgets/lts_image_editor.cpp +++ /dev/null @@ -1,116 +0,0 @@ - - - -#include "lts_image_editor.h" - -#include -#include -#include -#include -#include -#include - -using namespace LtsWidgets; - - -LtsImageEditor::LtsImageEditor(QWidget *parent) - : QWidget(parent) - , m_isHovered(false) - , m_menuEnabled(true) - , m_bDrawOuterRect(true) -{} - - -void LtsImageEditor::setPixmap(const QPixmap &pixmap) -{ - if (pixmap.isNull()) - m_pixmap = QPixmap(QStringLiteral(":style/noimage.png")); - else - m_pixmap = pixmap; - - update(); - emit imageChanged(); -} - - -void LtsImageEditor::enterEvent(QEvent *) -{ - - m_isHovered = true; - update(); -} - - -void LtsImageEditor::leaveEvent(QEvent *) -{ - - m_isHovered = false; - update(); -} - - -bool LtsImageEditor::event(QEvent *event) -{ - if (event->type() == QEvent::MouseButtonPress && m_menuEnabled) - { - emit signalClicked(); - event->accept(); - m_isHovered = false; - update(); - return true; - } - - return QWidget::event(event); -} - - -void LtsImageEditor::paintEvent(QPaintEvent* event) -{ - QPainter p(this); - - - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - QBrush brush = QColor(QStringLiteral("white")); - - - if (!m_pixmap.isNull()) - { - if (!(m_isHovered && m_menuEnabled)) - { - const QSize s(size()); - const QPixmap pixmap(m_pixmap.scaled(s, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - const int x = qRound((s.width() - pixmap.width()) / 2.); - const int y = qRound((s.height() - pixmap.height()) / 2.); - if (m_bDrawOuterRect) - { - p.setPen(QPen(QColor("gray"), 2)); - p.drawRect(rect()); - } - p.drawPixmap(x, y, pixmap); - event->accept(); - return; - } - } - - - - p.setPen(QPen(QColor("gray"), 2)); - p.setBrush(brush); - p.drawRect(rect()); - - - - if (m_isHovered && m_menuEnabled) - { - p.setPen(QPen(QColor(QStringLiteral("gray")), (size().height() / 48 * 2))); - - double xc = size().width()/2.012; - double yc = size().height()/2.012; - - p.drawLine(QPointF(xc - (xc/2.01f), yc - (yc/3.01f)), QPointF(xc + (xc/2.02f), yc - (yc/3.02f))); - p.drawLine(QPointF(xc - (xc/2.01f), yc - 0.01f), QPointF(xc + (xc/2.02f), yc - 0)); - p.drawLine(QPointF(xc - (xc/2.01f), yc + (yc/3.01f)), QPointF(xc + (xc/2.02f), yc + (yc/3.02f))); - } - - event->accept(); -} diff --git a/Lts/Common/LtsWidgets/lts_image_editor.h b/Lts/Common/LtsWidgets/lts_image_editor.h deleted file mode 100755 index 1d482b684..000000000 --- a/Lts/Common/LtsWidgets/lts_image_editor.h +++ /dev/null @@ -1,60 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsImageEditor : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) - Q_PROPERTY(bool menuEnabled READ isMenuEnabled WRITE setMenuEnabled) - -public: - explicit LtsImageEditor(QWidget *parent = nullptr); - - - - void setPixmap(const QPixmap &pixmap); - - - - QPixmap pixmap() const { return m_pixmap; } - - - - void setMenuEnabled(bool enabled = true) { m_menuEnabled = enabled; } - bool isMenuEnabled() const { return m_menuEnabled; } - - void setDrawOuterRect(bool bDraw) { m_bDrawOuterRect = bDraw; } - bool drawOuterRect() const { return m_bDrawOuterRect; } - -protected: - void paintEvent(QPaintEvent *event); - void enterEvent(QEvent *); - void leaveEvent(QEvent *); - bool event(QEvent *event); - -private: - QPixmap m_pixmap; - bool m_isHovered; - bool m_menuEnabled; - bool m_bDrawOuterRect; - -signals: - - - void signalClicked(); - - void imageChanged(); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_info_dialog.cpp b/Lts/Common/LtsWidgets/lts_info_dialog.cpp deleted file mode 100755 index b74298644..000000000 --- a/Lts/Common/LtsWidgets/lts_info_dialog.cpp +++ /dev/null @@ -1,81 +0,0 @@ - - - -#include "lts_info_dialog.h" -#include "ui_lts_info_dialog.h" - -#include -#include -#include - - -#ifndef LTS_VERSION_MAJOR - #define LTS_VERSION_MAJOR "undefined" -#endif -#ifndef LTS_VERSION_MINOR - #define LTS_VERSION_MINOR "undefined" -#endif -#ifndef LTS_VERSION_PATCH - #define LTS_VERSION_PATCH "undefined" -#endif -#ifndef LTS_VERSION_DATE - #define LTS_VERSION_DATE "undefined" -#endif -#ifndef JENKINS_BUILD_NUMBER - #define JENKINS_BUILD_NUMBER "unknown" -#endif -static const char ver_major[] = LTS_VERSION_MAJOR; -static const char ver_minor[] = LTS_VERSION_MINOR; -static const char ver_patch[] = LTS_VERSION_PATCH; -static const char ver_date[] = LTS_VERSION_DATE; -static const char ver_jenkins[] = JENKINS_BUILD_NUMBER; - -namespace LtsWidgets -{ - -LtsInfoDialog::LtsInfoDialog(QWidget *parent) - : LtsFramelessDialog(parent) - , m_ui(new Ui::LtsInfoDialog) -{ - m_ui->setupUi(this); - - QFont f = font(); - f.setBold(true); - m_ui->lblVersion->setFont(f); - m_ui->lblDate->setFont(f); - - const QString versionString = QStringLiteral("%1.%2-%3 (rev: %4)") - .arg(QString::fromLatin1(ver_major), - QString::fromLatin1(ver_minor), - QString::fromLatin1(ver_jenkins), - QString::fromLatin1(ver_patch)); - m_ui->lblVersion->setText(versionString); - m_ui->lblDate->setText(QString::fromLatin1(ver_date)); - - - QString projectLogoFilename(QStringLiteral(":project/login_logo.png")); - if (!QFile(projectLogoFilename).exists()) - projectLogoFilename = QStringLiteral(":/project/logo.png"); - - QPixmap pixmap(projectLogoFilename); - - if (pixmap.isNull()) - { - qWarning() << "LtsInfoDialog: project logo missing or is not an valid image " << projectLogoFilename; - } - else - { - int max = m_ui->lblProjectLogo->maximumHeight(); - pixmap = pixmap.scaled(999, max * 0.85f, Qt::KeepAspectRatio, Qt::SmoothTransformation); - m_ui->lblProjectLogo->setPixmap(pixmap); - } - connect(m_ui->pbClose, &QPushButton::clicked, this, &LtsInfoDialog::close); -} - - -LtsInfoDialog::~LtsInfoDialog() -{ - delete m_ui; -} - -} diff --git a/Lts/Common/LtsWidgets/lts_info_dialog.h b/Lts/Common/LtsWidgets/lts_info_dialog.h deleted file mode 100755 index 4268dcf2a..000000000 --- a/Lts/Common/LtsWidgets/lts_info_dialog.h +++ /dev/null @@ -1,28 +0,0 @@ - - - -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_widgets_export.h" - - -namespace Ui -{ -class LtsInfoDialog; -} - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsInfoDialog : public LtsFramelessDialog -{ - Q_OBJECT - -public: - explicit LtsInfoDialog(QWidget *parent = nullptr); - ~LtsInfoDialog(); - -private: - Ui::LtsInfoDialog *m_ui; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_info_dialog.ui b/Lts/Common/LtsWidgets/lts_info_dialog.ui deleted file mode 100755 index c45855d1b..000000000 --- a/Lts/Common/LtsWidgets/lts_info_dialog.ui +++ /dev/null @@ -1,135 +0,0 @@ - - - LtsInfoDialog - - - Operator Menu - - - - - - Rheinmetall Electronics GmbH - - - - - - - - 450 - 200 - - - - - 450 - 200 - - - - - - - :/rde_komplett.png - - - true - - - Qt::AlignCenter - - - - - - - - 0 - 150 - - - - - 16777215 - 100 - - - - <Project Logo> - - - false - - - Qt::AlignCenter - - - - - - - - - Version: - - - - - - - ? - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Date: - - - - - - - ? - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - Close - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_led_widget.cpp b/Lts/Common/LtsWidgets/lts_led_widget.cpp deleted file mode 100755 index 8915e52a4..000000000 --- a/Lts/Common/LtsWidgets/lts_led_widget.cpp +++ /dev/null @@ -1,148 +0,0 @@ - - - -#include "lts_led_widget.h" -#include -#include -#include -namespace LtsWidgets -{ - -void LtsLedWidget::resizeEvent(QResizeEvent *event) -{ - switch (m_aspectHint) { - case AspectHint::NoHint: - { - - - - - - QWidget::resizeEvent(event); - break; - } - case AspectHint::HeightIsWidth: - setMinimumHeight(event->size().width()); - setMaximumHeight(event->size().width()); - setMinimumWidth(0); - setMaximumWidth(65000); - break; - case AspectHint::WidthIsHeight: - setMinimumHeight(0); - setMaximumHeight(65000); - setMinimumWidth(event->size().height()); - setMaximumWidth(event->size().height()); - break; - } -} - - - -static constexpr double GRADIENT_KEY_LOWER = 0.3; -static constexpr double GRADIENT_KEY_UPPER = 0.9; -static constexpr int GRADIENT_VALUE_LOWER = 125; -static constexpr int GRADIENT_VALUE_UPPER = 165; -static constexpr double GRADIENT_CENTER_OFFSET = 5.0; -static constexpr double GRADIENT_FOCALPOINT_OFFSET = 2.7; -static constexpr double PERCENTAGE_OF_BORDER = 0.1; -static constexpr int DARK_FACTOR = 200; - -LtsLedWidget::LtsLedWidget(QWidget *parent, const QColor &oncolor, const QColor &offcolor, bool useGradient, int animtime) - : QWidget(parent) - , m_status(true) - , m_oncolor(oncolor) - , m_offcolor(offcolor) - , m_currentcolor(oncolor) - , m_gradient(useGradient) - , m_animTime(animtime) -{ - setObjectName(QString::fromUtf8("LtsLedWidget")); -} - -void LtsLedWidget::paintEvent(QPaintEvent *pe) -{ - Q_UNUSED(pe); - QPainter p; - p.begin(this); - p.setOpacity(QWidget::isEnabled() ? 1.00 : 0.5); - - int minSize = height() > width() ? width() : height(); - if (m_gradient) - { - QRadialGradient grad(QPointF(width(), height()) / GRADIENT_CENTER_OFFSET, - minSize, - QPointF(width(), height()) / GRADIENT_FOCALPOINT_OFFSET); - grad.setColorAt(GRADIENT_KEY_LOWER, m_currentcolor.lighter(GRADIENT_VALUE_LOWER)); - grad.setColorAt(GRADIENT_KEY_UPPER, m_currentcolor.darker(GRADIENT_VALUE_UPPER)); - p.setBrush(grad); - } - else - p.setBrush(QBrush(m_currentcolor, Qt::SolidPattern)); - - p.setRenderHint(QPainter::Antialiasing); - p.setPen(QPen(Qt::black, PERCENTAGE_OF_BORDER * minSize, Qt::SolidLine, Qt::SquareCap)); - p.drawEllipse(QPointF(width(), height()) / 2.0, - width() / (2.0 * (1.00 + PERCENTAGE_OF_BORDER)), - height() / (2.0 * (1.00 + PERCENTAGE_OF_BORDER))); - p.end(); -} - -void LtsLedWidget::animate() -{ - QPropertyAnimation *anim = new QPropertyAnimation(this, "currentcolor"); - anim->setDuration(m_animTime); - anim->setEasingCurve(QEasingCurve::OutBack); - anim->setStartValue(m_currentcolor); - anim->setEndValue(m_status ? m_oncolor : m_offcolor == Qt::black ? m_oncolor.darker(DARK_FACTOR) : m_offcolor); - anim->start(QPropertyAnimation::DeleteWhenStopped); -} - -void LtsLedWidget::setStatus(bool status) -{ - if (m_status == status) - return; - m_status = status; - animate(); -} - -void LtsLedWidget::setUseGradient(bool useGradient) -{ - if (m_gradient == useGradient) - return; - m_gradient = useGradient; - repaint(); -} - -void LtsLedWidget::setColor(const QColor &oncolor, const QColor &offcolor) -{ - setOnColor(oncolor); - setOffColor(offcolor); -} - -void LtsLedWidget::setOnColor(const QColor &oncolor) -{ - if (m_oncolor == oncolor) - return; - m_oncolor = oncolor; - if (m_status) - animate(); -} - -void LtsLedWidget::setOffColor(const QColor &offcolor) -{ - if (m_offcolor == offcolor) - return; - m_offcolor = offcolor; - if (!m_status) - animate(); -} - -void LtsLedWidget::setCurrentColor(const QColor ¤tcolor) -{ - if (m_currentcolor == currentcolor) - return; - m_currentcolor = currentcolor; - repaint(); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_led_widget.h b/Lts/Common/LtsWidgets/lts_led_widget.h deleted file mode 100755 index c0392c094..000000000 --- a/Lts/Common/LtsWidgets/lts_led_widget.h +++ /dev/null @@ -1,117 +0,0 @@ - - -#pragma once - -#include -#include "lts_widgets_export.h" - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsLedWidget : public QWidget -{ - Q_OBJECT - -public: - enum class AspectHint - { - WidthIsHeight, - NoHint, - HeightIsWidth - }; - Q_ENUM(AspectHint) - - Q_PROPERTY(QColor oncolor READ onColor WRITE setOnColor) - Q_PROPERTY(QColor offcolor READ offColor WRITE setOffColor) - Q_PROPERTY(bool status READ status WRITE setStatus) - Q_PROPERTY(bool usesGradient READ usesGradient WRITE setUseGradient) - Q_PROPERTY(int animTime READ animTime WRITE setAnimTime) - Q_PROPERTY(AspectHint aspectHint READ aspectHint WRITE setAspectHint) - Q_PROPERTY(QColor currentcolor READ currentColor WRITE setCurrentColor DESIGNABLE false) - - - - explicit LtsLedWidget(QWidget *parent = nullptr, const QColor &oncolor = Qt::red, const QColor &offcolor = Qt::black, bool useGradient = true, int animtime = 0); - - void paintEvent(QPaintEvent *pe); - - - - void setAnimTime(int time) { m_animTime = time; } - - - - int animTime() const { return m_animTime; } - - - - bool status() const { return m_status; } - - - - void setUseGradient(bool useGradient); - - - - bool usesGradient() const { return m_gradient; } - - - - void setOnColor(const QColor &oncolor); - - - - QColor onColor() const { return m_oncolor; } - - - - void setOffColor(const QColor &offcolor); - - - - QColor offColor() const { return m_offcolor; } - - - - void setColor(const QColor &oncolor, const QColor &offcolor = Qt::black); - - - - QColor currentColor() const { return m_currentcolor; } - - void resizeEvent(QResizeEvent *event); - - AspectHint aspectHint() const { return m_aspectHint; } - -public slots: - - - void setStatus(bool status); - - void setAspectHint(AspectHint aspectHint) - { - m_aspectHint = aspectHint; - } - -private: - - - void setCurrentColor(const QColor ¤tcolor); - - - - void animate(); - - bool m_status; - QColor m_oncolor; - QColor m_offcolor; - QColor m_currentcolor; - - bool m_gradient; - int m_animTime; - AspectHint m_aspectHint = AspectHint::NoHint; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_message_dialog.cpp b/Lts/Common/LtsWidgets/lts_message_dialog.cpp deleted file mode 100755 index 144dc54e8..000000000 --- a/Lts/Common/LtsWidgets/lts_message_dialog.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "lts_message_dialog.h" -#include "lts_message_dialog_helper.h" - -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -namespace LtsMessageDialog -{ -Button showMessage(QWidget* parent, - const QString& message, - const QString &title, - const Behaviour behaviour, - const Buttons buttons, - const QHash &buttonTexts) -{ - static QAtomicInt s_used = 0; - - if (s_used) - qDebug() << "LtsMessageDialog: showMessage called while showMessage is in its Event Loop - do not open more than one Dialog at once !!"; - - s_used = 1; - - LtsMessageDialogHelper *dialog = new LtsMessageDialogHelper(); - Button result = dialog->showMessage(parent, message, title, behaviour, buttons, buttonTexts); - dialog->deleteLater(); - - s_used = 0; - - return result; -} - -Button askApplyDiscardCancel(QWidget *parent) -{ - const QString question = QCoreApplication::translate("LtsDialog", - "
" - "Apply - apply changes and close.
" - "Discard - discard changes and close.
" - "Cancel - do not close."); - const QString title = QCoreApplication::translate("LtsDialog", "Unapplied changes made. Apply the changes?"); - return LtsMessageDialog::showMessage(parent, - question, - title, - Behaviour::Modal, - Button::Apply | Button::Discard | Button::Cancel); -} - - -} - - - - -LtsMessageDialogNew::LtsMessageDialogNew(QWidget *parent, const QString &message, const QString &title, const Behaviour behaviour, const Buttons buttons, const QHash &buttonTexts, bool deleteOnClose) -{ - m_dialog = new LtsFramelessDialog(parent, behaviour); - - if (deleteOnClose) - m_dialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(m_dialog, &LtsFramelessDialog::destroyed, - this, &LtsMessageDialogNew::deleteLater); - - QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog); - - QString text; - if (!title.isEmpty()) - { - text = QStringLiteral("%1

").arg(title); - } - - text += message; - - QLabel *messageLabel = new QLabel(text, m_dialog); - messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - mainLayout->addWidget(messageLabel); - - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->setMargin(0); - - - - - if (buttons.testFlag(Button::Ok)) - connect(addButton(buttonTexts.value(Button::Ok, tr("Ok")), buttonLayout, m_dialog, Button::Ok, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::ok); - - if (buttons.testFlag(Button::Yes)) - connect(addButton(buttonTexts.value(Button::Yes, tr("Yes")), buttonLayout, m_dialog, Button::Yes, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::yes); - - if (buttons.testFlag(Button::No)) - connect(addButton(buttonTexts.value(Button::No, tr("No")), buttonLayout, m_dialog, Button::No, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::no); - - if (buttons.testFlag(Button::Apply)) - connect(addButton(buttonTexts.value(Button::Apply, tr("Apply")), buttonLayout, m_dialog, Button::Apply, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::apply); - - if (buttons.testFlag(Button::Discard)) - connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::discard); - - if (buttons.testFlag(Button::Cancel)) - connect(addButton(buttonTexts.value(Button::Cancel, tr("Cancel")), buttonLayout, m_dialog, Button::Cancel, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::cancel); - - QWidget *buttonLayoutWidget = new QWidget; - buttonLayoutWidget->setLayout(buttonLayout); - - mainLayout->addWidget(buttonLayoutWidget); - - m_dialog->show(); - m_dialog->raise(); -} - - -QPushButton* LtsMessageDialogNew::addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent) -{ - QPushButton* btn = new QPushButton(text, parent); - connect(btn, &QPushButton::clicked, dialog, &LtsFramelessDialog::accept); - thatLayout->addWidget(btn); - - if (button == Button::Ok || - button == Button::Cancel) - { - btn->setDefault(true); - } - - return btn; -} - - -} diff --git a/Lts/Common/LtsWidgets/lts_message_dialog.h b/Lts/Common/LtsWidgets/lts_message_dialog.h deleted file mode 100755 index ef64383eb..000000000 --- a/Lts/Common/LtsWidgets/lts_message_dialog.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "lts_widgets_export.h" -#include "lts_frameless_dialog.h" - -#include -#include - - -class QWidget; -class QHBoxLayout; - - -namespace LtsWidgets -{ - - - -enum class Button -{ - UNKNOWN = 0, - Ok = 1, - Yes = 2, - No = 4, - Cancel = 8, - - Apply = 16, - Discard = 32, -}; -Q_DECLARE_FLAGS(Buttons, Button) -inline uint qHash(LtsWidgets::Button b) -{ - return ::qHash(static_cast(b)); -} - - - -namespace LtsMessageDialog -{ - - -LTSWIDGETS_EXPORT Button showMessage(QWidget* parent, - const QString &message, - const QString &title = QString(), - const Behaviour behaviour = Behaviour::Modal, - const Buttons buttons = Button::Ok, - const QHash &buttonTexts = QHash()); -LTSWIDGETS_EXPORT Button askApplyDiscardCancel(QWidget *parent); -} - - - - - -class LTSWIDGETS_EXPORT LtsMessageDialogNew : public QObject -{ - Q_OBJECT - -public: - explicit LtsMessageDialogNew(QWidget* parent, - const QString &message, - const QString &title = QString(), - const Behaviour behaviour = Behaviour::Modal, - const Buttons buttons = Button::Ok, - const QHash &buttonTexts = QHash(), - bool deleteOnClose = true ); - -private: - LtsFramelessDialog *m_dialog; - - QPushButton *addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent); - - -signals: - void ok(); - void yes(); - void no(); - void apply(); - void discard(); - void cancel(); - -}; - -} - -Q_DECLARE_OPERATORS_FOR_FLAGS(LtsWidgets::Buttons) diff --git a/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp b/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp deleted file mode 100755 index edc56ebf4..000000000 --- a/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "lts_message_dialog_helper.h" - -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ - -LtsMessageDialogHelper::LtsMessageDialogHelper(QObject *parent) - : QObject(parent) - , m_result(Button::Cancel) - , m_dialog(nullptr) -{} - - -Button LtsMessageDialogHelper::showMessage(QWidget *parent, - const QString &message, - const QString &title, - Behaviour behaviour, - Buttons buttons, - const QHash &buttonTexts) -{ - m_dialog = new LtsFramelessDialog(parent, behaviour); - - m_dialog->setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog); - - QString text; - if (!title.isEmpty()) - { - text = QStringLiteral("%1

").arg(title); - } - - text += message; - - QLabel *messageLabel = new QLabel(text, m_dialog); - messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - mainLayout->addWidget(messageLabel); - - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->setMargin(0); - - - connect(m_dialog, &LtsFramelessDialog::rejected, this, &LtsMessageDialogHelper::slotCancel); - - if (buttons.testFlag(Button::Ok)) - connect(addButton(buttonTexts.value(Button::Ok, tr("Ok")), buttonLayout, m_dialog, Button::Ok, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotOkay); - - if (buttons.testFlag(Button::Yes)) - connect(addButton(buttonTexts.value(Button::Yes, tr("Yes")), buttonLayout, m_dialog, Button::Yes, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotYes); - - if (buttons.testFlag(Button::No)) - connect(addButton(buttonTexts.value(Button::No, tr("No")), buttonLayout, m_dialog, Button::No, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotNo); - - if (buttons.testFlag(Button::Apply)) - connect(addButton(buttonTexts.value(Button::Apply, tr("Apply")), buttonLayout, m_dialog, Button::Apply, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotApply); - - if (buttons.testFlag(Button::Discard)) - connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotDiscard); - - if (buttons.testFlag(Button::Cancel)) - connect(addButton(buttonTexts.value(Button::Cancel, tr("Cancel")), buttonLayout, m_dialog, Button::Cancel, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotCancel); - - QWidget *buttonLayoutWidget = new QWidget; - buttonLayoutWidget->setLayout(buttonLayout); - - mainLayout->addWidget(buttonLayoutWidget); - - m_dialog->show(); - m_dialog->raise(); - - - QEventLoop loop; - connect(this, &LtsMessageDialogHelper::signalQuitEventLoop, &loop, &QEventLoop::quit, Qt::QueuedConnection); - loop.exec(); - - return m_result; -} - - -void LtsMessageDialogHelper::slotOkay() -{ - m_dialog->hide(); - m_result = Button::Ok; - emit signalQuitEventLoop(); -} - - -void LtsMessageDialogHelper::slotYes() -{ - m_dialog->hide(); - m_result = Button::Yes; - emit signalQuitEventLoop(); -} - - -void LtsMessageDialogHelper::slotNo() -{ - m_dialog->hide(); - m_result = Button::No; - emit signalQuitEventLoop(); -} - - -void LtsMessageDialogHelper::slotApply() -{ - m_dialog->hide(); - m_result = Button::Apply; - emit signalQuitEventLoop(); -} - - -void LtsMessageDialogHelper::slotDiscard() -{ - m_dialog->hide(); - m_result = Button::Discard; - emit signalQuitEventLoop(); -} - - -void LtsMessageDialogHelper::slotCancel() -{ - m_dialog->hide(); - m_result = Button::Cancel; - emit signalQuitEventLoop(); -} - - -QPushButton* LtsMessageDialogHelper::addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent) -{ - QPushButton* btn = new QPushButton(text, parent); - connect(btn, &QPushButton::clicked, dialog, &LtsFramelessDialog::accept); - thatLayout->addWidget(btn); - - if (button == Button::Ok || - button == Button::Cancel) - { - btn->setDefault(true); - } - - return btn; -} - -} diff --git a/Lts/Common/LtsWidgets/lts_message_dialog_helper.h b/Lts/Common/LtsWidgets/lts_message_dialog_helper.h deleted file mode 100755 index 0aaf73a4e..000000000 --- a/Lts/Common/LtsWidgets/lts_message_dialog_helper.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_message_dialog.h" - -#include -#include -#include - - -class QPushButton; -class QHBoxLayout; - - -namespace LtsWidgets -{ - - - -class LtsMessageDialogHelper : public QObject -{ - Q_OBJECT - -public: - explicit LtsMessageDialogHelper(QObject *parent = nullptr); - ~LtsMessageDialogHelper() = default; - - Button showMessage(QWidget *parent, - const QString &message, - const QString &title, - Behaviour behaviour, - LtsWidgets::Buttons buttons, - const QHash &buttonTexts); - -private: - QPushButton *addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent); - - Button m_result; - LtsFramelessDialog *m_dialog; - -private slots: - void slotOkay(); - void slotYes(); - void slotNo(); - void slotCancel(); - void slotApply(); - void slotDiscard(); - -signals: - void signalQuitEventLoop(); -}; -} diff --git a/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp b/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp deleted file mode 100755 index 8d9316a77..000000000 --- a/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "lts_msec_date_time_edit.h" - - -namespace LtsWidgets -{ - -LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(QWidget *parent) - : QDateTimeEdit(parent) -{ - setWrapping(true); - setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz")); - connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished); -} - -LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QDateTime &dt, QWidget *parent) - : QDateTimeEdit(dt, parent) -{ - setWrapping(true); - setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz")); - connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished); -} - -LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QDate &d, QWidget *parent) - : QDateTimeEdit(d, parent) -{ - setWrapping(true); - setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz")); - connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished); -} - -LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QTime &t, QWidget *parent) - : QDateTimeEdit(t, parent) -{ - setWrapping(true); - setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz")); - connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished); -} - -void LtsMSecDateTimeEdit::stepBy(int steps) -{ - switch (currentSection()) { - case QDateTimeEdit::MonthSection: - QDateTimeEdit::setDateTime(dateTime().addMonths(steps)); - break; - case QDateTimeEdit::YearSection: - QDateTimeEdit::setDateTime(dateTime().addYears(steps)); - break; - case QDateTimeEdit::DaySection: - QDateTimeEdit::setDateTime(dateTime().addDays(steps)); - break; - case QDateTimeEdit::HourSection: - QDateTimeEdit::setDateTime(dateTime().addSecs(steps * 3600)); - break; - case QDateTimeEdit::MinuteSection: - QDateTimeEdit::setDateTime(dateTime().addSecs(steps * 60)); - break; - case QDateTimeEdit::SecondSection: - QDateTimeEdit::setDateTime(dateTime().addSecs(steps)); - break; - case QDateTimeEdit::MSecSection: - QDateTimeEdit::setDateTime(dateTime().addMSecs(steps * m_msecStepSize)); - break; - case QDateTimeEdit::NoSection: - case QDateTimeEdit::AmPmSection: - case QDateTimeEdit::TimeSections_Mask: - case QDateTimeEdit::DateSections_Mask: - default: - break; - } -} - -qint64 LtsMSecDateTimeEdit::msecsSinceEpoch() const -{ - return dateTime().toMSecsSinceEpoch() - (m_timezoneHoursOffset * 3600 * 1000); -} - -void LtsMSecDateTimeEdit::setTimezoneHoursOffset(int timezoneHoursOffset) -{ - qint64 b4 = msecsSinceEpoch(); - m_timezoneHoursOffset = timezoneHoursOffset; - setMsecsSinceEpoch(b4); -} - -void LtsMSecDateTimeEdit::setDateTime(const QDateTime &dateTime) -{ - - setMsecsSinceEpoch(QDateTime::fromMSecsSinceEpoch(dateTime.toMSecsSinceEpoch(), Qt::TimeSpec::OffsetFromUTC, m_timezoneHoursOffset * 3600).toMSecsSinceEpoch()); -} - -void LtsMSecDateTimeEdit::setMsecsSinceEpoch(qint64 msecs) -{ - QDateTimeEdit::setDateTime(QDateTime::fromMSecsSinceEpoch(msecs, Qt::TimeSpec::OffsetFromUTC, m_timezoneHoursOffset * 3600)); -} - -int LtsMSecDateTimeEdit::msecStepSize() const -{ - return m_msecStepSize; -} - -void LtsMSecDateTimeEdit::setMsecStepSize(int msecStepSize) -{ - m_msecStepSize = msecStepSize; -} - -void LtsMSecDateTimeEdit::onEditingFinished() -{ - qint64 val = msecsSinceEpoch(); - if (val % m_msecStepSize == 0) - return; - val = (val + (m_msecStepSize/2) ) / m_msecStepSize * m_msecStepSize; - setMsecsSinceEpoch(val); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h b/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h deleted file mode 100755 index 9fc1b6e9e..000000000 --- a/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -#include "lts_widgets_export.h" - - -namespace LtsWidgets { - -class LTSWIDGETS_EXPORT LtsMSecDateTimeEdit : public QDateTimeEdit -{ - Q_OBJECT - - Q_PROPERTY(int msecStepSize READ msecStepSize WRITE setMsecStepSize) - -public: - explicit LtsMSecDateTimeEdit(QWidget *parent = nullptr); - explicit LtsMSecDateTimeEdit(const QDateTime &dt, QWidget *parent = nullptr); - explicit LtsMSecDateTimeEdit(const QDate &d, QWidget *parent = nullptr); - explicit LtsMSecDateTimeEdit(const QTime &t, QWidget *parent = nullptr); - - qint64 msecsSinceEpoch() const; - int timezoneHoursOffset() const { return m_timezoneHoursOffset; } - void stepBy(int steps) override; - int msecStepSize() const; - -public slots: - void setDateTime(const QDateTime &dateTime); - void setMsecsSinceEpoch(qint64 msecs); - void setMsecStepSize(int msecStepSize); - void setTimezoneHoursOffset(int timezoneHoursOffset); - -private slots: - void onEditingFinished(); - -private: - int m_msecStepSize = 10; - int m_timezoneHoursOffset = 0; -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp b/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp deleted file mode 100755 index 80d4af2d0..000000000 --- a/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp +++ /dev/null @@ -1,313 +0,0 @@ - - - -#include "lts_multi_range_spinbox.h" - -#include -#include -#include - -namespace LtsWidgets -{ - -LtsMultiRangeSpinBox::LtsMultiRangeSpinBox(QWidget *parent) - : QSpinBox(parent) -{ - m_curVal = minimum(); - -} - - - - -QValidator::State LtsMultiRangeSpinBox::validate(QString &input, int &pos) const -{ - - - - - - - - - - - - - - - - - return QValidator::Acceptable; -} - -void LtsMultiRangeSpinBox::fixup(QString &str) const -{ - - - - - - - - - - - - - - - - -} - -QValidator::State LtsMultiRangeSpinBox::validateAll(QString &input, int &pos) -{ - for(QPair range : qAsConst(m_rangesList)) - { - QIntValidator validator(range.first, range.second); - QValidator::State valState = validator.validate(input, pos); - if (valState == QValidator::Acceptable) - { - return valState; - } - } - return QValidator::Invalid; - - -} - - -QVector> LtsWidgets::LtsMultiRangeSpinBox::sortRanges(const QVector > &ranges) -{ - QVector> rangesEditable = ranges; - QVector> result; - std::sort(rangesEditable.begin(), rangesEditable.end()); - QVector>::iterator it = rangesEditable.begin(); - QPair current = *(it)++; - while (it != rangesEditable.end()) - { - if (current.second > it->first) - current.second = std::max(current.second, it->second); - else - { - result.push_back(current); - current = *(it); - } - ++it; - } - result.push_back(current); - return result; -} - -void LtsMultiRangeSpinBox::setRanges(const QVector> &ranges) -{ - if(ranges.empty()) - return; - - m_rangesList = sortRanges(ranges); - - QSpinBox::setMaximum(m_rangesList.last().second); - QSpinBox::setMinimum(m_rangesList.first().first); - - QStringList rangeStrings; - rangeStrings.reserve(m_rangesList.size()); - for (const QPair &range : qAsConst(m_rangesList)) - rangeStrings << tr("%1-%2%3").arg(range.first).arg(range.second).arg(suffix()); - setToolTip(tr("Ranges:\n%1").arg(rangeStrings.join(QStringLiteral(",\n")))); -} - -void LtsMultiRangeSpinBox::setRange(int min, int max) -{ - setRanges({{min,max}}); -} - -void LtsMultiRangeSpinBox::setMinimum(int min) -{ - auto ranges = m_rangesList; - ranges.first().first = min; - setRanges(ranges); -} - -void LtsMultiRangeSpinBox::setMaximum(int max) -{ - auto ranges = m_rangesList; - ranges.last().second = max; - setRanges(ranges); -} - -int LtsMultiRangeSpinBox::fixupValue(int value) const -{ - - int nearestValue = INT_MAX; - for(QPair range : m_rangesList) - { - if(std::abs(range.first - value) < std::abs(nearestValue - value)) - nearestValue = range.first; - if(std::abs(range.second - value) < std::abs(nearestValue - value)) - nearestValue = range.second; - } - value = nearestValue; - int buffer = value; - - int remainder = (value - minimum()) % singleStep(); - - if (2 * remainder > singleStep()) - { - value += singleStep(); - } - value -= remainder; - - if(buffer + singleStep() > value + singleStep()) - { - value+= singleStep(); - } - - if (value > maximum()) - { - value -= singleStep(); - } - - return value; -} - -int LtsMultiRangeSpinBox::fixupValue(int value, bool higher) const -{ - - int nearestValue = INT_MAX; - for(QPair range : m_rangesList) - { - if(std::abs(range.first - value) < std::abs(nearestValue - value)) - { - if(higher) - { - if(range.second < value) - continue; - - } else - if(range.second > value) - continue; - - nearestValue = range.first; - } - if(std::abs(range.second - value) < std::abs(nearestValue - value)) - { - if(higher) - { - if(range.second < value) - continue; - - } else - if(range.second > value) - continue; - - nearestValue = range.second; - } - } - - value = nearestValue; - int curVal = value; - - int remainder = (value - minimum()) % singleStep(); - - if (2 * remainder > singleStep()) - { - value += singleStep(); - } - value -= remainder; - - if(curVal + singleStep() > value + singleStep()) - { - value+= singleStep(); - } - if (value > maximum()) - { - value -= singleStep(); - } - - return value; -} - - -void LtsMultiRangeSpinBox::focusOutEvent(QFocusEvent *event) -{ - QSpinBox::focusOutEvent(event); - QString value = QString::number(this->value()); - int length = value.length(); - QValidator::State valState = validateAll(value, length); - - if(valState == QValidator::State::Invalid) - { - this->setValue(fixupValue(value.toInt())); - } -} - -void LtsMultiRangeSpinBox::wheelEvent(QWheelEvent *event) -{ - QSpinBox::wheelEvent(event); - - QString valueStr = QString::number(this->value()); - int length = valueStr.length(); - QValidator::State valState = validateAll(valueStr, length); - - if(valState == QValidator::State::Invalid) - { - - - - if(this->value() < m_curVal) - { - m_curVal = fixupValue(this->value(), false); - setValue(m_curVal); - } - else - { - m_curVal = fixupValue(this->value(), true); - setValue(m_curVal); - } - } -} - -void LtsMultiRangeSpinBox::mousePressEvent(QMouseEvent *event) -{ - QSpinBox::mousePressEvent(event); - QString valueStr = QString::number(this->value()); - int length = valueStr.length(); - QValidator::State valState = validateAll(valueStr, length); - - if(valState == QValidator::State::Invalid) - { - - - - - if(this->value() < m_curVal) - { - m_curVal = fixupValue(this->value(), false); - setValue(m_curVal); - } - else - { - m_curVal = fixupValue(this->value(), true); - setValue(m_curVal); - } - } - - -} - -void LtsMultiRangeSpinBox::keyPressEvent(QKeyEvent *event) -{ - QSpinBox::keyPressEvent(event); - QString value = QString::number(this->value()); - int length = value.length(); - QValidator::State valState = validateAll(value, length); - - if(valState == QValidator::State::Invalid) - { - setValue(fixupValue(value.toInt())); - } -} - - -} diff --git a/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h b/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h deleted file mode 100755 index 9559e50cc..000000000 --- a/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h +++ /dev/null @@ -1,52 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" -#include -#include - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsMultiRangeSpinBox : public QSpinBox -{ - Q_OBJECT - -public: - explicit LtsMultiRangeSpinBox(QWidget *parent = nullptr); - ~LtsMultiRangeSpinBox() = default; - - -public slots: - void setRanges(const QVector> &ranges); - void setRange(int min, int max); - void setMinimum(int min); - void setMaximum(int max); - -protected: - QValidator::State validate(QString &input, int &pos) const override; - void fixup(QString &str) const override; - void focusOutEvent(QFocusEvent *event) override; - void wheelEvent(QWheelEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; - -private: - static QVector> sortRanges(const QVector > &ranges); - - QValidator::State validateAll(QString &input, int &pos); - - int fixupValue(int value) const; - int fixupValue(int value, bool higher) const; -private: - QVector> m_rangesList; - - int m_curVal = minimum(); - QRegExp m_regex; -}; - - - -} diff --git a/Lts/Common/LtsWidgets/lts_notification_box.cpp b/Lts/Common/LtsWidgets/lts_notification_box.cpp deleted file mode 100755 index f39dbaefd..000000000 --- a/Lts/Common/LtsWidgets/lts_notification_box.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "lts_notification_box.h" -#include "ui_lts_notification_box.h" - -#include -#include -#include -#include -#include -#include -#ifdef NOTIBOX_DEBUG -#include -#endif - - -namespace LtsWidgets -{ -const int LtsNotificationBox::margin = 14; - -LtsNotificationBox::LtsNotificationBox(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::NotiBox) -{ - m_ui->setupUi(this); - m_ui->lblText->setText({}); - - connect(m_ui->lblText, &QLabel::linkActivated, - this, [this](const QString &link) - { - emit signalLinkClicked(link); - }); - - connect(m_ui->tbClose, &QToolButton::clicked, - this, &LtsNotificationBox::signalClose); - connect(m_ui->tbAck, &QToolButton::clicked, - this, &LtsNotificationBox::signalAck); - - setObjectName(QStringLiteral("NotiBox")); - - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); - shadow->setOffset(0, 0); - shadow->setBlurRadius(margin*2); - shadow->setColor(QColor(25, 25, 25)); - - setGraphicsEffect(shadow); -} - - -void LtsNotificationBox::message(const QString &title, const QString &text, MessageType type) -{ - static QString rgbBaseString(QStringLiteral("%1, %2, %3")); - static QString closeDefault(QStringLiteral("close.svg")); - static QString closeDark(QStringLiteral("close_dark.svg")); - static QString ackDefault(QStringLiteral("ack.svg")); - static QString ackDark(QStringLiteral("ack_dark.svg")); - static QString lightTextColor(QStringLiteral("fbfbfb")); - static QString darkTextColor(QStringLiteral("2c3642")); - - static QColor errorStart(254, 45, 102); - static QColor errorEnd(255, 92, 61); - - static QColor successStart(43, 167, 70); - static QColor successEnd(130, 180, 45); - - static QColor infoStart(74, 74, 74); - static QColor infoEnd(43, 43, 43); - - static QColor warningStart(253, 151, 4); - static QColor warningEnd(253, 202, 0); - - - QColor start; - QColor end; - QString close; - QString closeHover; - QString ack; - QString ackHover; - QString textColor; - - switch (type) - { - case MessageType::Error: - { - start = errorStart; - end = errorEnd; - close = closeDefault; - closeHover = closeDark; - ack = ackDefault; - ackHover = ackDark; - textColor = lightTextColor; - m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/error.svg"))); - - break; - } - - case MessageType::Success: - { - start = successStart; - end = successEnd; - close = closeDefault; - closeHover = closeDark; - ack = ackDefault; - ackHover = ackDark; - textColor = lightTextColor; - m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/success.svg"))); - - break; - } - - case MessageType::Info: - { - start = infoStart; - end = infoEnd; - close = closeDefault; - closeHover = closeDark; - ack = ackDefault; - ackHover = ackDark; - textColor = lightTextColor; - m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/info.svg"))); - - break; - } - - case MessageType::Warning: - { - start = warningStart; - end = warningEnd; - close = closeDark; - closeHover = closeDefault; - ack = ackDark; - ackHover = ackDefault; - textColor = darkTextColor; - m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/warning.svg"))); - - break; - } - } - - QStringList css; - - css << QStringLiteral("#NotiBox { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop: 0 rgb(%1), stop: 1 rgb(%2)); border: 1px solid rgb(%3); border-radius: 8px; margin: %4px }"). - arg(rgbBaseString.arg(start.red()).arg(start.green()).arg(start.blue()), - rgbBaseString.arg(end.red()).arg(end.green()).arg(end.blue()), - rgbBaseString.arg(end.red()).arg(end.green()).arg(end.blue())). - arg(margin); - - css << QStringLiteral("QToolButton#tbClose { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(close); - css << QStringLiteral("QToolButton#tbClose:hover { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(closeHover); - - css << QStringLiteral("QToolButton#tbAck { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(ack); - css << QStringLiteral("QToolButton#tbAck:hover { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(ackHover); - -#ifdef NOTIBOX_DEBUG - qWarning() << "css" << css; - - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/close.svg")); - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/close_dark.svg")); - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/error.svg")); - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/info.svg")); - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/warning.svg")); - qWarning() << "exists?" << QFile::exists(QStringLiteral(":/success.svg")); -#endif - - m_ui->lblImage->setStyleSheet(QString()); - setStyleSheet(css.join(QStringLiteral(" "))); - - m_ui->lblText->setText(QStringLiteral("

%2

%4

").arg(textColor, title, textColor, text)); - - show(); -} - - -void LtsNotificationBox::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} -} - - - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_notification_box.h b/Lts/Common/LtsWidgets/lts_notification_box.h deleted file mode 100755 index f57d88295..000000000 --- a/Lts/Common/LtsWidgets/lts_notification_box.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "lts_widgets_export.h" -#include -#include - - -class QPaintEvent; -class QResizeEvent; - - -namespace Ui -{ -class NotiBox; -} - - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsNotificationBox : public QWidget -{ - Q_OBJECT -public: - enum class MessageType - { - Success, - Error, - Warning, - Info, - }; - - explicit LtsNotificationBox(QWidget *parent = nullptr); - - void message(const QString &title, const QString &text, MessageType type = MessageType::Error); - -signals: - void signalClose(); - void signalAck(); - void signalLinkClicked(const QString &link); - -protected: - void paintEvent(QPaintEvent *) override; - -private: - Ui::NotiBox *m_ui; - QPainterPath roundRectPath; - const static int margin; -}; -} - diff --git a/Lts/Common/LtsWidgets/lts_notification_box.ui b/Lts/Common/LtsWidgets/lts_notification_box.ui deleted file mode 100755 index bb58cd5a8..000000000 --- a/Lts/Common/LtsWidgets/lts_notification_box.ui +++ /dev/null @@ -1,182 +0,0 @@ - - - NotiBox - - - - 0 - 0 - 670 - 130 - - - - - 0 - 0 - - - - - 670 - 130 - - - - - 670 - 130 - - - - Notification - - - - :/rde_icon_256.png:/rde_icon_256.png - - - - 20 - - - 40 - - - 18 - - - 18 - - - 18 - - - - - - 0 - 0 - - - - - 50 - 50 - - - - - 50 - 50 - - - - - - - true - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - <html><head/><body><p><span style=" font-size:14pt; font-weight:600; color:#eeeeee;">Error Message</span></p><p><span style=" font-size:14pt; color:#eeeeee;">This is an error message. looooooooooooooooooooong text<br/>With 2 lines!</span></p></body></html> - - - false - - - false - - - false - - - - - - - - - 0 - - - - - - 0 - 0 - - - - - 20 - 20 - - - - - - - true - - - - - - - - 0 - 0 - - - - - 20 - 20 - - - - - - - true - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_operator_menu.cpp b/Lts/Common/LtsWidgets/lts_operator_menu.cpp deleted file mode 100755 index 16e844b6a..000000000 --- a/Lts/Common/LtsWidgets/lts_operator_menu.cpp +++ /dev/null @@ -1,114 +0,0 @@ - - - -#include "lts_operator_menu.h" -#include "ui_lts_operator_menu.h" -#include "lts_info_dialog.h" -#include - -#include -#include -#include - - -namespace LtsWidgets -{ -LtsOperatorMenu::LtsOperatorMenu(const QString &operatorName, const QString &workspaceId, bool debug, int screenCount, QWidget *parent) - : LtsFramelessDialog(parent) - , m_ui(new Ui::LtsOperatorMenu) -{ - m_ui->setupUi(this); - m_ui->lbOperatorName->setText(operatorName); - m_ui->lbHostName->setText(QHostInfo::localHostName()); - - if (screenCount < 2) - m_ui->pbSwap->hide(); - - if (debug) - m_ui->lbWorkspaceId->setText(tr("Workspace: >>>%1<<<").arg(workspaceId)); - else - m_ui->lbWorkspaceId->hide(); - - connect(m_ui->pbOperatorSettings, &QPushButton::clicked, this, &LtsOperatorMenu::signalShowOperatorSettings); - connect(m_ui->pbOperatorSettings, &QPushButton::clicked, this, &LtsOperatorMenu::close); - connect(m_ui->pbWindow, &QPushButton::clicked, this, &LtsOperatorMenu::onTerminalButtonClicked); - connect(m_ui->pbWindow, &QPushButton::clicked, this, &LtsOperatorMenu::close); - connect(m_ui->pbLogout, &QPushButton::clicked, this, &LtsOperatorMenu::signalLogout); - connect(m_ui->pbExit, &QPushButton::clicked, this, &LtsOperatorMenu::signalExit); - connect(m_ui->pbCancel, &QPushButton::clicked, this, &LtsOperatorMenu::close); - connect(m_ui->pbSwap, &QPushButton::clicked, this, &LtsOperatorMenu::signalSwap); - connect(m_ui->pbSwap, &QPushButton::clicked, this, &LtsOperatorMenu::close); - connect(m_ui->pbAbout, &QPushButton::clicked, this, &LtsOperatorMenu::slotShowInfoDialog); - connect(m_ui->pbAbout, &QPushButton::clicked, this, &LtsOperatorMenu::close); - - - if (NGL::translationLocale() == "de_DE") - m_ui->pbGer->setChecked(true); - else - m_ui->pbInt->setChecked(true); - - connect(m_ui->pbGer, &QPushButton::clicked, - this, &LtsOperatorMenu::onLanguageChange); - connect(m_ui->pbInt, &QPushButton::clicked, - this, &LtsOperatorMenu::onLanguageChange); - -#ifndef LTS_DEBUG - m_ui->debugWidget->hide(); -#endif -} - - -LtsOperatorMenu::~LtsOperatorMenu() -{ - delete m_ui; -} - - -void LtsOperatorMenu::slotShowInfoDialog() -{ - LtsInfoDialog *infoDialog = new LtsInfoDialog(); - infoDialog->setAttribute(Qt::WA_DeleteOnClose); - infoDialog->show(); -} - - -void LtsOperatorMenu::onTerminalButtonClicked() -{ - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - - QString lang = QString::fromLatin1(NGL::translationLocale()); - lang.append(QStringLiteral(".UTF-8")); - env.insert(QStringLiteral("LANG"), lang); - - QProcess process; - process.setProcessEnvironment(env); - QStringList args; - args << QStringLiteral("--geometry") << QStringLiteral("800x800+200+100"); - - if (QApplication::queryKeyboardModifiers() == Qt::ControlModifier) - process.startDetached(QStringLiteral("xterm"), args); - else - process.startDetached(QStringLiteral("konsole"), args); -} - - -void LtsOperatorMenu::setTerminalButton(bool bVisible) -{ - m_ui->pbWindow->setVisible(bVisible); -} - -void LtsOperatorMenu::setOperatorSettingsButton(bool bVisible) -{ - m_ui->pbOperatorSettings->setVisible(bVisible); -} - - -void LtsOperatorMenu::onLanguageChange() -{ - if (m_ui->pbGer->isChecked()) - NGL::setTranslationLocale(QByteArray("de_DE")); - - if (m_ui->pbInt->isChecked()) - NGL::setTranslationLocale(QByteArray("en_GB")); -} -} diff --git a/Lts/Common/LtsWidgets/lts_operator_menu.h b/Lts/Common/LtsWidgets/lts_operator_menu.h deleted file mode 100755 index fabd479b1..000000000 --- a/Lts/Common/LtsWidgets/lts_operator_menu.h +++ /dev/null @@ -1,42 +0,0 @@ - - - -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_widgets_export.h" - - -namespace Ui { -class LtsOperatorMenu; -} - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsOperatorMenu : public LtsFramelessDialog -{ - Q_OBJECT - -public: - explicit LtsOperatorMenu(const QString &operatorName, const QString &workspaceId, bool debug, int screenCount, QWidget *parent); - ~LtsOperatorMenu(); - - void setTerminalButton(bool bVisible); - void setOperatorSettingsButton(bool bVisible); - -private: - Ui::LtsOperatorMenu *m_ui; - -signals: - void signalLogout(); - void signalExit(); - void signalShowOperatorSettings(); - void signalSwap(); - -private slots: - void onTerminalButtonClicked(); - void onLanguageChange(); - void slotShowInfoDialog(); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_operator_menu.ui b/Lts/Common/LtsWidgets/lts_operator_menu.ui deleted file mode 100755 index aed0c23cc..000000000 --- a/Lts/Common/LtsWidgets/lts_operator_menu.ui +++ /dev/null @@ -1,247 +0,0 @@ - - - LtsOperatorMenu - - - - 0 - 0 - 310 - 504 - - - - - 0 - 0 - - - - - 0 - 0 - - - - Operator Menu - - - - - - - 16 - - - - %OPERATORNAME% - - - Qt::AlignCenter - - - true - - - - - - - %HOSTNAME% - - - Qt::AlignCenter - - - - - - - Workspace: %WORKSPACEID% - - - Qt::AlignCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - - - - Operator Settings - - - - - - - Terminal Window - - - - - - - Qt::Horizontal - - - - - - - Swap with another window - - - - - - - Operator Logout - - - - - - - Exit Application - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - About - - - - - - - Cancel - - - - - - - - 0 - 0 - - - - - 50 - 50 - - - - - - - Qt::Horizontal - - - - - - - For Debug only - - - - - - - - - Deutsch - German - - - - - - - :/Germany-Flag.png:/Germany-Flag.png - - - true - - - true - - - true - - - - - - - English - Englisch (International) - - - - - - - :/Great-Britain-Flag.png:/Great-Britain-Flag.png - - - true - - - true - - - false - - - - - - - Qt::Horizontal - - - - 18 - 29 - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_overlay_loading.cpp b/Lts/Common/LtsWidgets/lts_overlay_loading.cpp deleted file mode 100755 index bd756988b..000000000 --- a/Lts/Common/LtsWidgets/lts_overlay_loading.cpp +++ /dev/null @@ -1,45 +0,0 @@ - - - -#include "lts_overlay_loading.h" -#include "ui_lts_overlay_loading.h" - -#include -#include -#include -#include - - -namespace LtsWidgets -{ -LtsOverlayLoading::LtsOverlayLoading(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::LtsOverlayLoading) -{ - m_ui->setupUi(this); - setCancelButtonVisible(false); - connect(m_ui->pbCancel, &QPushButton::clicked, - this, [this](){ setCancelButtonEnabled(false); }); - connect(m_ui->pbCancel, &QPushButton::clicked, - this, &LtsOverlayLoading::cancel); -} - - -void LtsOverlayLoading::setText(const QString &text) -{ - m_ui->label->setText(text); -} - - -void LtsOverlayLoading::setCancelButtonVisible(bool visible) -{ - m_ui->pbCancel->setVisible(visible); -} - - -void LtsOverlayLoading::setCancelButtonEnabled(bool enabled) -{ - m_ui->pbCancel->setEnabled(enabled); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_overlay_loading.h b/Lts/Common/LtsWidgets/lts_overlay_loading.h deleted file mode 100755 index e6184f487..000000000 --- a/Lts/Common/LtsWidgets/lts_overlay_loading.h +++ /dev/null @@ -1,34 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace Ui { class LtsOverlayLoading; } - - -namespace LtsWidgets -{ - - -class LTSWIDGETS_EXPORT LtsOverlayLoading : public QWidget -{ - Q_OBJECT -public: - explicit LtsOverlayLoading(QWidget *parent = nullptr); - - void setText(const QString &text); - void setCancelButtonVisible(bool visible); - void setCancelButtonEnabled(bool enabled); - -signals: - void cancel(); - -private: - Ui::LtsOverlayLoading *m_ui; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_overlay_loading.ui b/Lts/Common/LtsWidgets/lts_overlay_loading.ui deleted file mode 100755 index 2389e32b3..000000000 --- a/Lts/Common/LtsWidgets/lts_overlay_loading.ui +++ /dev/null @@ -1,76 +0,0 @@ - - - LtsOverlayLoading - - - - 0 - 0 - 370 - 120 - - - - - 0 - 0 - - - - - 370 - 120 - - - - - 555 - 200 - - - - - - - - - - Loading ... - - - Qt::AlignCenter - - - true - - - - - - - - - 0 - - - 0 - - - - - - - &Cancel - - - Shift+C - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_picture_view.cpp b/Lts/Common/LtsWidgets/lts_picture_view.cpp deleted file mode 100755 index eccdac91a..000000000 --- a/Lts/Common/LtsWidgets/lts_picture_view.cpp +++ /dev/null @@ -1,339 +0,0 @@ - - - -#include "lts_picture_view.h" -#include -#include -#include -#include - - -using namespace LtsWidgets; - -LtsPictureView::LtsPictureView(QWidget *parent) - : QGraphicsView(parent) - , m_gsPicture(new QGraphicsScene(this)) - , m_rubberband(new QRubberBand(QRubberBand::Rectangle, this)) - , m_image(nullptr) - , m_origimage(nullptr) -{ - m_mouseActive = false; - m_moveable = false; - m_aspectRatioEnabled = true; - m_s_inEdit = false; - - m_aspectRatio = 1; - - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); -} - -LtsPictureView::~LtsPictureView() -{ - delete m_rubberband; -} - -void LtsPictureView::setAspectRatio(double aspectRatio) -{ - m_aspectRatio = aspectRatio; - newRubberBand(); -} - -void LtsPictureView::ratioClicked(bool enabled) -{ - m_aspectRatioEnabled = enabled; - if (enabled) - newRubberBand(); -} - -void LtsPictureView::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) - { - if (m_rubberband->geometry().contains(event->pos())) - { - m_bandoffset = event->pos() - m_rubberband->pos(); - m_moveable = true; - m_rubberband->show(); - } - else - { - m_origin = event->pos(); - m_curRectangle = QRect(m_origin.x(), m_origin.y(), 0, 0); - m_rubberband->setGeometry(m_curRectangle); - m_rubberband->show(); - m_mouseActive = true; - } - } - else - { - hideRubberband(); - newRubberBand(); - m_mouseActive = false; - } - QGraphicsView::mousePressEvent(event); -} - -void LtsPictureView::mouseMoveEvent(QMouseEvent *event) -{ - if (m_moveable == true) - { - QPoint newCoord = event->pos() - m_bandoffset; - if (newCoord.x() < 0) - newCoord.setX(0); - if (newCoord.y() < 0) - newCoord.setY(0); - if (m_origimage->pixmap().width() < (newCoord.x() + m_rubberband->width())) - newCoord.setX(m_origimage->pixmap().width() - m_rubberband->width() + 1); - if (m_origimage->pixmap().height() < (newCoord.y() + m_rubberband->height())) - newCoord.setY(m_origimage->pixmap().height() - m_rubberband->height() + 1); - m_rubberband->move(newCoord); - QPoint move = m_rubberband->pos(); - int left = move.x(); - int top = move.y(); - int width = m_rubberband->rect().width(); - int height = m_rubberband->rect().height(); - emit mouseMoved(QRect(left, top, width, height)); - } - else - { - if (m_mouseActive) - { - QRect rect; - - int xMax = event->pos().x(); - int yMax = event->pos().y(); - - if (event->pos().x() > m_origimage->pixmap().width()) - xMax = m_origimage->pixmap().width(); - if (event->pos().x() < 0) - xMax = 0; - if (event->pos().y() > m_origimage->pixmap().height()) - yMax = m_origimage->pixmap().height(); - if (event->pos().y() < 0) - yMax = 0; - - if (m_aspectRatioEnabled) - { - const int xMin = m_origin.x(); - const int yMin = m_origin.y(); - - int xLengthAbs(std::abs(xMax - xMin)); - int yLengthAbs(std::abs(yMax - yMin)); - - QPoint length; - - if (xLengthAbs / m_aspectRatio > yLengthAbs * m_aspectRatio) - { - length.setX(xLengthAbs); - length.setY(length.x() / m_aspectRatio); - } - else - { - length.setY(yLengthAbs); - length.setX(length.y() * m_aspectRatio); - } - - if (!(yMax - yMin < 0) && length.y() + m_origin.y() > m_origimage->pixmap().height()) - { - length.setY(m_origimage->pixmap().height() - m_origin.y()); - length.setX(length.y()* m_aspectRatio); - } - if (!(xMax - xMin < 0) && length.x() + m_origin.x() > m_origimage->pixmap().width()) - { - length.setX(m_origimage->pixmap().width() - m_origin.x()); - length.setY(length.x() / m_aspectRatio); - } - - if (xMax - xMin < 0) - { - length.setX(length.x() * -1); - if (length.x() + m_origin.x() < 0) - { - length.setX(0 - m_origin.x()); - length.setY((length.x() / m_aspectRatio) * -1); - } - } - - if (yMax - yMin < 0) - { - length.setY(length.y() * -1); - if (length.y() + m_origin.y() < 0) - { - length.setY(0 - m_origin.y()); - length.setX(length.y() * m_aspectRatio); - if (!(xMax - xMin < 0)) - length.setX(length.x() * -1); - } - } - - length = m_origin + length; - rect.setTopLeft(m_origin); - rect.setBottomRight(length); - } - else - { - rect.setTopLeft(m_origin); - rect.setBottomRight(QPoint(xMax, yMax)); - } - m_curRectangle = rect.normalized(); - m_rubberband->setGeometry(m_curRectangle); - emit mouseMoved(m_curRectangle); - } - } - QGraphicsView::mouseMoveEvent(event); -} - -void LtsPictureView::mouseReleaseEvent(QMouseEvent *event) -{ - m_moveable = false; - QGraphicsView::mouseReleaseEvent(event); -} - -void LtsPictureView::hideRubberband() -{ - m_rubberband->hide(); -} - -void LtsPictureView::newRubberBand() -{ - delete m_rubberband; - m_rubberband = new QRubberBand(QRubberBand::Rectangle, this); -} - -void LtsPictureView::setPicture(const QImage &image, const QPoint &pos, const bool flag) -{ - if (m_image) - { - m_gsPicture->removeItem(m_image); - delete m_image; - } - if (flag) - { - if (m_origimage) - { - m_gsPicture->removeItem(m_origimage); - delete m_origimage; - } - QGraphicsOpacityEffect *opacity = new QGraphicsOpacityEffect; - opacity->setOpacity(0.2); - - m_origimage = new QGraphicsPixmapItem(QPixmap::fromImage(image)); - m_origimage->setGraphicsEffect(opacity); - - m_gsPicture->addItem(m_origimage); - - setMinimumSize(image.size()); - setMaximumSize(image.size()); - emit setLimits(m_origimage->pixmap().size(), true); - } - m_image = new QGraphicsPixmapItem(QPixmap::fromImage(image)); - - m_image->setPos(pos); - m_gsPicture->addItem(m_image); - setScene(m_gsPicture); -} - -void LtsPictureView::leftChanged(int left) -{ - if (m_s_inEdit || m_mouseActive) - return; - m_s_inEdit = true; - m_curRectangle.setLeft(left); - if(m_aspectRatioEnabled) - { - int newHeight = m_curRectangle.width() / m_aspectRatio; - if (m_origin.y() < m_curRectangle.bottom()) - { - m_curRectangle.setHeight(newHeight); - } - else - { - int offset = m_curRectangle.height() - newHeight; - m_curRectangle.setTop(m_curRectangle.top() + offset); - } - } - m_rubberband->setGeometry(m_curRectangle); - emit mouseMoved(m_curRectangle); - m_s_inEdit = false; -} - -void LtsPictureView::rightChanged(int right) -{ - if (m_s_inEdit || m_mouseActive) - return; - m_s_inEdit = true; - m_curRectangle.setRight(right); - if(m_aspectRatioEnabled) - { - int newHeight = m_curRectangle.width() / m_aspectRatio; - if (m_origin.y() < m_curRectangle.bottom()) - { - m_curRectangle.setHeight(newHeight); - } - else - { - int offset = m_curRectangle.height() - newHeight; - m_curRectangle.setTop(m_curRectangle.top() + offset); - } - } - m_rubberband->setGeometry(m_curRectangle); - emit mouseMoved(m_curRectangle); - m_s_inEdit = false; -} - -void LtsPictureView::topChanged(int top) -{ - if (m_s_inEdit || m_mouseActive) - return; - m_s_inEdit = true; - m_curRectangle.setTop(top); - if(m_aspectRatioEnabled) - { - int newWidth = m_curRectangle.height() * m_aspectRatio; - if (m_origin.x() <= m_curRectangle.left()) - { - m_curRectangle.setWidth(newWidth); - } - else - { - int offset = m_curRectangle.width() - newWidth; - m_curRectangle.setLeft(m_curRectangle.left() + offset); - } - } - m_rubberband->setGeometry(m_curRectangle); - emit mouseMoved(m_curRectangle); - m_s_inEdit = false; -} - -void LtsPictureView::bottomChanged(int bottom) -{ - if (m_s_inEdit || m_mouseActive) - return; - m_s_inEdit = true; - m_curRectangle.setBottom(bottom); - if(m_aspectRatioEnabled) - { - int newWidth = m_curRectangle.height() * m_aspectRatio; - if (m_origin.x() <= m_curRectangle.left()) - { - m_curRectangle.setWidth(newWidth); - } - else - { - int offset = m_curRectangle.width() - newWidth; - m_curRectangle.setLeft(m_curRectangle.left() + offset); - } - } - m_rubberband->setGeometry(m_curRectangle); - emit mouseMoved(m_curRectangle); - m_s_inEdit = false; -} - - -QGraphicsPixmapItem *LtsPictureView::getImage() -{ - if (!m_image) - m_image = new QGraphicsPixmapItem; - return m_image; -} diff --git a/Lts/Common/LtsWidgets/lts_picture_view.h b/Lts/Common/LtsWidgets/lts_picture_view.h deleted file mode 100755 index 78a0eab1d..000000000 --- a/Lts/Common/LtsWidgets/lts_picture_view.h +++ /dev/null @@ -1,60 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" -#include - -class QRubberBand; - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsPictureView : public QGraphicsView -{ - Q_OBJECT - -public: - explicit LtsPictureView(QWidget *parent = nullptr); - ~LtsPictureView(); - void hideRubberband(); - void newRubberBand(); - void cutPicture(); - QGraphicsPixmapItem *getImage(); - -protected: - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - -private: - bool m_mouseActive; - bool m_moveable; - bool m_aspectRatioEnabled; - bool m_s_inEdit; - - double m_aspectRatio; - QRect m_curRectangle; - - QGraphicsScene *m_gsPicture; - QRubberBand *m_rubberband; - QGraphicsPixmapItem *m_image; - QGraphicsPixmapItem *m_origimage; - - QPoint m_origin; - QPoint m_bandoffset; - -public slots: - void setPicture(const QImage &image, const QPoint &pos, const bool flag); - void setAspectRatio(double aspectRatio); - void ratioClicked(bool enabled); - void leftChanged(int left); - void rightChanged(int right); - void topChanged(int top); - void bottomChanged(int bottom); - -signals: - void mouseMoved(const QRect &); - void setLimits(const QSize &, bool); -}; -} diff --git a/Lts/Common/LtsWidgets/lts_profile_button.cpp b/Lts/Common/LtsWidgets/lts_profile_button.cpp deleted file mode 100755 index e29651b58..000000000 --- a/Lts/Common/LtsWidgets/lts_profile_button.cpp +++ /dev/null @@ -1,133 +0,0 @@ - - - -#include "lts_profile_button.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace LtsWidgets; - - -LtsProfileButton::LtsProfileButton(QWidget *parent) - : QWidget(parent) - , m_isHovered(false) - , m_menuEnabled(true) -{ - QSizePolicy qsp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - qsp.setHeightForWidth(true); - setSizePolicy(qsp); -} - - -void LtsProfileButton::setPixmap(const QPixmap &pixmap) -{ - if (pixmap.isNull()) - m_pixmap = QPixmap(QStringLiteral("icon:no_pic.svg")); - else - m_pixmap = pixmap; - - update(); -} - - -void LtsProfileButton::enterEvent(QEvent *event) -{ - - m_isHovered = true; - update(); - QWidget::enterEvent(event); -} - - -void LtsProfileButton::leaveEvent(QEvent *event) -{ - - m_isHovered = false; - update(); - QWidget::leaveEvent(event); -} - - -bool LtsProfileButton::event(QEvent *event) -{ - if (event->type() == QEvent::MouseButtonPress && m_menuEnabled) - { - emit signalClicked(); - event->accept(); - - update(); - return true; - } - - return QWidget::event(event); -} - - -void LtsProfileButton::paintEvent(QPaintEvent* event) -{ - QPainter p(this); - p.save(); - - int minSize; - QPointF center; - minSize = height() > width() ? width() : height(); - center = QPointF(width() / 2.0, height() / 2.0); - - - - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - - - QPixmap pixmap(minSize, minSize); - pixmap.fill(Qt::white); - - if (!m_pixmap.isNull()) - { - if (!(m_isHovered && m_menuEnabled)) - { - pixmap = m_pixmap.scaled(minSize, minSize, Qt::KeepAspectRatioByExpanding); - } - } - - - - QPainterPath path; - path.addEllipse(center, - minSize / 2.0 - 4, - minSize / 2.0 - 4); - p.setClipPath(path); - p.drawPixmap(center - QPointF(minSize/2.0,minSize/2.0),pixmap); - - - - - p.setPen(QPen(QColor("gray"), 2)); - p.drawEllipse(center, - minSize / 2.0 - 4, - minSize / 2.0 - 4); - - - - if (m_isHovered && m_menuEnabled) - { - p.setPen(QPen(QColor(QStringLiteral("gray")), (minSize / 48 * 2))); - - double xc = size().width()/2.012; - double yc = size().height()/2.012; - double diff = minSize/2.012; - - p.drawLine(QPointF(xc - (diff/2.01f), yc - (diff/3.01f)), QPointF(xc + (diff/2.02f), yc - (diff/3.02f))); - p.drawLine(QPointF(xc - (diff/2.01f), yc - 0.01f), QPointF(xc + (diff/2.02f), yc - 0)); - p.drawLine(QPointF(xc - (diff/2.01f), yc + (diff/3.01f)), QPointF(xc + (diff/2.02f), yc + (diff/3.02f))); - } - - - event->accept(); - p.restore(); -} diff --git a/Lts/Common/LtsWidgets/lts_profile_button.h b/Lts/Common/LtsWidgets/lts_profile_button.h deleted file mode 100755 index 1f832c5fe..000000000 --- a/Lts/Common/LtsWidgets/lts_profile_button.h +++ /dev/null @@ -1,55 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsProfileButton : public QWidget -{ - Q_OBJECT - - Q_PROPERTY( QPixmap pixMap READ pixmap WRITE setPixmap ) - Q_PROPERTY( bool menuEnabled READ menuEnabled WRITE setMenuEnabled ) - -public: - explicit LtsProfileButton(QWidget *parent = nullptr); - - - - void setPixmap(const QPixmap &pixmap); - - const QPixmap &pixmap() const { return m_pixmap; } - - - - void setMenuEnabled(bool enabled = true) { m_menuEnabled = enabled; } - bool menuEnabled() const { return m_menuEnabled; } - - -protected: - void paintEvent(QPaintEvent *event); - void enterEvent(QEvent *event); - void leaveEvent(QEvent *event); - bool event(QEvent *event); - -private: - QPixmap m_pixmap; - bool m_isHovered; - bool m_menuEnabled; - -signals: - - - void signalClicked(); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp b/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp deleted file mode 100755 index 3a64d04f1..000000000 --- a/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp +++ /dev/null @@ -1,147 +0,0 @@ - - - -#include "lts_progress_dialog_helper.h" - -#include "lts_frameless_dialog.h" - -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ - -LtsProgressDialogHelper::LtsProgressDialogHelper(QWidget *parent) - : QObject(parent) - , m_waitDlg(nullptr) - , m_parentWidget(parent) - , m_textLabel(nullptr) -{} - - -LtsProgressDialogHelper::~LtsProgressDialogHelper() -{ - delete m_waitDlg; -} - - -void LtsProgressDialogHelper::setCustomText(const QString &customText) -{ - if (m_waitDlg == nullptr || m_textLabel == nullptr) - return; - - if (customText.isEmpty()) - m_textLabel->setText(tr("Waiting for server reply...")); - else - m_textLabel->setText(customText); -} - -void LtsProgressDialogHelper::setParentWidget(QWidget *newParent) -{ - m_parentWidget = newParent; - if (m_waitDlg) - { - m_waitDlg->setParent(newParent); - QTimer::singleShot(0, m_waitDlg.data(), &LtsFramelessDialog::onUpdateGeometry); - } -} - -void LtsProgressDialogHelper::slotShowWaitForReplyWindow() -{ - if (m_waitDlg) - m_waitDlg->deleteLater(); - m_waitDlg = new LtsFramelessDialog(m_parentWidget, Behaviour::Modal); - - QHBoxLayout *l = new QHBoxLayout(m_waitDlg); - - QProgressBar *pb = new QProgressBar(m_waitDlg); - pb->setMaximum(0); - pb->setValue(-1); - - l->addWidget(pb); - - m_textLabel = new QLabel(m_waitDlg); - setCustomText(); - l->addWidget(m_textLabel); - - m_waitDlg->setLayout(l); - m_waitDlg->show(); - m_waitDlg->raise(); - - -} - - -void LtsProgressDialogHelper::slotStopWaitForReplyWindow(const ReplyResult &replyResult, const QString &replyString) -{ - if (m_waitDlg == nullptr) - { - - return; - } - - - m_waitDlg->deleteLater(); - m_waitDlg = nullptr; - - - QString resultText = tr("Unknown result (%1)!").arg((int)replyResult); - - switch (replyResult) - { - case ReplyResult::Close: - { - return; - } - - case ReplyResult::Success: - { - resultText = tr("Operation was successful"); - break; - } - - case ReplyResult::Failure: - { - resultText = tr("Operation has failed!\nReason: '%1'").arg(replyString); - break; - } - - case ReplyResult::TimeOut: - { - resultText = tr("Operation has timed out and could not be processed! Please try again later"); - break; - } - } - - m_waitDlg = new LtsFramelessDialog(m_parentWidget, Behaviour::Modal); - - QVBoxLayout *newLayout = new QVBoxLayout(m_waitDlg); - newLayout->addWidget(new QLabel(resultText, m_waitDlg)); - - QPushButton *btn = new QPushButton(tr("OK"),m_waitDlg); - connect(btn, &QPushButton::clicked, - this, &LtsProgressDialogHelper::accept); - - newLayout->addWidget(btn); - - m_waitDlg->setLayout(newLayout); - m_waitDlg->show(); - - -} - - -void LtsProgressDialogHelper::accept() -{ - m_waitDlg->accept(); - m_waitDlg->deleteLater(); - m_waitDlg = nullptr; - emit accepted(); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h b/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h deleted file mode 100755 index d402c1038..000000000 --- a/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h +++ /dev/null @@ -1,66 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include - -class QLabel; -class QWidget; - -namespace LtsWidgets -{ -class LtsFramelessDialog; - - - -class LTSWIDGETS_EXPORT LtsProgressDialogHelper : public QObject -{ - Q_OBJECT - -public: - - - enum class ReplyResult - { - Close = 0, - Success, - Failure, - TimeOut, - }; - - explicit LtsProgressDialogHelper(QWidget *parent = nullptr); - ~LtsProgressDialogHelper(); - bool isShown() const { return !!m_waitDlg; } - void setCustomText(const QString &customText = QString()); - - void setParentWidget(QWidget *newParent); - -public slots: - - - void slotShowWaitForReplyWindow(); - - - - void slotStopWaitForReplyWindow(const LtsWidgets::LtsProgressDialogHelper::ReplyResult &replyResult = ReplyResult::Close , - const QString &replyString = QString()); - -private: - QPointer m_waitDlg; - QWidget *m_parentWidget; - QPointer m_textLabel; - -signals: - - - void accepted(); - -private slots: - void accept(); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_range_slider.cpp b/Lts/Common/LtsWidgets/lts_range_slider.cpp deleted file mode 100755 index 89781980f..000000000 --- a/Lts/Common/LtsWidgets/lts_range_slider.cpp +++ /dev/null @@ -1,823 +0,0 @@ - - - - - - -#include "lts_range_slider.h" - - -#include -#include -#include -#include -#include -#include -#include - -namespace LtsWidgets -{ - -class LtsRangeSliderPrivate -{ - Q_DECLARE_PUBLIC(LtsRangeSlider) -protected: - LtsRangeSlider *const q_ptr; -public: - - - enum Handle - { - NoHandle = 0x0, - MinimumHandle = 0x1, - MaximumHandle = 0x2 - }; - Q_DECLARE_FLAGS(Handles, Handle) - - explicit LtsRangeSliderPrivate(LtsRangeSlider &object); - void init(); - - - - - - Handle handleAtPos(const QPoint &pos, QRect &handleRect) const; - - - int pixelPosToRangeValue(int pos) const; - int pixelPosFromRangeValue(int val) const; - - - void drawMinimumSlider(QStylePainter *painter) const; - void drawMaximumSlider(QStylePainter *painter) const; - - - int m_minimumValue = 0; - int m_maximumValue = 100; - - - int m_minimumPosition = 0; - int m_maximumPosition = 100; - - - QStyle::SubControl m_minimumSliderSelected = QStyle::SC_None; - QStyle::SubControl m_maximumSliderSelected = QStyle::SC_None; - - - - int m_subclassClickOffset = 0; - - - - int m_subclassPosition = 0; - - - float m_subclassWidth = 0; - - LtsRangeSliderPrivate::Handles m_selectedHandles = {}; - - - - bool m_symmetricMoves = false; - - QString m_handleToolTip; - -private: - Q_DISABLE_COPY(LtsRangeSliderPrivate) -}; - - -LtsRangeSliderPrivate::LtsRangeSliderPrivate(LtsRangeSlider &object) - : q_ptr(&object) -{} - - -void LtsRangeSliderPrivate::init() -{ - Q_Q(LtsRangeSlider); - m_minimumValue = q->minimum(); - m_maximumValue = q->maximum(); - m_minimumPosition = q->minimum(); - m_maximumPosition = q->maximum(); - q->connect(q, &LtsRangeSlider::rangeChanged, q, &LtsRangeSlider::onRangeChanged); -} - - -LtsRangeSliderPrivate::Handle LtsRangeSliderPrivate::handleAtPos(const QPoint &pos, QRect &handleRect)const -{ - Q_Q(const LtsRangeSlider); - - QStyleOptionSlider option; - q->initStyleOption(&option); - - - - - - - option.sliderPosition = m_minimumPosition; - option.sliderValue = m_minimumValue; - - QStyle::SubControl minimumControl = q->style()->hitTestComplexControl( - QStyle::CC_Slider, &option, pos, q); - QRect minimumHandleRect = q->style()->subControlRect( - QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q); - - - option.sliderPosition = m_maximumPosition; - option.sliderValue = m_maximumValue; - - QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q); - QRect maximumHandleRect = q->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q); - - - if (minimumControl == QStyle::SC_SliderHandle && - maximumControl == QStyle::SC_SliderHandle) - { - int minDist = 0; - int maxDist = 0; - if (q->orientation() == Qt::Horizontal) - { - minDist = pos.x() - minimumHandleRect.left(); - maxDist = maximumHandleRect.right() - pos.x(); - } - else - { - minDist = minimumHandleRect.bottom() - pos.y(); - maxDist = pos.y() - maximumHandleRect.top(); - } - Q_ASSERT(minDist >= 0 && maxDist >= 0); - minimumControl = minDist < maxDist ? minimumControl : QStyle::SC_None; - } - - if (maximumControl == QStyle::SC_SliderHandle) - { - handleRect = maximumHandleRect; - return MaximumHandle; - } - else if (minimumControl == QStyle::SC_SliderHandle) - { - handleRect = minimumHandleRect; - return MinimumHandle; - } - - handleRect = minimumHandleRect.united(maximumHandleRect); - return NoHandle; -} - - - - -int LtsRangeSliderPrivate::pixelPosToRangeValue(int pos) const -{ - Q_Q(const LtsRangeSlider); - QStyleOptionSlider option; - q->initStyleOption(&option); - - QRect gr = q->style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderGroove, - q); - QRect sr = q->style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderHandle, - q); - int sliderMin, sliderMax, sliderLength; - if (option.orientation == Qt::Horizontal) - { - sliderLength = sr.width(); - sliderMin = gr.x(); - sliderMax = gr.right() - sliderLength + 1; - } - else - { - sliderLength = sr.height(); - sliderMin = gr.y(); - sliderMax = gr.bottom() - sliderLength + 1; - } - - return QStyle::sliderValueFromPosition(q->minimum(), - q->maximum(), - pos - sliderMin, - sliderMax - sliderMin, - option.upsideDown); -} - - -int LtsRangeSliderPrivate::pixelPosFromRangeValue(int val) const -{ - Q_Q(const LtsRangeSlider); - QStyleOptionSlider option; - q->initStyleOption(&option); - - QRect gr = q->style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderGroove, - q); - QRect sr = q->style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderHandle, - q); - int sliderMin, sliderMax, sliderLength; - if (option.orientation == Qt::Horizontal) - { - sliderLength = sr.width(); - sliderMin = gr.x(); - sliderMax = gr.right() - sliderLength + 1; - } - else - { - sliderLength = sr.height(); - sliderMin = gr.y(); - sliderMax = gr.bottom() - sliderLength + 1; - } - - return QStyle::sliderPositionFromValue(q->minimum(), - q->maximum(), - val, - sliderMax - sliderMin, - option.upsideDown) + sliderMin; -} - - - -void LtsRangeSliderPrivate::drawMinimumSlider(QStylePainter *painter) const -{ - Q_Q(const LtsRangeSlider); - QStyleOptionSlider option; - q->initMinimumSliderStyleOption(&option); - - option.subControls = QStyle::SC_SliderHandle; - option.sliderValue = m_minimumValue; - option.sliderPosition = m_minimumPosition; - if (q->isMinimumSliderDown()) - { - option.activeSubControls = QStyle::SC_SliderHandle; - option.state |= QStyle::State_Sunken; - } -#ifdef Q_OS_MAC - - QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, - QStyle::SC_SliderHandle, q); - painter->setClipRect(clip); -#endif - painter->drawComplexControl(QStyle::CC_Slider, option); -} - - - -void LtsRangeSliderPrivate::drawMaximumSlider(QStylePainter *painter) const -{ - Q_Q(const LtsRangeSlider); - QStyleOptionSlider option; - q->initMaximumSliderStyleOption(&option); - - option.subControls = QStyle::SC_SliderHandle; - option.sliderValue = m_maximumValue; - option.sliderPosition = m_maximumPosition; - if (q->isMaximumSliderDown()) - { - option.activeSubControls = QStyle::SC_SliderHandle; - option.state |= QStyle::State_Sunken; - } -#ifdef Q_OS_MAC - - QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, - QStyle::SC_SliderHandle, q); - painter->setClipRect(clip); -#endif - painter->drawComplexControl(QStyle::CC_Slider, option); -} - - -LtsRangeSlider::LtsRangeSlider(Qt::Orientation o, - QWidget *parent) - : QSlider(o, parent) - , d_ptr(new LtsRangeSliderPrivate(*this)) -{ - Q_D(LtsRangeSlider); - d->init(); -} - - -LtsRangeSlider::LtsRangeSlider(LtsRangeSliderPrivate *impl, Qt::Orientation o, - QWidget *parent) - : QSlider(o, parent) - , d_ptr(impl) -{ - Q_D(LtsRangeSlider); - d->init(); -} - - -LtsRangeSlider::~LtsRangeSlider() -{ -} - - -int LtsRangeSlider::minimumValue() const -{ - Q_D(const LtsRangeSlider); - return d->m_minimumValue; -} - - -void LtsRangeSlider::setMinimumValue(int min) -{ - Q_D(LtsRangeSlider); - setValues(min, qMax(d->m_maximumValue, min)); -} - - -int LtsRangeSlider::maximumValue() const -{ - Q_D(const LtsRangeSlider); - return d->m_maximumValue; -} - - -void LtsRangeSlider::setMaximumValue(int max) -{ - Q_D(LtsRangeSlider); - setValues(qMin(d->m_minimumValue, max), max); -} - - -void LtsRangeSlider::setValues(int min, int max) -{ - Q_D(LtsRangeSlider); - const int minValue = qBound(minimum(), qMin(min, max), maximum()); - const int maxValue = qBound(minimum(), qMax(min, max), maximum()); - bool emitMinValChanged = (minValue != d->m_minimumValue); - bool emitMaxValChanged = (maxValue != d->m_maximumValue); - - d->m_minimumValue = minValue; - d->m_maximumValue = maxValue; - - bool emitMinPosChanged = (minValue != d->m_minimumPosition); - bool emitMaxPosChanged = (maxValue != d->m_maximumPosition); - d->m_minimumPosition = minValue; - d->m_maximumPosition = maxValue; - - if (isSliderDown()) - { - if (emitMinPosChanged || emitMaxPosChanged) - emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition); - if (emitMinPosChanged) - emit minimumPositionChanged(d->m_minimumPosition); - if (emitMaxPosChanged) - emit maximumPositionChanged(d->m_maximumPosition); - } - if (emitMinValChanged || emitMaxValChanged) - emit valuesChanged(d->m_minimumValue, d->m_maximumValue); - - if (emitMinValChanged) - emit minimumValueChanged(d->m_minimumValue); - - if (emitMaxValChanged) - emit maximumValueChanged(d->m_maximumValue); - - if (emitMinPosChanged || emitMaxPosChanged || - emitMinValChanged || emitMaxValChanged) - update(); -} - - -int LtsRangeSlider::minimumPosition() const -{ - Q_D(const LtsRangeSlider); - return d->m_minimumPosition; -} - - -int LtsRangeSlider::maximumPosition() const -{ - Q_D(const LtsRangeSlider); - return d->m_maximumPosition; -} - - -void LtsRangeSlider::setMinimumPosition(int min) -{ - Q_D(const LtsRangeSlider); - setPositions(min, qMax(min, d->m_maximumPosition)); -} - - -void LtsRangeSlider::setMaximumPosition(int max) -{ - Q_D(const LtsRangeSlider); - setPositions(qMin(d->m_minimumPosition, max), max); -} - - -void LtsRangeSlider::setPositions(int min, int max) -{ - Q_D(LtsRangeSlider); - const int minPosition = qBound(minimum(), qMin(min, max), maximum()); - const int maxPosition = qBound(minimum(), qMax(min, max), maximum()); - - bool emitMinPosChanged = (minPosition != d->m_minimumPosition); - bool emitMaxPosChanged = (maxPosition != d->m_maximumPosition); - - if (!emitMinPosChanged && !emitMaxPosChanged) - return; - - d->m_minimumPosition = minPosition; - d->m_maximumPosition = maxPosition; - - if (!hasTracking()) - update(); - - if (isSliderDown()) - { - if (emitMinPosChanged) - emit minimumPositionChanged(d->m_minimumPosition); - if (emitMaxPosChanged) - emit maximumPositionChanged(d->m_maximumPosition); - if (emitMinPosChanged || emitMaxPosChanged) - emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition); - } - if (hasTracking()) - { - triggerAction(SliderMove); - setValues(d->m_minimumPosition, d->m_maximumPosition); - } -} - - -void LtsRangeSlider::setSymmetricMoves(bool symmetry) -{ - Q_D(LtsRangeSlider); - d->m_symmetricMoves = symmetry; -} - - -bool LtsRangeSlider::symmetricMoves() const -{ - Q_D(const LtsRangeSlider); - return d->m_symmetricMoves; -} - - -void LtsRangeSlider::onRangeChanged(int minimum, int maximum) -{ - Q_UNUSED(minimum); - Q_UNUSED(maximum); - Q_D(LtsRangeSlider); - setValues(d->m_minimumValue, d->m_maximumValue); -} - - - -void LtsRangeSlider::paintEvent(QPaintEvent *) -{ - Q_D(LtsRangeSlider); - QStyleOptionSlider option; - initStyleOption(&option); - - QStylePainter painter(this); - - - - - painter.setPen(Qt::lightGray); - if (option.orientation == Qt::Horizontal) - { - painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom()); - painter.drawRect(option.rect.left(), option.rect.center().y() - 1, option.rect.right() - option.rect.left(), 3); - painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom()); - } - else - { - painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()); - painter.drawRect(option.rect.center().x() - 1, option.rect.top(), 3, option.rect.top() - option.rect.bottom()); - painter.drawLine(option.rect.center().x(), option.rect.top(), option.rect.center().x(), option.rect.bottom()); - painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()); - } - - - option.subControls = QStyle::SC_SliderGroove; - - - - option.sliderValue = minimum() - maximum(); - option.sliderPosition = minimum() - maximum(); - option.subControls &= ~QStyle::SC_SliderGroove; - painter.drawComplexControl(QStyle::CC_Slider, option); - option.subControls |= QStyle::SC_SliderGroove; - - option.sliderPosition = d->m_minimumPosition; - const QRect lr = style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderHandle, - this); - - option.sliderPosition = d->m_maximumPosition; - const QRect ur = style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderHandle, - this); - - QRect sr = style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderGroove, - this); - QRect rangeBox; - if (option.orientation == Qt::Horizontal) - { - rangeBox = QRect(QPoint(qMin(lr.center().x(), ur.center().x()), sr.center().y() - 2), - QPoint(qMax(lr.center().x(), ur.center().x()), sr.center().y() + 1)); - } - else - { - rangeBox = QRect(QPoint(sr.center().x() - 2, qMin(lr.center().y(), ur.center().y())), - QPoint(sr.center().x() + 1, qMax(lr.center().y(), ur.center().y()))); - } - - - - - QRect groove = style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderGroove, - this); - groove.adjust(0, 0, -1, 0); - - - - QColor highlight = palette().color(QPalette::Normal, QPalette::Highlight); - QLinearGradient gradient; - if (option.orientation == Qt::Horizontal) - gradient = QLinearGradient(groove.center().x(), groove.top(), groove.center().x(), groove.bottom()); - else - gradient = QLinearGradient(groove.left(), groove.center().y(), groove.right(), groove.center().y()); - - - - - - gradient.setColorAt(0, highlight.darker(120)); - gradient.setColorAt(1, highlight.lighter(160)); - - painter.setPen(QPen(highlight.darker(150), 0)); - painter.setBrush(gradient); - painter.drawRect(rangeBox.intersected(groove)); - - - - - if (isMinimumSliderDown()) - { - painter.setClipRect(ur); - d->drawMaximumSlider(&painter); - painter.setClipRect(lr); - d->drawMinimumSlider(&painter); - } - else - { - painter.setClipRect(lr); - d->drawMinimumSlider(&painter); - painter.setClipRect(ur); - d->drawMaximumSlider(&painter); - } -} - - - -void LtsRangeSlider::mousePressEvent(QMouseEvent *ev) -{ - Q_D(LtsRangeSlider); - if (minimum() == maximum() || (ev->buttons() ^ ev->button())) - { - ev->ignore(); - return; - } - int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y(); - - QStyleOptionSlider option; - initStyleOption(&option); - - QRect handleRect; - LtsRangeSliderPrivate::Handle handle_ = d->handleAtPos(ev->pos(), handleRect); - - if (handle_ != LtsRangeSliderPrivate::NoHandle) - { - if (ev->buttons() == Qt::RightButton) - { - d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.; - d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition); - d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.; - - setSliderDown(true); - if (!isMinimumSliderDown() || !isMaximumSliderDown()) - { - d->m_selectedHandles = QFlags(LtsRangeSliderPrivate::MinimumHandle) | - QFlags(LtsRangeSliderPrivate::MaximumHandle); - QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this); - update(handleRect.united(sr)); - } - ev->accept(); - return; - } - - d->m_subclassPosition = (handle_ == LtsRangeSliderPrivate::MinimumHandle) ? d->m_minimumPosition : d->m_maximumPosition; - - - d->m_subclassClickOffset = mepos - (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top()); - - setSliderDown(true); - - if (d->m_selectedHandles != handle_) - { - d->m_selectedHandles = handle_; - update(handleRect); - } - - ev->accept(); - return; - } - - - - - QStyle::SubControl control = style()->hitTestComplexControl(QStyle::CC_Slider, &option, ev->pos(), this); - QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this); - int minCenter = (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top()); - int maxCenter = (orientation() == Qt::Horizontal ? handleRect.right() : handleRect.bottom()); - if (control == QStyle::SC_SliderGroove && mepos > minCenter && mepos < maxCenter) - { - - d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.; - d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition); - d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.; - - setSliderDown(true); - if (!isMinimumSliderDown() || !isMaximumSliderDown()) - { - d->m_selectedHandles = QFlags(LtsRangeSliderPrivate::MinimumHandle) | - QFlags(LtsRangeSliderPrivate::MaximumHandle); - update(handleRect.united(sr)); - } - ev->accept(); - return; - } - ev->ignore(); -} - - - -void LtsRangeSlider::mouseMoveEvent(QMouseEvent *ev) -{ - Q_D(LtsRangeSlider); - if (!d->m_selectedHandles) - { - ev->ignore(); - return; - } - int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y(); - - QStyleOptionSlider option; - initStyleOption(&option); - - - if (isMinimumSliderDown() && isMaximumSliderDown()) - { - int newPosition = d->pixelPosToRangeValue(mepos); - int newMinPosition = newPosition - static_cast(d->m_subclassWidth); - int newMaxPosition = newPosition + static_cast(d->m_subclassWidth + .5); - - if (newMinPosition < minimum()) - { - newMinPosition = minimum(); - newMaxPosition = newMinPosition + (d->m_maximumPosition - d->m_minimumPosition); - } - else if (newMaxPosition > maximum()) - { - newMaxPosition = maximum(); - newMinPosition = newMaxPosition - (d->m_maximumPosition - d->m_minimumPosition); - } - setPositions(newMinPosition, newMaxPosition); - ev->accept(); - return; - } - - const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &option, this); - - int newPosition = d->pixelPosToRangeValue(mepos - d->m_subclassClickOffset); - - if (m >= 0) - { - const QRect r = rect().adjusted(-m, -m, m, m); - if (!r.contains(ev->pos())) - newPosition = d->m_subclassPosition; - } - - - if (isMinimumSliderDown() && !isMaximumSliderDown()) - { - double newMinPos = qMin(newPosition, d->m_maximumPosition); - setPositions(newMinPos, d->m_maximumPosition + (d->m_symmetricMoves ? d->m_minimumPosition - newMinPos : 0)); - } - - else if (isMaximumSliderDown() && !isMinimumSliderDown()) - { - double newMaxPos = qMax(d->m_minimumPosition, newPosition); - setPositions(d->m_minimumPosition - (d->m_symmetricMoves ? newMaxPos - d->m_maximumPosition : 0), newMaxPos); - } - ev->accept(); -} - - - -void LtsRangeSlider::mouseReleaseEvent(QMouseEvent *ev) -{ - Q_D(LtsRangeSlider); - QSlider::mouseReleaseEvent(ev); - - setSliderDown(false); - d->m_selectedHandles = {}; - - update(); -} - - -bool LtsRangeSlider::isMinimumSliderDown() const -{ - Q_D(const LtsRangeSlider); - return d->m_selectedHandles & LtsRangeSliderPrivate::MinimumHandle; -} - - -bool LtsRangeSlider::isMaximumSliderDown() const -{ - Q_D(const LtsRangeSlider); - return d->m_selectedHandles & LtsRangeSliderPrivate::MaximumHandle; -} - - -void LtsRangeSlider::initMinimumSliderStyleOption(QStyleOptionSlider *option) const -{ - initStyleOption(option); -} - - -void LtsRangeSlider::initMaximumSliderStyleOption(QStyleOptionSlider *option) const -{ - initStyleOption(option); -} - - -QString LtsRangeSlider::handleToolTip()const -{ - Q_D(const LtsRangeSlider); - return d->m_handleToolTip; -} - - -void LtsRangeSlider::setHandleToolTip(const QString &toolTip) -{ - Q_D(LtsRangeSlider); - d->m_handleToolTip = toolTip; -} - - -bool LtsRangeSlider::event(QEvent *event) -{ - Q_D(LtsRangeSlider); - switch (event->type()) - { - case QEvent::ToolTip: - { - QHelpEvent *helpEvent = static_cast(event); - QStyleOptionSlider opt; - - opt.sliderPosition = d->m_minimumPosition; - opt.sliderValue = d->m_minimumValue; - initStyleOption(&opt); - QStyle::SubControl hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this); - if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle) - { - QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(minimumValue())); - event->accept(); - return true; - } - - opt.sliderPosition = d->m_maximumPosition; - opt.sliderValue = d->m_maximumValue; - initStyleOption(&opt); - hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this); - if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle) - { - QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(maximumValue())); - event->accept(); - return true; - } - } - default: - break; - } - return QSlider::event(event); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_range_slider.h b/Lts/Common/LtsWidgets/lts_range_slider.h deleted file mode 100755 index a0283ed47..000000000 --- a/Lts/Common/LtsWidgets/lts_range_slider.h +++ /dev/null @@ -1,194 +0,0 @@ - - - - - -#pragma once - -#include "lts_widgets_export.h" - - -#include - -namespace LtsWidgets -{ - -class LtsRangeSliderPrivate; - - - - - - - - - - - - - - - - -class LTSWIDGETS_EXPORT LtsRangeSlider : public QSlider -{ - Q_OBJECT - - Q_PROPERTY(int minimumValue READ minimumValue WRITE setMinimumValue) - Q_PROPERTY(int maximumValue READ maximumValue WRITE setMaximumValue) - Q_PROPERTY(int minimumPosition READ minimumPosition WRITE setMinimumPosition) - Q_PROPERTY(int maximumPosition READ maximumPosition WRITE setMaximumPosition) - Q_PROPERTY(bool symmetricMoves READ symmetricMoves WRITE setSymmetricMoves) - Q_PROPERTY(QString handleToolTip READ handleToolTip WRITE setHandleToolTip) - -public: - - - - explicit LtsRangeSlider(Qt::Orientation o, QWidget *parent = nullptr); - explicit LtsRangeSlider(QWidget *parent = nullptr) - : LtsRangeSlider(Qt::Vertical, parent) - {} - ~LtsRangeSlider(); - - - - - - - int minimumValue() const; - - - - - - - int maximumValue() const; - - - - - int minimumPosition() const; - void setMinimumPosition(int min); - - - - - int maximumPosition() const; - void setMaximumPosition(int max); - - - - - void setPositions(int min, int max); - - - - - bool symmetricMoves() const; - void setSymmetricMoves(bool symmetry); - - - - - - - QString handleToolTip() const; - void setHandleToolTip(const QString &toolTip); - - - - bool isMinimumSliderDown() const; - - - bool isMaximumSliderDown() const; - -Q_SIGNALS: - - - - void minimumValueChanged(int min); - - - - void maximumValueChanged(int max); - - - void valuesChanged(int min, int max); - - - - - - - void minimumPositionChanged(int min); - - - - - - - void maximumPositionChanged(int max); - - - - - void positionsChanged(int min, int max); - -public Q_SLOTS: - - - - - - - void setMinimumValue(int min); - - - - - - - - void setMaximumValue(int max); - - - - - - - - - void setValues(int min, int max); - -protected Q_SLOTS: - void onRangeChanged(int minimum, int maximum); - -protected: - LtsRangeSlider(LtsRangeSliderPrivate *impl, Qt::Orientation o, QWidget *parent = nullptr); - - - - void mousePressEvent(QMouseEvent *ev) override; - void mouseMoveEvent(QMouseEvent *ev) override; - void mouseReleaseEvent(QMouseEvent *ev) override; - - - - void paintEvent(QPaintEvent *ev) override; - void initMinimumSliderStyleOption(QStyleOptionSlider *option) const; - void initMaximumSliderStyleOption(QStyleOptionSlider *option) const; - - - - bool event(QEvent *event) override; - -protected: - QScopedPointer d_ptr; - -private: - Q_DECLARE_PRIVATE(LtsRangeSlider) - Q_DISABLE_COPY(LtsRangeSlider) -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_selection_popup.cpp b/Lts/Common/LtsWidgets/lts_selection_popup.cpp deleted file mode 100755 index ed217150e..000000000 --- a/Lts/Common/LtsWidgets/lts_selection_popup.cpp +++ /dev/null @@ -1,212 +0,0 @@ - - - -#include "lts_selection_popup.h" -#include "ui_lts_selection_popup.h" -#include "lts_base.h" - -#include -#include -#include -#include - - -#ifdef NEVER -# include -#endif - - -namespace LtsWidgets -{ - -class RadioButton : public QRadioButton -{ - Q_OBJECT - -public: - explicit RadioButton(int row, const QString &text) - : QRadioButton(text) - , m_row(row) - {} - - int row() const { return m_row; } - -signals: - void doubleClickEvent(); - -private: - int m_row; - - void mouseDoubleClickEvent(QMouseEvent *) override - { - emit doubleClickEvent(); - } -}; - - - -LtsSelectionPopup::LtsSelectionPopup(QAbstractItemModel* model, - int currentIndex, - QWidget *parent) - : LtsFramelessDialog(parent, Behaviour::Popup) - , m_ui(new Ui::LtsSelectionPopup) - , m_currentIndex(currentIndex) - , m_firstIndex(currentIndex) - , m_filter(new QSortFilterProxyModel(this)) - , m_valid(false) -{ - m_ui->setupUi(this); - - m_filter->setSourceModel(model); - m_filter->setFilterCaseSensitivity(Qt::CaseInsensitive); - - slotFilterChanged(); - #ifdef TABLET_MODE - m_ui->leSearch->setVisible(false); - m_ui->label->setVisible(false); - #endif - connect(m_ui->pbCancel, &QPushButton::clicked, this, &LtsSelectionPopup::reject); - connect(m_ui->pbSelect, &QPushButton::clicked, this, &LtsSelectionPopup::accept); - connect(m_ui->leSearch, &QLineEdit::textEdited, - this, &LtsSelectionPopup::slotFilterChanged); - - #ifdef NEVER - - - - - - - - connect(this, static_cast(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){ - qCritical() << " LtsSelectionPopup: called: currentIndexChanged(int), idx=" << idx; - }); - - - connect(this, static_cast(&LtsSelectionPopup::activated), this, [](int idx){ - qCritical() << " LtsSelectionPopup: called: activated(int), idx=" << idx; - }); - - #endif -} - - -LtsSelectionPopup::~LtsSelectionPopup() -{ - delete m_ui; -} - - -void LtsSelectionPopup::slotFilterChanged() -{ - m_filter->setFilterWildcard(m_ui->leSearch->text().count() ? m_ui->leSearch->text() : QStringLiteral("*") ); - refresh(); -} - - -void LtsSelectionPopup::refresh() -{ - m_valid = false; - qDeleteAll(m_ui->itemLayoutWidget->findChildren()); - QLayoutItem *tem = nullptr; - if (m_ui->itemLayoutWidget->layout()->count() > 0) - { - tem = m_ui->itemLayoutWidget->layout()->takeAt(0); - } - - int count = m_filter->rowCount(); - - for (int i = 0; i < count; i++) - { - int realRow = m_filter->mapToSource(m_filter->index(i, 0)).row(); - - RadioButton *rbItem = new RadioButton(realRow, m_filter->data(m_filter->index(i, 0)).toString()); - int disable = m_filter->data(m_filter->index(i, 0), Qt::UserRole-1).toInt(); - if (disable == 1) - rbItem->setEnabled(false); - m_ui->itemLayoutWidget->layout()->addWidget(rbItem); - - if (realRow == m_currentIndex) - { - rbItem->setChecked(true); - m_valid = true; - } - - connect(rbItem, &RadioButton::doubleClickEvent, - this, &LtsSelectionPopup::accept); - connect(rbItem, &RadioButton::toggled, - this, [this](bool checked) - { - if (checked) - m_ui->pbSelect->setEnabled(true); - }); - } - if (tem) - m_ui->itemLayoutWidget->layout()->addItem(tem); - - if (count == 1) - m_valid = true; - - m_ui->pbSelect->setEnabled(m_valid); -} - - -void LtsSelectionPopup::showEvent(QShowEvent *event) -{ - LtsFramelessDialog::showEvent(event); - m_ui->leSearch->setFocus(); - - resize(sizeHint().width() + m_ui->scrollArea->horizontalScrollBar()->height() , sizeHint().height()); -} - - -void LtsSelectionPopup::selectItem(int row, bool bCurrentIndexChanged, bool bActivated) -{ - - - if (bCurrentIndexChanged) - emit currentIndexChanged(row); - - if (bActivated) - emit activated(row); - - m_ui->pbSelect->setEnabled(true); - - LtsFramelessDialog::accept(); -} - - -void LtsSelectionPopup::accept() -{ - const QList items = m_ui->itemLayoutWidget->findChildren(); - - int count = items.count(); - - for (int i = 0; i < count; i++) - { - const RadioButton *rb = items.at(i); - - if (rb->isChecked()) - { - int realRow = m_filter->mapToSource(m_filter->index(i, 0)).row(); - - bool bCurrentIndexChanged = realRow != m_firstIndex; - bool bActivated = true; - selectItem(realRow, bCurrentIndexChanged, bActivated); - return; - } - } - - - if (count == 1) - { - const RadioButton *rb = items.at(0); - selectItem(rb->row(), true, true); - return; - } - -} - -} - -#include "lts_selection_popup.moc" diff --git a/Lts/Common/LtsWidgets/lts_selection_popup.h b/Lts/Common/LtsWidgets/lts_selection_popup.h deleted file mode 100755 index ba2ad3b0d..000000000 --- a/Lts/Common/LtsWidgets/lts_selection_popup.h +++ /dev/null @@ -1,83 +0,0 @@ - - - -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_widgets_export.h" - - -namespace Ui -{ -class LtsSelectionPopup; -} - -class QAbstractItemModel; -class QSortFilterProxyModel; - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsSelectionPopup : public LtsFramelessDialog -{ - Q_OBJECT - -public: - - - LtsSelectionPopup(QAbstractItemModel *model, - int currentIndex, - QWidget *parent = nullptr); - ~LtsSelectionPopup(); - -protected: - - - void showEvent(QShowEvent *event) override; - -private: - Ui::LtsSelectionPopup *m_ui; - int m_currentIndex; - int m_firstIndex; - QSortFilterProxyModel *m_filter; - bool m_valid; - - - - void refresh(); - - - - void selectItem(int row, bool bCurrentIndexChanged, bool bActivated); - -signals: - - - - - - - - - - - - void activated(int index); - - - - void currentIndexChanged(int index); - -public slots: - - - void accept() override; - - - - void slotFilterChanged(); - -}; -} diff --git a/Lts/Common/LtsWidgets/lts_selection_popup.ui b/Lts/Common/LtsWidgets/lts_selection_popup.ui deleted file mode 100755 index 60f6d52aa..000000000 --- a/Lts/Common/LtsWidgets/lts_selection_popup.ui +++ /dev/null @@ -1,232 +0,0 @@ - - - LtsSelectionPopup - - - - 0 - 0 - 193 - 230 - - - - - 0 - 0 - - - - true - - - - - - - 12 - - - - Select an Item - - - Qt::AlignCenter - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 3 - - - - - - - - - - 8 - - - - - - - :/style/show.svg - - - - - - - Search ... - - - - - - - - - true - - - - 0 - 0 - - - - Qt::NoFocus - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - true - - - Qt::AlignCenter - - - - - 0 - 0 - 177 - 102 - - - - - 0 - 0 - - - - - 800 - 800 - - - - Qt::NoFocus - - - - 0 - - - QLayout::SetMinAndMaxSize - - - 0 - - - 0 - - - 100 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 3 - - - - - - - - - Select - - - - - - - Cancel - - - - - - - - - pbSelect - pbCancel - leSearch - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_spinbox.cpp b/Lts/Common/LtsWidgets/lts_spinbox.cpp deleted file mode 100755 index d24161285..000000000 --- a/Lts/Common/LtsWidgets/lts_spinbox.cpp +++ /dev/null @@ -1,173 +0,0 @@ - - - -#include "lts_spinbox.h" - -#include - -namespace LtsWidgets -{ - -LtsSpinBox::LtsSpinBox(QWidget *parent) - : QSpinBox(parent) -{ -} - -QValidator::State LtsSpinBox::validate(QString &input, int &pos) const -{ - QIntValidator validator; - for (auto range : m_validRanges) - { - validator.setBottom(range.first); - validator.setTop(range.second); - QValidator::State state = validator.validate(input, pos); - if (state == QValidator::Invalid || - state == QValidator::Intermediate) - return state; - } - - - int value = input.toInt(); - value -= minimum(); - return (value % singleStep() == 0) ? QValidator::Acceptable : QValidator::Intermediate; -} - -void LtsSpinBox::fixup(QString &str) const -{ - QSpinBox::fixup(str); - - if (singleStep() == 1) - return; - - bool bOk; - int value = str.toInt(&bOk); - if (!bOk) - return; - - int remainder = (value - minimum()) % singleStep(); - - if (2 * remainder > singleStep()) - value += singleStep(); - - value -= remainder; - - if (value > maximum()) - value -= singleStep(); - - str = QString::number(value); -} - -bool LtsSpinBox::setExclusionRanges(const QVector > &exclusionRanges, bool preserveCurrentValues) -{ - bool retVal = true; - - auto newExclusions = exclusionRanges; - - if (preserveCurrentValues) - newExclusions += m_exclusions; - - for (auto &elem : newExclusions) - { - if (elem.first > elem.second) - { - int tmp = elem.first; - elem.first= elem.second; - elem.second = tmp; - } - } - - std::sort(newExclusions.begin(), newExclusions.end(), [](const QPair &a, const QPair &b) { return a.first < b.first; }); - for (int i=0; i= newExclusions[i+1].first) - { - retVal = false; - break; - } - } - - if (!retVal || newExclusions[0].first <= minimum() || newExclusions[newExclusions.size()-1].second >= maximum()) - { - - if (!preserveCurrentValues) - m_exclusions.clear(); - return false; - } - - m_exclusions = exclusionRanges; - - - m_validRanges.clear(); - if (m_exclusions[0].first - 1 > minimum()) - m_validRanges.append({minimum(), m_exclusions[0].first - 1}); - for (int i=0; i singleStep()) - value += singleStep(); - - value -= remainder; - - if (value > maximum()) - value -= singleStep(); - - str = QString::number(value, 'f', decimals()); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_spinbox.h b/Lts/Common/LtsWidgets/lts_spinbox.h deleted file mode 100755 index 3dbd82fbe..000000000 --- a/Lts/Common/LtsWidgets/lts_spinbox.h +++ /dev/null @@ -1,60 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsSpinBox : public QSpinBox -{ - Q_OBJECT - -public: - explicit LtsSpinBox(QWidget *parent = nullptr); - ~LtsSpinBox() = default; - -public slots: - void setValue(int val) { QString valStr = QString::number(val); fixup(valStr); QSpinBox::setValue(valStr.toInt()); } - bool setExclusionRanges(const QVector > &exclusionRanges, bool preserveCurrentValues = false); - bool addExclusionRange(int min, int max); - void clearExclusionRanges(); - -protected: - QValidator::State validate(QString &input, int &pos) const override; - void fixup(QString &str) const override; - -private: - - struct - { - bool operator()(const QPair &a, const QPair &b) const { return a.first < b.first; } - } customless; - - QVector > m_exclusions; - QVector > m_validRanges; - -}; - -class LTSWIDGETS_EXPORT LtsDoubleSpinBox : public QDoubleSpinBox -{ - Q_OBJECT - -public: - explicit LtsDoubleSpinBox(QWidget *parent = nullptr); - ~LtsDoubleSpinBox() = default; - -public slots: - void setValue(double val) { QString valStr = QString::number(val); fixup(valStr); QDoubleSpinBox::setValue(valStr.toDouble()); } - -protected: - QValidator::State validate(QString &input, int &pos) const override; - void fixup(QString &str) const override; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_splash.cpp b/Lts/Common/LtsWidgets/lts_splash.cpp deleted file mode 100755 index 341549ab0..000000000 --- a/Lts/Common/LtsWidgets/lts_splash.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "lts_splash.h" - -#include - -#include -#include -#include - - -namespace LtsWidgets -{ - -Splash::Splash() - : QSplashScreen() - , m_done(false) -{ - static const auto splashFiles = - { - QStringLiteral(":/project/splash.png"), - QStringLiteral(":/project/splash.jpg"), - }; - for (const QString &f : splashFiles) - { - if (!QFile::exists(f)) - continue; - setPixmap(QPixmap(f)); - break; - } -} - - -bool Splash::event(QEvent *e) -{ - if (e->type() == QEvent::MouseButtonPress || - e->type() == QEvent::MouseButtonRelease || - e->type() == QEvent::MouseButtonDblClick) - { - e->ignore(); - return false; - } - - return QSplashScreen::event(e); -} - - -void Splash::setMessage(const QString &message) -{ - showMessage(message, Qt::AlignRight | Qt::AlignBottom); -} - - -void Splash::done() -{ - if (m_done) - return; - m_done = true; - - hide(); - LtsWidgets::LtsGlobalWaitDialog::getInstance()->setVisible(true); -} - - -static Splash *s_splashInstance = nullptr; -void Splash::cleanupSplashInstance() -{ - delete s_splashInstance; - s_splashInstance = nullptr; -} - - -Splash *Splash::getInstance() -{ - if (!s_splashInstance) - { - s_splashInstance = new Splash; - qAddPostRoutine(&Splash::cleanupSplashInstance); - } - return s_splashInstance; -} - -} diff --git a/Lts/Common/LtsWidgets/lts_splash.h b/Lts/Common/LtsWidgets/lts_splash.h deleted file mode 100755 index 34598fe7c..000000000 --- a/Lts/Common/LtsWidgets/lts_splash.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -#include -#include - - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT Splash : public QSplashScreen -{ - Q_OBJECT -public: - static Splash* getInstance(); - - void done(); - void setMessage(const QString &message); - -private: - Splash(); - static void cleanupSplashInstance(); - - bool event(QEvent *e) override; - - bool m_done; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_splitter.cpp b/Lts/Common/LtsWidgets/lts_splitter.cpp deleted file mode 100755 index 46b740cd4..000000000 --- a/Lts/Common/LtsWidgets/lts_splitter.cpp +++ /dev/null @@ -1,57 +0,0 @@ - - - -#include "lts_splitter.h" -#include "lts_splitter_handle.h" - - -namespace LtsWidgets -{ - -LtsSplitter::LtsSplitter(QWidget *parent) - : QSplitter(parent) -{ - connect(this, &LtsSplitter::sizesChanged, this, &LtsSplitter::changeSizes); -} - -LtsSplitter::~LtsSplitter() -{ -} - -QSplitterHandle *LtsSplitter::createHandle() -{ - LtsSplitterHandle *hand = new LtsSplitterHandle(orientation(), this); -#ifdef TABLET_MODE - setHandleWidth(25); -#else - setHandleWidth(10); -#endif - return hand; -} - -void LtsSplitter::collapseChild(const int index) -{ - if (children().size() > index) - { - QWidget *widget = static_cast(children().at(index)); - if (widget) - { - QList s = sizes(); - s[index] = 0; - setSizes(s); - } - } -} - -void LtsSplitter::animate(int handleId) -{ - LtsSplitterHandle *handle = dynamic_cast(this->handle(handleId)); - handle->animate(); -} - -void LtsSplitter::changeSizes(const QList &list) -{ - QSplitter::setSizes(list); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_splitter.h b/Lts/Common/LtsWidgets/lts_splitter.h deleted file mode 100755 index 81f083aec..000000000 --- a/Lts/Common/LtsWidgets/lts_splitter.h +++ /dev/null @@ -1,37 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" - -#include -#include - - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsSplitter : public QSplitter -{ - Q_OBJECT - Q_PROPERTY(QList sizes READ getSizes WRITE setSizes NOTIFY sizesChanged) - -public: - explicit LtsSplitter(QWidget *parent = nullptr); - ~LtsSplitter(); - - QSplitterHandle *createHandle(); - void collapseChild(const int index); - QList getSizes() const { return sizes(); } - void animate(int handleId); - -signals: - void sizesChanged(const QList &newSizes); - -public slots: - void changeSizes(const QList &list); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_splitter_handle.cpp b/Lts/Common/LtsWidgets/lts_splitter_handle.cpp deleted file mode 100755 index 7ddda8f6d..000000000 --- a/Lts/Common/LtsWidgets/lts_splitter_handle.cpp +++ /dev/null @@ -1,87 +0,0 @@ - - - -#include "lts_splitter_handle.h" -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ - -LtsSplitterHandle::LtsSplitterHandle(Qt::Orientation orientation, QSplitter *parent) - : QSplitterHandle(orientation, parent) -{ - auto bgrole = backgroundRole(); - QPalette p = palette(); - m_endColor = p.color(bgrole); - - m_colorAnimation = new QPropertyAnimation(this, "splittercolor"); - m_colorAnimation->setDuration(1000); - m_colorAnimation->setStartValue(m_currentColor); - m_colorAnimation->setEndValue(m_endColor); - m_colorAnimation->setLoopCount(2); - connect(m_colorAnimation, &QPropertyAnimation::finished, this, [this]{ - m_animate = false; - m_currentColor = m_endColor; - repaint(); - }); - -} - -LtsSplitterHandle::~LtsSplitterHandle() -{ -} - -void LtsSplitterHandle::paintEvent(QPaintEvent *event) -{ - if(!m_animate) - { - QSplitterHandle::paintEvent(event); - return; - } - QPainter painter(this); - QLinearGradient gradient; - gradient.setColorAt(0, m_currentColor); - - - if(orientation() == Qt::Horizontal) - { - gradient.setStart(rect().left(), rect().height()/2); - gradient.setFinalStop(rect().height(), rect().height()/2); - }else - { - gradient.setStart(rect().width()/2, rect().top()); - gradient.setFinalStop(rect().width()/2, rect().bottom()/2); - } - painter.fillRect(event->rect(), QBrush(gradient)); -} - -void LtsSplitterHandle::animate() -{ - if (m_colorAnimation->state() == QPropertyAnimation::Running) - return; - - m_currentColor = QColor(239,87,50); - m_animate = true; - m_colorAnimation->start(); -} - -QColor LtsSplitterHandle::splitterColor() const -{ - return m_currentColor; -} - -void LtsSplitterHandle::setCurrentColor(const QColor ¤tcolor) -{ - if (currentcolor == m_currentColor) - return; - m_currentColor = currentcolor; - repaint(); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_splitter_handle.h b/Lts/Common/LtsWidgets/lts_splitter_handle.h deleted file mode 100755 index d7769bbe5..000000000 --- a/Lts/Common/LtsWidgets/lts_splitter_handle.h +++ /dev/null @@ -1,39 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" - -#include -#include -#include - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsSplitterHandle : public QSplitterHandle -{ - Q_OBJECT - Q_PROPERTY(QColor splittercolor READ splitterColor WRITE setCurrentColor DESIGNABLE false) - -public: - explicit LtsSplitterHandle(Qt::Orientation orientation, QSplitter *parent = nullptr); - ~LtsSplitterHandle(); - - void paintEvent(QPaintEvent *event) override; - void animate(); - - QColor splitterColor() const; - void setCurrentColor(const QColor ¤tcolor); - -private: - bool m_animate {false}; - QColor m_endColor {Qt::black}; - QColor m_currentColor{QColor(239,87,50)}; - QPropertyAnimation *m_colorAnimation; - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_tab_bar.cpp b/Lts/Common/LtsWidgets/lts_tab_bar.cpp deleted file mode 100755 index 19663a74e..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_bar.cpp +++ /dev/null @@ -1,120 +0,0 @@ - - - -#include "lts_tab_bar.h" - -#include "lts_dialog.h" -#include "lts_dialog_helper.h" -#include "lts_events.h" -#include "lts_tab_widget.h" - -#include -#include -#include - -namespace LtsWidgets -{ - -LtsTabBar::LtsTabBar(bool isSecondary, QWidget *parent) - : QTabBar(parent) - , m_secondary(isSecondary) -{ - setDocumentMode(true); - setTabsClosable(true); - - connect(this, &LtsTabBar::currentChanged, this, &LtsTabBar::updateCloseButtonColors); - - if (m_secondary) - { - connect(this, &LtsTabBar::customContextMenuRequested, - this, &LtsTabBar::onCustomContextMenuRequested); - setContextMenuPolicy(Qt::CustomContextMenu); - } -} - - -void LtsTabBar::_internal_closeTab() -{ - QObject *object = sender(); - int tabToClose = -1; - - for (int i = 0; i < count(); i++) - { - if (tabButton(i, QTabBar::RightSide) == object) - { - tabToClose = i; - break; - } - } - - if (tabToClose != -1) - emit tabCloseRequested(tabToClose); -} - - -void LtsTabBar::updateCloseButtonColors(int newIndex) -{ - - if (newIndex == -1) - newIndex = currentIndex(); - - for (int i = 0; i < count(); i++) - { - QWidget *w = tabButton(i, QTabBar::RightSide); - CloseButton *cb = qobject_cast(w); - - if (!cb) - continue; - - QColor text; - - bool enabled = isTabEnabled(i); - bool selected = (i == newIndex); - bool secondary = m_secondary; - - if (enabled) - { - if (selected) - text = (secondary) ? Styles::TabBarTabStyle::secondarySelected : Styles::TabBarTabStyle::primarySelected; - else - text = (secondary) ? Styles::TabBarTabStyle::secondaryNormal : Styles::TabBarTabStyle::primaryNormal; - } - else - text = (secondary) ? Styles::TabBarTabStyle::secondaryDisabled : Styles::TabBarTabStyle::primaryDisabled; - - - cb->setFontColor(text); - } -} - -void LtsTabBar::onCustomContextMenuRequested(const QPoint pos) -{ - if (!m_secondary) - return; - QMenu m; - QAction *a = new QAction(tr("Save as image")); - connect(a, &QAction::triggered, this, [this, pos]() { LtsTabBar::onSaveCurrentWidgetAsImage(pos); }); - m.addAction(a); - m.exec(mapToGlobal(pos)); -} - -void LtsTabBar::onSaveCurrentWidgetAsImage(const QPoint pos) -{ - int curTab = tabAt(pos); - if (curTab < 0) - curTab = currentIndex(); - if (curTab < 0) - return; - LtsTabWidget *tw = qobject_cast(parentWidget()); - QWidget *w = tw ? tw->widget(curTab) : nullptr; - if (!w) - return; - LtsDialogHelper *helper = qobject_cast(w); - if (helper) - w = helper->getDialog(); - SaveImageEvent *ev = new SaveImageEvent(w, w->grab()); - ev->setAccepted(false); - QCoreApplication::postEvent(qApp, ev); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_tab_bar.h b/Lts/Common/LtsWidgets/lts_tab_bar.h deleted file mode 100755 index f4fc20037..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_bar.h +++ /dev/null @@ -1,39 +0,0 @@ - - - -#pragma once - -#include -#include "lts_widgets_export.h" - -namespace LtsWidgets -{ - - - -class LTSWIDGETS_EXPORT LtsTabBar : public QTabBar -{ - Q_OBJECT - Q_PROPERTY(bool secondary MEMBER m_secondary) - -public: - - - explicit LtsTabBar(bool isSecondary = false, QWidget *parent = nullptr); - - - - void _internal_closeTab(); - -protected: - void onCustomContextMenuRequested(const QPoint pos); - void onSaveCurrentWidgetAsImage(const QPoint pos); -private: - bool m_secondary; - -public slots: - void updateCloseButtonColors(int newIndex = -1); - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp b/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp deleted file mode 100755 index 06705f39c..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp +++ /dev/null @@ -1,59 +0,0 @@ - - -#include "lts_tab_sequence_widget.h" - -#include -#include -#include -#include - -namespace LtsWidgets -{ -LtsTabSequenceWidget::LtsTabSequenceWidget(QWidget *parent) - : LtsAbstractSequenceWidget(parent) - , m_twSequences(new QTabWidget(this)) - -{ - QHBoxLayout *gridLayout = new QHBoxLayout(this); - gridLayout->addWidget(m_twSequences); - gridLayout->addWidget(pbAddMember(), Qt::AlignTop); - - m_twSequences->setCornerWidget(pbAddMember()); - pbAddMember()->setFixedSize(m_twSequences->tabBar()->size()); - pbAddMember()->setIconSize(m_twSequences->tabBar()->size()); -} - -LtsTabSequenceWidget::~LtsTabSequenceWidget() -{ - delete pbAddMember(); - delete m_twSequences; -} - -void LtsTabSequenceWidget::setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName, bool isButtonVisible) -{ - int index = m_twSequences->addTab(child, objectName); - if(isButtonVisible) - m_twSequences->tabBar()->setTabButton(index, QTabBar::RightSide, pbChild); -} - -QWidget* LtsTabSequenceWidget::getWidget(int index) -{ - return m_twSequences->widget(index); -} - -QWidget* LtsTabSequenceWidget::getCurrentWidget() -{ - return m_twSequences->currentWidget(); -} - -int LtsTabSequenceWidget::getCurrentTabIndex() -{ - return m_twSequences->currentIndex(); -} -void LtsTabSequenceWidget::changeTabName(const QString &name) -{ - m_twSequences->setTabText(m_twSequences->currentIndex(), name); -} - - -} diff --git a/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h b/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h deleted file mode 100755 index 706e744ff..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h +++ /dev/null @@ -1,37 +0,0 @@ - - -#ifndef LTS_TAB_SEQUENCE_WIDGET_H -#define LTS_TAB_SEQUENCE_WIDGET_H - -#pragma once - -#include "lts_abstract_sequence_widget.h" -#include "lts_widgets_export.h" - -class QTabWidget; - -namespace LtsWidgets -{ - - class LTSWIDGETS_EXPORT LtsTabSequenceWidget : public LtsAbstractSequenceWidget - { - Q_OBJECT - - public: - LtsTabSequenceWidget(QWidget *parent = nullptr); - - virtual ~LtsTabSequenceWidget(); - QWidget* getWidget(int index); - QWidget* getCurrentWidget(); - int getCurrentTabIndex(); - void changeTabName(const QString &name); - - protected: - void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName, bool isButtonVisible = true) override; - - private: - QTabWidget *m_twSequences = nullptr; - }; -} - -#endif diff --git a/Lts/Common/LtsWidgets/lts_tab_widget.cpp b/Lts/Common/LtsWidgets/lts_tab_widget.cpp deleted file mode 100755 index a866a1310..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_widget.cpp +++ /dev/null @@ -1,894 +0,0 @@ - - - -#include "lts_tab_widget.h" -#include "lts_tab_bar.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsWidgets -{ -LtsTabWidget::LtsTabWidget(QWidget *parent, bool isSecondary) - : QTabWidget(parent) - , m_tabbar(nullptr) - , m_hasSubTabs(false) - , m_isSecondary(isSecondary) - , m_mainSplitterState(QByteArray()) -{ - setDocumentMode(true); - setMinimumSize(200, 100); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); - - LtsTabBar *tabBar = new LtsTabBar(isSecondary, this); - setTabBar(tabBar); - - QFont currentFont = tabBar->font(); - - QFont::Capitalization c = QFont::SmallCaps; - bool bold = true; - - if (m_isSecondary) - { - c = QFont::MixedCase; - bold = false; - } - - - currentFont.setCapitalization(c); - currentFont.setBold(bold); - - tabBar->setFont(currentFont); - - connect(this, &LtsTabWidget::currentChanged, - this, &LtsTabWidget::slotCurrentChanged); - - - QSplitter *splitter = static_cast(parent); - if (splitter && - splitter->objectName() == QStringLiteral("mainSplitter") && - splitter->widget(1) == this && - !isSecondary) - { - int tabBarHeight = m_tabbar->height() * 1.33; - int tabBarWidth = 20; - - setStyleSheet(styleSheet() + QStringLiteral("QTabWidget::tab-bar {" - "left: %1px; " - "} ").arg(tabBarWidth)); - QPushButton *maxBtn = new QPushButton(this); - maxBtn->setObjectName(QStringLiteral("MinMaxTabButton")); - maxBtn->setText(QStringLiteral("▲\n▼")); - maxBtn->setFixedSize(tabBarWidth, tabBarHeight * 2); - maxBtn->setStyleSheet(QStringLiteral("QPushButton { " - "border-radius: 0px; " - "border: 1px solid gray; " - "} ")); - connect(maxBtn, &QPushButton::clicked, this, &LtsTabWidget::slotMaximizeButtonClicked); - } -} - - -LtsTabWidget::~LtsTabWidget() -{ - - - clearAll(); -} - - -void LtsTabWidget::slotCurrentChanged(int newIdx) -{ - if (newIdx < 0) - return; - - QWidget *w = widget(newIdx); - - if (w == nullptr) - return; - - LtsDialogHelper *dialog1 = qobject_cast(w); - - if (dialog1 != nullptr) - { - LtsDialog *mainDialog = dialog1->getDialog(); - - if (mainDialog != nullptr) - emit mainDialog->signalFocused(); - - return; - } - - - LtsTabWidget *tab = qobject_cast(w); - - if (tab != nullptr) - { - LtsDialogHelper *dialog2 = qobject_cast(tab->widget(tab->currentIndex())); - - if (dialog2 != nullptr) - { - LtsDialog *mainDialog = dialog2->getDialog(); - - if (mainDialog != nullptr) - emit mainDialog->signalFocused(); - - return; - } - } -} - -void LtsTabWidget::slotMaximizeButtonClicked() -{ - LtsSplitter *splitter = static_cast(parent()); - if (splitter) - { - if (splitter->widget(0)->height() != splitter->widget(0)->minimumHeight()) - { - m_mainSplitterState = splitter->saveState(); - splitter->collapseChild(0); - } - else - { - splitter->restoreState(m_mainSplitterState); - } - } -} - - -void LtsTabWidget::closeRequestTabWidget(LtsTabWidget *tabWidget) -{ - for (int i = 0; i < tabWidget->count(); i++) - { - QWidget *w = tabWidget->widget(i); - - LtsDialogHelper *helper = qobject_cast(w); - - if (helper) - { - LtsDialog *dlg = qobject_cast(helper->getDialog()); - - if (dlg) - dlg->closeRequest(); - } - - LtsTabWidget *tab = qobject_cast(w); - if (tab) - closeRequestTabWidget(tab); - } -} - - -void LtsTabWidget::slotTabCloseRequested(int index) -{ - QWidget *w = widget(index); - - if (w == nullptr) - return; - - LtsDialogHelper *helper = qobject_cast(w); - - if (helper != nullptr) - { - LtsDialog *dialog = helper->getDialog(); - - if (dialog != nullptr) - { - if (dialog->isClosable()) - { - setCurrentIndex(index); - dialog->closeRequest(); - return; - } - - return; - } - } - - LtsTabWidget *tab = qobject_cast(w); - - if (tab != nullptr) - { - if (LtsTabWidget::hasUnsavedChanges(tab)) - { - LtsWidgets::Buttons buttons; - buttons.setFlag(Button::Yes, true); - buttons.setFlag(Button::Cancel, true); - QHash buttonTexts; - buttonTexts.insert(Button::Yes, tr("Discard All")); - const auto ret = LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.
Please save or discard their changes before closing."), tr("Unsaved changes"), Behaviour::Modal, buttons, buttonTexts); - switch (ret) - { - case Button::Yes: - tab->deleteLater(); - return; - default: - return; - } - } - else - { - - closeRequestTabWidget(tab); - } - } - w->deleteLater(); - - -} - - -void LtsTabWidget::onCheckTabBarVisibility() -{ - - - if (!m_isSecondary) - { - - if (count() < 1) - setHidden(true); - } - else - { - if (count() < 1) - deleteLater(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - -} - - -void LtsTabWidget::setSubTabsVisible(bool visible) -{ - if (count()) - { - - return; - } - - m_hasSubTabs = visible; -} - - -void LtsTabWidget::addTab(const QString &menuGroup, const QString &identifier, QWidget *widget, - bool setClosable, bool focusNewTab) -{ - - setFocus(); - LtsTabWidget *tab = this; - QString fullName; - - - - - - if (m_hasSubTabs) - { - fullName = identifier; - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); - - if (!tab) - { - - tab = new LtsTabWidget(nullptr, true); - tab->setMovable(true); - addWidget(menuGroup, tab, true , focusNewTab); - } - else - { - - tab->setHidden(false); - } - - if (focusNewTab) - setCurrentIndex(indexOf(tab)); - } - else - { - fullName = menuGroup + QLatin1Char('.') + identifier; - - } - - setHidden(false); - - tab->addWidget(fullName, widget, setClosable, focusNewTab); - - - - if (m_hasSubTabs) - { - LtsTabBar *tabbar = tab->tabBar(); - - for (int i = 0; i < tabbar->count(); i++) - { - - if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden()) - { - m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->hide(); - break; - } - } - } -} - - -void LtsTabWidget::addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab) -{ - QString theName = fullName; - - if (fullName.isEmpty()) - { - - const QString possibleCharacters(QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); - const int randomStringLength = 24; - - QString randomString; - for (int i = 0; i < randomStringLength; i++) - { - int index = qrand() % possibleCharacters.length(); - QChar nextChar = possibleCharacters.at(index); - randomString.append(nextChar); - } - - theName = randomString; - } - - widget->setObjectName(theName); - - LtsDialogHelper *helper = qobject_cast(widget); - if (helper) - helper->retranslate(); - - QString title = widget->windowTitle(); - - if (title.isEmpty()) - { - if (helper) - title = QStringLiteral("retranslate() fehlt!"); - else - title = widget->objectName(); - } - - if (helper) - { - connect(helper->getDialog(), &LtsDialog::signalHide, this, static_cast(&LtsTabWidget::onRemoveTab), - static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); - - } - - connect(widget, &QWidget::destroyed, this, static_cast(&LtsTabWidget::onRemoveTab), Qt::DirectConnection); - connect(widget, &QWidget::windowTitleChanged, this, &LtsTabWidget::slotWindowTitleChanged); - - if (helper) - { - m_tabMap.insert(theName, helper); - - } - else - { - m_tabMap.insert(theName, widget); - - } - - int newTabIndex = QTabWidget::addTab(widget, title); - -#ifdef LTS_DEBUG - setTabToolTip(newTabIndex, tr("DEBUG: WindowName: %1").arg(theName)); -#endif - - if (focusNewTab) - setCurrentIndex(newTabIndex); - - LtsTabBar *tabbar = qobject_cast(tabBar()); - - if (tabbar) - { - - CloseButton *closeBtn = new CloseButton(); - connect(closeBtn, &QPushButton::clicked, tabbar, &LtsTabBar::_internal_closeTab); - tabbar->setTabButton(newTabIndex, QTabBar::RightSide, closeBtn); - } - - if (!setClosable) - m_tabbar->tabButton(newTabIndex, QTabBar::RightSide)->hide(); - - if (tabbar) - tabbar->updateCloseButtonColors(); - -} - - -void LtsTabWidget::slotWindowTitleChanged(const QString &title) -{ - QWidget *w = qobject_cast(sender()); - - if (!w) - { - qWarning() << "LtsTabWidget: slotWindowTitleChanged failed! (not a QWidget) " << sender(); - return; - } - - int index = indexOf(w); - - if (index < 0) - { - qWarning() << "LtsTabWidget: slotWindowTitleChanged failed! (widget not found on TabBar)"; - return; - } - - - QString realTitle = title; - QString readOnlyHint; - QStringList sl = title.split(QStringLiteral("- READ-ONLY")); - if (sl.size() == 2) - { - realTitle = sl.first().trimmed(); - readOnlyHint = sl.back().trimmed(); - - - if (readOnlyHint.startsWith(QStringLiteral("("))) - readOnlyHint.remove(0,1); - if (readOnlyHint.endsWith(QStringLiteral(")"))) - readOnlyHint.remove(readOnlyHint.size()-1,1); - - readOnlyHint = tr("Locked by: %1").arg(readOnlyHint); - } - - setTabText(index, realTitle); - - QTabBar *tabBar = this->tabBar(); - - if (!readOnlyHint.isEmpty()) - { - QLabel *readOnly = new QLabel(QStringLiteral("READ-ONLY"), this); - readOnly->setToolTip(readOnlyHint); - readOnly->setPixmap(QIcon(QStringLiteral("icon:warning.svg")).pixmap(QSize(25, 25))); - - tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, readOnly); - } else { - QWidget *readOnly = tabBar->tabButton(index, QTabBar::ButtonPosition::LeftSide); - if (readOnly) - { - - - - - tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, nullptr); - readOnly->setParent(this); - delete readOnly; - } - } -} - - -QWidget *LtsTabWidget::getWidget(const QString &menuGroup, const QString &identifier) -{ - LtsTabWidget *tab = this; - QString fullName; - - if (m_hasSubTabs) - { - fullName = identifier; - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); - - if (tab == nullptr) - return nullptr; - } - else - { - fullName = menuGroup + QLatin1Char('.') + identifier; - } - - return tab->getWidget(fullName); -} - - -QWidget *LtsTabWidget::getWidget(const QString &fullName) -{ - return m_tabMap.value(fullName, nullptr); -} - - -bool LtsTabWidget::focusTab(const QString &menuGroup, const QString &identifier) -{ - QPointer tab = this; - QString fullName; - - if (m_hasSubTabs) - { - fullName = identifier; - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); - - if (!tab) - { - - return false; - } - setCurrentIndex(indexOf(tab)); - } - else - fullName = menuGroup + QLatin1Char('.') + identifier; - - if (!tab) - return false; - - - LtsSplitter *splitter = static_cast(parent()); - - - - - - QTimer::singleShot(500, this, [splitter, tab] (){ - - - if (splitter && tab && (tab->count() >= 1) && splitter->sizes().count() && !(splitter->sizes().at(1))) - { - splitter->animate(1); - } - }); - return tab->focusWidget(fullName); -} - - -bool LtsTabWidget::focusWidget(const QString &fullName) -{ - QWidget *widget = m_tabMap.value(fullName, nullptr); - - if (widget == nullptr) - return false; - - setCurrentIndex(indexOf(widget)); - widget->setFocus(); - return true; -} - - -bool LtsTabWidget::removeWidget(QWidget * widget) -{ - if (nullptr == widget) - return false; - - for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i) - { - QWidget *w = i.value(); - - if (w == nullptr) - continue; - - if (w == widget) - { - m_tabMap.erase(i); - disconnect(widget, &QWidget::destroyed, this, static_cast(&LtsTabWidget::onRemoveTab)); - return true; - } - - if (m_hasSubTabs) - { - LtsTabWidget *tab = qobject_cast(w); - if (tab != nullptr && tab->removeWidget(widget)) - { - return true; - } - } - } - - return false; -} - - -void LtsTabWidget::onRemoveTab(QObject *object) -{ - if (object == nullptr) - { - qWarning() << "onRemoveTab: obj == nullptr"; - return; - } - - QWidget *w = static_cast(object); - - bool rmd = false; - for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i) - { - if (i.value() == w) - { - m_tabMap.erase(i); - rmd = true; - break; - } - } - - if (!rmd) - { - qWarning() << "LtsTabWidget::onRemoveTab: widget to remove was not on tab map!"; - } - - int index = indexOf(w); - if (index < 0) - { - - qWarning() << "LtsTabWidget::onRemoveTab: cannot remove widget, as it was not found on the tab widget" << (void *)w; - } - else - { - removeTab(index); - } - - onCheckTabBarVisibility(); -} - - -void LtsTabWidget::setActuatorTabStyle(bool enable) -{ - if (enable) - setTabBar(new LtsTabBar(false, this)); -} - - -LtsTabBar *LtsTabWidget::tabBar() -{ - return m_tabbar; -} - - -void LtsTabWidget::setTabBar(LtsTabBar *tabbar) -{ - if (m_tabbar) - m_tabbar->deleteLater(); - - - QFont currentFont = font(); - currentFont.setCapitalization(QFont::SmallCaps); - currentFont.setBold(true); - - tabbar->setFont(currentFont); - connect(tabbar, &LtsTabBar::tabCloseRequested, this, &LtsTabWidget::slotTabCloseRequested); - - m_tabbar = tabbar; - QTabWidget::setTabBar(m_tabbar); -} - - -void LtsTabWidget::clearAll() -{ - - - for (int i = 0; i < count(); i++) - { - QWidget *w = widget(i); - - if (w == nullptr) - continue; - - disconnect(w, &QWidget::destroyed, this, static_cast(&LtsTabWidget::onRemoveTab)); - - w->deleteLater(); - } - - clear(); -} - - -void LtsTabWidget::aboutToClose() -{ - for (int i = 0; i < count(); i++) - { - if (m_hasSubTabs) - { - LtsTabWidget *tab = qobject_cast(widget(i)); - tab->aboutToClose(); - } - else - { - LtsDialogHelper *helper = qobject_cast(widget(i)); - - if (helper == nullptr) - continue; - - LtsDialog *dialog = qobject_cast(helper->getDialog()); - - if (dialog == nullptr) - continue; - - dialog->aboutToClose(); - } - } -} - - -bool LtsTabWidget::hasUnsavedChanges(LtsTabWidget *tabWidget) -{ - for (int i = 0; i < tabWidget->count(); i++) - { - QWidget *w = tabWidget->widget(i); - - LtsDialogHelper *helper = qobject_cast(w); - - if (helper) - { - LtsDialog *dlg = qobject_cast(helper->getDialog()); - - if (dlg != nullptr) - { - - - if (dlg->hasUnsavedChanges()) - { - int dlgIndex = tabWidget->indexOf(helper); - if (dlgIndex != -1) - tabWidget->setCurrentIndex(dlgIndex); - - - return true; - } - } - } - - LtsTabWidget *tab = qobject_cast(w); - - if (tab) - { - - - bool _hasUnsavedChanges = hasUnsavedChanges(tab); - if (_hasUnsavedChanges) - { - int tabIndex = tabWidget->indexOf(tab); - if (tabIndex != -1) - tabWidget->setCurrentIndex(tabIndex); - - return true; - } - } - - - - } - - return false; -} - - - - - - -void CloseButton::paintEvent(QPaintEvent *) -{ - QPainter p(this); - - p.save(); - p.setPen(QPen(QColor(m_fontColor), 1.5)); - p.setRenderHint(QPainter::Antialiasing); - - - p.drawLine(line()); - - - p.setPen(QPen(QColor(m_fontColor), 2.5)); - - QRectF c = cross(); - - p.drawLine(c.topLeft(), c.bottomRight()); - p.drawLine(c.bottomLeft(), c.topRight()); - - p.restore(); -} - - -QLineF CloseButton::line() const -{ - qreal forceBlur = 0.001; - qreal lineStart = height() * (0.25 / 2) + forceBlur; - qreal lineSize = height() - lineStart; - - return QLineF(QPointF(lineStart, lineStart), QPointF(lineStart, lineSize)); -} - - -QRectF CloseButton::cross() const -{ - QLineF l = line(); - - qreal lineLeft = l.x1(); - qreal forceBlur = 0.001; - - qreal crossStartX = lineLeft * 3; - qreal crossStartY = lineLeft * 2; - qreal crossEndX = width(); crossEndX -= crossEndX * 0.15 + forceBlur; - qreal crossEndY = height() - crossStartY; - - return QRectF(QPointF(crossStartX, crossStartY), QPointF(crossEndX, crossEndY)); -} - - -QSize CloseButton::sizeHint() const -{ - QFontMetrics fm (QApplication::font()); - int fontHeight = fm.height(); - - int fontHeightQuarter = fontHeight * 0.25; - int finalSize = fontHeight + fontHeightQuarter; -#ifdef TABLET_MODE - finalSize = finalSize*1.5; -#endif - return QSize(finalSize, finalSize); -} - - -void CloseButton::mousePressEvent(QMouseEvent *e) -{ - e->ignore(); - m_isPressed = true; -} - - -void CloseButton::mouseReleaseEvent(QMouseEvent *e) -{ - bool isPressed = m_isPressed; - m_isPressed = false; - - if (!isPressed) - { - e->ignore(); - return; - } - - QRectF c = cross(); - QRect adjusted = QRect(QPoint(floor(c.x()), floor(c.y())), QPoint(ceil(c.right()), ceil(c.bottom() +1 ))); - - if (!adjusted.contains(e->localPos().toPoint())) - { - - e->ignore(); - return; - } - - - e->accept(); - emit clicked(); -} -} diff --git a/Lts/Common/LtsWidgets/lts_tab_widget.h b/Lts/Common/LtsWidgets/lts_tab_widget.h deleted file mode 100755 index e7fbeac27..000000000 --- a/Lts/Common/LtsWidgets/lts_tab_widget.h +++ /dev/null @@ -1,163 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" - -#include -#include -#include - - -namespace LtsWidgets -{ -class LtsDialogHelper; -class LtsTabBar; - - - -class LTSWIDGETS_EXPORT LtsTabWidget : public QTabWidget -{ - Q_OBJECT -public: - - - explicit LtsTabWidget(QWidget *parent = nullptr, bool isSecondary = false); - ~LtsTabWidget(); - - - - void setActuatorTabStyle(bool enable); - - - - void clearAll(); - - - void aboutToClose(); - - - - LtsTabBar *tabBar(); - - - - void setTabBar(LtsTabBar *tabbar); - - - - bool focusTab(const QString &menuGroup, const QString &identifier); - - - - void setSubTabsVisible(bool visible); - - - - QWidget *getWidget(const QString &menuGroup, const QString &identifier); - - - - static bool hasUnsavedChanges(LtsTabWidget *tabWidget); - - - -signals: - - - void signalCheckTabBarVisibility(); - - -public slots: - - - void addTab(const QString &menuGroup, const QString &identifier, QWidget *widget, - bool setClosable = true, bool focusNewTab = true); - - - - bool removeWidget(QWidget * widget); - -private slots: - - - - - - - - - - - void onRemoveTab(QObject *object); - - - - void onCheckTabBarVisibility(); - - - - void slotTabCloseRequested(int index); - - void slotCurrentChanged(int newIdx); - - void slotMaximizeButtonClicked(); - -private: - - - void addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab = true); - - - - QWidget *getWidget(const QString &fullName); - - - - bool focusWidget(const QString &fullName); - - - - void slotWindowTitleChanged(const QString &title); - - void closeRequestTabWidget(LtsTabWidget *tabWidget); - - QPoint startPos; - LtsTabBar *m_tabbar = nullptr; - QPalette m_palette; - - - QMap m_tabMap; - - - bool m_hasSubTabs; - bool m_isSecondary; - - QByteArray m_mainSplitterState; -}; - - -class CloseButton : public QAbstractButton -{ - Q_OBJECT - -public: - using QAbstractButton::QAbstractButton; - - void setFontColor(const QColor &fontColor) { m_fontColor = fontColor; } - QRectF cross() const; - QLineF line() const; - -protected: - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void paintEvent(QPaintEvent *e) override; - QSize sizeHint() const override; - -private: - QColor m_fontColor; - bool m_isPressed = false; -}; -} diff --git a/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp b/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp deleted file mode 100755 index 772f99f16..000000000 --- a/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp +++ /dev/null @@ -1,137 +0,0 @@ - - - -#include "lts_text_edit_dialog.h" -#include "lts_text_editor.h" - -#include - -#if 1 -#define USE_TEXT_EDITOR_2 -#else -#define USE_TEXT_EDITOR -#endif - -namespace LtsWidgets -{ - -LtsTextEditDialog::LtsTextEditDialog(QWidget *parent) - : LtsDialog(parent, ButtonCombination::OkApplyCancel) - #ifdef USE_TEXT_EDITOR - , m_textEditor(new LtsTextEditor(this)) - #else - , m_textEditor2(new LtsTextEditor2(this)) - #endif -{ - QHBoxLayout *hbox = new QHBoxLayout(this); -#ifdef USE_TEXT_EDITOR - hbox->addWidget(m_textEditor); - connect(m_textEditor->textEdit(), &QTextEdit::textChanged, this, [this]() { - emit signalTextChanged(this->getHtmlText()); - }); - connect(m_textEditor, &LtsTextEditor::backgroundColorChanged, - this, &LtsTextEditDialog::onBackgroundColorChanged); -#else - hbox->addWidget(m_textEditor2); - connect(m_textEditor2->textEdit(), &QTextEdit::textChanged, this, [this]() { - emit signalTextChanged(this->getHtmlText()); - }); - connect(m_textEditor2, &LtsTextEditor2::backgroundColorChanged, - this, &LtsTextEditDialog::onBackgroundColorChanged); -#endif - - - enableBackgroundColor(false); -} - -LtsTextEditDialog::~LtsTextEditDialog() -{} - -void LtsTextEditDialog::enableBackgroundColor(bool onOff) -{ -#ifdef USE_TEXT_EDITOR - m_textEditor->enableBackgroundColor(onOff); -#else - m_textEditor2->enableBackgroundColor(onOff); -#endif -} - - - - -void LtsTextEditDialog::retranslate() -{ - - QString name = tr("Edit Text"); - translate(name); -} - -bool LtsTextEditDialog::apply() -{ -#ifdef USE_TEXT_EDITOR - QString textHtml = m_textEditor->textEdit()->document()->toHtml(); - const QString backGroundColor = QLatin1String("background-color:'%1'; ").arg(m_textEditor->backgroundColor().name()); - -#else - QString textHtml = m_textEditor2->textEdit()->document()->toHtml(); - const QString backGroundColor = QLatin1String("background-color:'%1'; ").arg(m_textEditor2->backgroundColor().name()); -#endif - - emit signalTextApplied(textHtml); - - const int insertBackGroundColorIndex = textHtml.indexOf(QLatin1String("font-family")); - textHtml.insert(insertBackGroundColorIndex, backGroundColor); - - emit signalTextAppliedWithBackGroundColor(textHtml); - - return true; -} - -void LtsTextEditDialog::setHtml(const QString &htmlText) -{ - - -#ifdef USE_TEXT_EDITOR - m_textEditor->textEdit()->document()->setHtml(htmlText); -#else - m_textEditor2->textEdit()->document()->setHtml(htmlText); -#endif -} - -QString LtsTextEditDialog::getHtmlText() const -{ -#ifdef USE_TEXT_EDITOR - if(m_textEditor && m_textEditor->textEdit() && m_textEditor->textEdit()->document()) - return m_textEditor->textEdit()->document()->toHtml(); -#else - if(m_textEditor2 && m_textEditor2->textEdit() && m_textEditor2->textEdit()->document()) - return m_textEditor2->textEdit()->document()->toHtml(); -#endif - return {}; -} - -void LtsTextEditDialog::setInitialBackgroundColor(const QColor &bgColor) -{ -#ifdef USE_TEXT_EDITOR - if (m_textEditor) - m_textEditor->setInitialBackgroundColor(bgColor); -#else - if (m_textEditor2) - m_textEditor2->setInitialBackgroundColor(bgColor); -#endif -} - -void LtsTextEditDialog::dialogCanceled() -{ - emit signalDialogCanceled(); - BaseClass::dialogCanceled(); -} - -void LtsTextEditDialog::onBackgroundColorChanged(const QColor &backgroundColor) -{ - slotChangesMade(); - emit signalBackgroundColorChanged(backgroundColor); -} - -} - diff --git a/Lts/Common/LtsWidgets/lts_text_edit_dialog.h b/Lts/Common/LtsWidgets/lts_text_edit_dialog.h deleted file mode 100755 index bcec6afc0..000000000 --- a/Lts/Common/LtsWidgets/lts_text_edit_dialog.h +++ /dev/null @@ -1,55 +0,0 @@ - - -#pragma once - -#include -#include -#include - - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsTextEditDialog : public LtsWidgets::LtsDialog -{ - Q_OBJECT - - using BaseClass = LtsWidgets::LtsDialog; - -public: - LtsTextEditDialog(QWidget *parent = nullptr); - ~LtsTextEditDialog(); - - void enableBackgroundColor(bool onOff); - void setInitialBackgroundColor(const QColor &bgColor); - - void retranslate() override; - bool apply() override; - void setHtml(const QString &htmlText); - QString getHtmlText() const; - -protected: - void dialogCanceled() override; - -private: - LtsTextEditor *m_textEditor{nullptr}; - LtsTextEditor2 *m_textEditor2{nullptr}; - - -protected Q_SLOTS: - void onBackgroundColorChanged(const QColor &backgroundColor); - -Q_SIGNALS: - void signalTextChanged(const QString &htmlText); - void signalTextApplied(const QString &htmlText); - void signalTextAppliedWithBackGroundColor(const QString &htmlText); - void signalBackgroundColorChanged(const QColor &bgColor); - void signalDialogApplied(); - void signalDialogCanceled(); - -}; - -} - - - diff --git a/Lts/Common/LtsWidgets/lts_text_editor.cpp b/Lts/Common/LtsWidgets/lts_text_editor.cpp deleted file mode 100755 index 54fb681ef..000000000 --- a/Lts/Common/LtsWidgets/lts_text_editor.cpp +++ /dev/null @@ -1,391 +0,0 @@ -#include "lts_text_editor.h" - -#include "ui_lts_text_editor.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace LtsWidgets -{ - -LtsTextEditor::LtsTextEditor(QWidget *parent) - : QWidget(parent) - , m_ui(new Ui::LtsTextEditor) -{ - m_ui->setupUi(this); - - connect(m_ui->textEdit, &QTextEdit::currentCharFormatChanged, - this, &LtsTextEditor::slotCurrentCharFormatChanged); - connect(m_ui->textEdit, &QTextEdit::cursorPositionChanged, - this, &LtsTextEditor::slotAlignmentChanged); - - connect(m_ui->textEdit->document(), &QTextDocument::undoAvailable, - m_ui->pbUndo, &QToolButton::setEnabled); - connect(m_ui->textEdit->document(), &QTextDocument::redoAvailable, - m_ui->pbRedo, &QToolButton::setEnabled); - - m_ui->pbUndo->setEnabled(m_ui->textEdit->document()->isUndoAvailable()); - m_ui->pbRedo->setEnabled(m_ui->textEdit->document()->isRedoAvailable()); - - connect(QApplication::clipboard(), &QClipboard::dataChanged, - this, &LtsTextEditor::slotClipboardDataChanged); - - - connect(m_ui->pbCut, &QToolButton::clicked, - m_ui->textEdit, &QTextEdit::cut); - connect(m_ui->pbCopy, &QToolButton::clicked, - m_ui->textEdit, &QTextEdit::copy); - connect(m_ui->pbPaste, &QToolButton::clicked, - m_ui->textEdit, &QTextEdit::paste); - - connect(m_ui->pbUndo, &QToolButton::clicked, - m_ui->textEdit, &QTextEdit::undo); - connect(m_ui->pbRedo, &QToolButton::clicked, - m_ui->textEdit, &QTextEdit::redo); - - connect(m_ui->pbBold, &QToolButton::clicked, - this, &LtsTextEditor::slotTextBold); - connect(m_ui->pbItalic, &QToolButton::clicked, - this, &LtsTextEditor::slotTextItalic); - connect(m_ui->pbUnderline, &QToolButton::clicked, - this, &LtsTextEditor::slotTextUnderline); - - connect(m_ui->pbTextColor, &QToolButton::clicked, - this, &LtsTextEditor::slotTextColor); - connect(m_ui->pbBackgroundColor, &QToolButton::clicked, - this, &LtsTextEditor::slotBackgroundColor); - - connect(m_ui->pbAlignLeft, &QToolButton::toggled, - this, &LtsTextEditor::slotTextAlign); - connect(m_ui->pbAlignCenter, &QToolButton::toggled, - this, &LtsTextEditor::slotTextAlign); - connect(m_ui->pbAlignRight, &QToolButton::toggled, - this, &LtsTextEditor::slotTextAlign); - connect(m_ui->pbAlignJustify, &QToolButton::toggled, - this, &LtsTextEditor::slotTextAlign); - - - QFont textFont(QStringLiteral("Sans Serif")); - textFont.setStyleHint(QFont::SansSerif); - m_ui->textEdit->setFont(textFont); - slotFontChanged(m_ui->textEdit->font()); - slotTextColorChanged(m_ui->textEdit->textColor()); - slotAlignmentChanged(); - - slotBackgroundColorChanged(m_backgroundColor); - - - connect(m_ui->cbFonts, QOverload::of(&QComboBox::activated), - this, &LtsTextEditor::slotTextFamily); - - const QList standardSizes = QFontDatabase::standardSizes(); - for (int size : standardSizes) - m_ui->cbFontSize->addItem(QString::number(size)); - m_ui->cbFontSize->setCurrentIndex(standardSizes.indexOf(QApplication::font().pointSize())); - - connect(m_ui->cbFontSize, QOverload::of(&QComboBox::activated), - this, &LtsTextEditor::slotTextSize); - - connect(m_ui->cbFontSize->lineEdit(), &QLineEdit::editingFinished, - this, &LtsTextEditor::slotTextSizeEditingFinished); - - m_ui->textEdit->setFocus(); -} - - -LtsTextEditor::~LtsTextEditor() -{ - delete m_ui; -} - - -QTextEdit *LtsTextEditor::textEdit() -{ - return m_ui->textEdit; -} - -QColor LtsTextEditor::backgroundColor() const -{ - return m_backgroundColor; -} - -void LtsTextEditor::enableBackgroundColor(bool onOff) -{ - m_ui->pbBackgroundColor->setVisible(onOff); -} - -void LtsTextEditor::setInitialBackgroundColor(const QColor &bgColor) -{ - if (bgColor.isValid()) - m_backgroundColor = bgColor; - slotBackgroundColorChanged(m_backgroundColor); -} - - -void LtsTextEditor::slotTextBold() -{ - QTextCharFormat fmt; - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - if (m_ui->textEdit->document()->isEmpty()) - { - const QString &f = m_ui->cbFonts->currentText(); - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - fmt.setForeground(m_foregroundColor); - const qreal pointSize = (m_ui->cbFontSize->currentText().toFloat() > 0 ? m_ui->cbFontSize->currentText().toFloat() : 11); - fmt.setFontPointSize(pointSize); - } - slotMergeFormatOnWordOrSelection(fmt); -} - - -void LtsTextEditor::slotTextUnderline() -{ - QTextCharFormat fmt; - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - if (m_ui->textEdit->document()->isEmpty()) - { - const QString &f = m_ui->cbFonts->currentText(); - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - fmt.setForeground(m_foregroundColor); - const qreal pointSize = (m_ui->cbFontSize->currentText().toFloat() > 0 ? m_ui->cbFontSize->currentText().toFloat() : 11); - fmt.setFontPointSize(pointSize); - } - slotMergeFormatOnWordOrSelection(fmt); -} - - -void LtsTextEditor::slotTextItalic() -{ - QTextCharFormat fmt; - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - if (m_ui->textEdit->document()->isEmpty()) - { - const QString &f = m_ui->cbFonts->currentText(); - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - fmt.setForeground(m_foregroundColor); - const qreal pointSize = (m_ui->cbFontSize->currentText().toFloat() > 0 ? m_ui->cbFontSize->currentText().toFloat() : 11); - fmt.setFontPointSize(pointSize); - } - slotMergeFormatOnWordOrSelection(fmt); -} - - -void LtsTextEditor::slotTextFamily(const QString &f) -{ - QTextCharFormat fmt; - if (m_ui->textEdit->document()->isEmpty()) - { - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - - const qreal pointSize = (m_ui->cbFontSize->currentText().toFloat() > 0 ? m_ui->cbFontSize->currentText().toFloat() : 11); - fmt.setFontPointSize(pointSize); - fmt.setForeground(m_foregroundColor); - } - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - slotMergeFormatOnWordOrSelection(fmt); -} - - -void LtsTextEditor::slotTextSize(const QString &p) -{ - qreal pointSize = p.toFloat(); - - if (p.toFloat() > 0) - { - QSignalBlocker signalBlocker(m_ui->cbFontSize->lineEdit()); - m_ui->cbFontSize->clearFocus(); - - QTextCharFormat fmt; - if (m_ui->textEdit->document()->isEmpty()) - { - const QString &f = m_ui->cbFonts->currentText(); - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - fmt.setForeground(m_foregroundColor); - - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - - } - fmt.setFontPointSize(pointSize); - slotMergeFormatOnWordOrSelection(fmt); - } -} - -void LtsTextEditor::slotTextSizeEditingFinished() -{ - qreal pointSize = m_ui->cbFontSize->currentText().toFloat(); - - if (pointSize > 0) - { - QTextCharFormat textCharFormat; - - textCharFormat.setFontPointSize(pointSize); - - QTextCursor cursor = m_ui->textEdit->textCursor(); - - if (cursor.charFormat().font().pointSizeF() == pointSize) - return; - - if (!cursor.hasSelection()) - cursor.select(QTextCursor::WordUnderCursor); - - cursor.mergeCharFormat(textCharFormat); - } -} - -void LtsTextEditor::slotTextColor() -{ - QColor col = QColorDialog::getColor(m_ui->textEdit->textColor(), this); - if (!col.isValid()) - return; - - QTextCharFormat fmt; - if (m_ui->textEdit->document()->isEmpty()) - { - fmt.setFontItalic(m_ui->pbItalic->isChecked()); - fmt.setFontUnderline(m_ui->pbUnderline->isChecked()); - fmt.setFontWeight(m_ui->pbBold->isChecked() ? QFont::Bold : QFont::Normal); - - const QString &f = m_ui->cbFonts->currentText(); - fmt.setFontFamily(f); - fmt.setFontFamilies({f}); - const qreal pointSize = (m_ui->cbFontSize->currentText().toFloat() > 0 ? m_ui->cbFontSize->currentText().toFloat() : 11); - fmt.setFontPointSize(pointSize); - } - fmt.setForeground(col); - slotMergeFormatOnWordOrSelection(fmt); - slotTextColorChanged(col); -} - -void LtsTextEditor::slotBackgroundColor() -{ - QColor col = QColorDialog::getColor(m_backgroundColor.isValid() ? m_backgroundColor : Qt::white, this); - if (!col.isValid()) - return; - - m_backgroundColor = col; - slotBackgroundColorChanged(m_backgroundColor); - emit backgroundColorChanged(m_backgroundColor); -} - - -void LtsTextEditor::slotCurrentCharFormatChanged(const QTextCharFormat &format) -{ - slotFontChanged(format.font()); - slotTextColorChanged(format.foreground().color()); -} - - -void LtsTextEditor::slotFontChanged(const QFont &f) -{ - m_ui->cbFonts->setCurrentIndex(m_ui->cbFonts->findText(f.family())); - - QSignalBlocker comboBoxsignalBlocker(m_ui->cbFontSize); - QSignalBlocker lineEditSignalBlocker(m_ui->cbFontSize->lineEdit()); - QString fontSizeString = QString::number(f.pointSize()); - int32_t fontSizeIndex = m_ui->cbFontSize->findText(fontSizeString); - - if (fontSizeIndex >= 0) - m_ui->cbFontSize->setCurrentIndex(fontSizeIndex); - else if (m_ui->cbFontSize->currentText() != fontSizeString) - m_ui->cbFontSize->setCurrentText(fontSizeString); - - m_ui->pbBold->setChecked(f.bold()); - m_ui->pbItalic->setChecked(f.italic()); - m_ui->pbUnderline->setChecked(f.underline()); -} - - -void LtsTextEditor::slotMergeFormatOnWordOrSelection(const QTextCharFormat &format) -{ - QTextCursor cursor = m_ui->textEdit->textCursor(); - if (!cursor.hasSelection()) - cursor.select(QTextCursor::WordUnderCursor); - - cursor.mergeCharFormat(format); - - - if (m_ui->textEdit->document()->isEmpty()) - m_ui->textEdit->setTextCursor(cursor); -} - - -void LtsTextEditor::slotTextColorChanged(const QColor &c) -{ - QPixmap pix(32, 32); - pix.fill(c); - m_ui->pbTextColor->setIcon(pix); - m_foregroundColor = c; -} - - -void LtsTextEditor::slotBackgroundColorChanged(const QColor &bgColor) -{ - QPixmap pix(32, 32); - pix.fill(bgColor); - m_ui->pbBackgroundColor->setIcon(pix); -} - - -void LtsTextEditor::slotTextAlign() -{ - QToolButton *b = qobject_cast(sender()); - - if (b == nullptr) - return; - - if (b == m_ui->pbAlignLeft) - m_ui->textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); - else if (b == m_ui->pbAlignCenter) - m_ui->textEdit->setAlignment(Qt::AlignHCenter); - else if (b == m_ui->pbAlignRight) - m_ui->textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); - else if (b == m_ui->pbAlignJustify) - m_ui->textEdit->setAlignment(Qt::AlignJustify); -} - - -void LtsTextEditor::slotAlignmentChanged() -{ - auto a = m_ui->textEdit->alignment(); - - if (a & Qt::AlignLeft) - m_ui->pbAlignLeft->setChecked(true); - if (a & Qt::AlignHCenter) - m_ui->pbAlignCenter->setChecked(true); - if (a & Qt::AlignRight) - m_ui->pbAlignRight->setChecked(true); - if (a & Qt::AlignJustify) - m_ui->pbAlignJustify->setChecked(true); -} - - -void LtsTextEditor::slotClipboardDataChanged() -{ - if (const QMimeData *md = QApplication::clipboard()->mimeData()) - m_ui->pbPaste->setEnabled(md->hasText()); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_text_editor.h b/Lts/Common/LtsWidgets/lts_text_editor.h deleted file mode 100755 index 385e0959c..000000000 --- a/Lts/Common/LtsWidgets/lts_text_editor.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include - -#include - - -namespace Ui -{ -class LtsTextEditor; -} - -class QTextEdit; -class QTextCharFormat; - - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsTextEditor : public QWidget -{ - Q_OBJECT -public: - LtsTextEditor(QWidget *parent = nullptr); - ~LtsTextEditor(); - void enableBackgroundColor(bool onOff); - void setInitialBackgroundColor(const QColor &bgColor); - QTextEdit *textEdit(); - QColor backgroundColor() const; - -Q_SIGNALS: - void backgroundColorChanged(const QColor &bgColor); - -private: - Ui::LtsTextEditor *m_ui; - QColor m_backgroundColor{Qt::white}; - QColor m_foregroundColor{Qt::black}; - -private slots: - void slotCurrentCharFormatChanged(const QTextCharFormat &format); - void slotFontChanged(const QFont &f); - void slotTextColorChanged(const QColor &c); - void slotBackgroundColorChanged(const QColor &bgColor); - void slotAlignmentChanged(); - void slotMergeFormatOnWordOrSelection(const QTextCharFormat &format); - void slotClipboardDataChanged(); - void slotTextColor(); - void slotBackgroundColor(); - void slotTextSize(const QString &p); - void slotTextSizeEditingFinished(); - void slotTextFamily(const QString &f); - void slotTextItalic(); - void slotTextUnderline(); - void slotTextBold(); - - void slotTextAlign(); -}; - -} - - diff --git a/Lts/Common/LtsWidgets/lts_text_editor.ui b/Lts/Common/LtsWidgets/lts_text_editor.ui deleted file mode 100755 index dccc94a58..000000000 --- a/Lts/Common/LtsWidgets/lts_text_editor.ui +++ /dev/null @@ -1,598 +0,0 @@ - - - LtsTextEditor - - - - 0 - 0 - 1223 - 429 - - - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Cut - - - - - - - :/style/edit-cut.svg:/style/edit-cut.svg - - - - 32 - 32 - - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Copy - - - - - - - :/style/edit-copy.svg:/style/edit-copy.svg - - - - 32 - 32 - - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Paste - - - - - - - :/style/edit-paste.svg:/style/edit-paste.svg - - - - 32 - 32 - - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Undo - - - - - - - :/style/undo.svg:/style/undo.svg - - - - 32 - 32 - - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Redo - - - - - - - :/style/redo.svg:/style/redo.svg - - - - 32 - 32 - - - - - - - - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - QLayout::SetMinimumSize - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Align text left - - - - - - - :/style/align-left.svg:/style/align-left.svg - - - - 32 - 32 - - - - true - - - true - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Center text - - - - - - - :/style/align-center.svg:/style/align-center.svg - - - - 32 - 32 - - - - true - - - true - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Align text right - - - - - - - :/style/align-right.svg:/style/align-right.svg - - - - 32 - 32 - - - - true - - - true - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Justify text - - - - - - - :/style/align-justify.svg:/style/align-justify.svg - - - - 32 - 32 - - - - true - - - true - - - - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Bold - - - - - - - :/style/text-bold.svg:/style/text-bold.svg - - - - 32 - 32 - - - - true - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Italic - - - - - - - :/style/text-italic.svg:/style/text-italic.svg - - - - 32 - 32 - - - - true - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Underline - - - - - - - :/style/text-underline.svg:/style/text-underline.svg - - - - 32 - 32 - - - - true - - - - - - - - - - false - - - Sans Serif - - - - - - - true - - - QComboBox::NoInsert - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Change text color - - - - - - - 32 - 32 - - - - - - - - - 42 - 42 - - - - - 42 - 42 - - - - Change background color - - - - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_text_editor2.cpp b/Lts/Common/LtsWidgets/lts_text_editor2.cpp deleted file mode 100755 index 0a0540c85..000000000 --- a/Lts/Common/LtsWidgets/lts_text_editor2.cpp +++ /dev/null @@ -1,920 +0,0 @@ - - - - -#include "lts_text_editor2.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 -#if defined(QT_PRINTSUPPORT_LIB) -#include -#if QT_CONFIG(printer) -#if QT_CONFIG(printdialog) -#include -#endif -#include -#if QT_CONFIG(printpreviewdialog) -#include -#endif -#endif -#endif - - -#include -#ifdef Q_OS_MAC -inline QString rsrcPath(){ QStringLiteral(":/images/mac");} -#else -inline QString rsrcPath(){return QStringLiteral(":/images/win");} -#endif - -namespace LtsWidgets -{ -LtsTextEditor2::LtsTextEditor2(QWidget *parent) - : QMainWindow(parent) -{ - setWindowFlags(Qt::CustomizeWindowHint ); - setWindowTitle(QCoreApplication::applicationName()); - - m_textEdit = new QTextEdit(this); - connect(m_textEdit, &QTextEdit::currentCharFormatChanged, - this, &LtsTextEditor2::currentCharFormatChanged); - connect(m_textEdit, &QTextEdit::cursorPositionChanged, - this, &LtsTextEditor2::cursorPositionChanged); - setCentralWidget(m_textEdit); - - setToolButtonStyle(Qt::ToolButtonFollowStyle); - setupFileActions(); - setupEditActions(); - setupTextActions(); - - { - QMenu *helpMenu = menuBar()->addMenu(tr("Help")); - helpMenu->addAction(tr("About"), this, &LtsTextEditor2::about); - helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); - } - - QFont textFont(QStringLiteral("Helvetica")); - textFont.setStyleHint(QFont::SansSerif); - m_textEdit->setFont(textFont); - fontChanged(m_textEdit->font()); - colorChanged(m_textEdit->textColor()); - alignmentChanged(m_textEdit->alignment()); - - connect(m_textEdit->document(), &QTextDocument::modificationChanged, - m_actionSave, &QAction::setEnabled); - connect(m_textEdit->document(), &QTextDocument::modificationChanged, - this, &QWidget::setWindowModified); - connect(m_textEdit->document(), &QTextDocument::undoAvailable, - m_actionUndo, &QAction::setEnabled); - connect(m_textEdit->document(), &QTextDocument::redoAvailable, - m_actionRedo, &QAction::setEnabled); - - setWindowModified(m_textEdit->document()->isModified()); - m_actionSave->setEnabled(m_textEdit->document()->isModified()); - m_actionUndo->setEnabled(m_textEdit->document()->isUndoAvailable()); - m_actionRedo->setEnabled(m_textEdit->document()->isRedoAvailable()); - -#ifndef QT_NO_CLIPBOARD - m_actionCut->setEnabled(false); - connect(m_textEdit, &QTextEdit::copyAvailable, m_actionCut, &QAction::setEnabled); - m_actionCopy->setEnabled(false); - connect(m_textEdit, &QTextEdit::copyAvailable, m_actionCopy, &QAction::setEnabled); - - connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &LtsTextEditor2::clipboardDataChanged); -#endif - - m_textEdit->setFocus(); - setCurrentFileName(QString()); - - this->menuBar()->hide(); - m_actionSave->setVisible(false); - m_actionOpen->setVisible(false); - m_actionQuit->setVisible(false); - m_actionSaveAs->setVisible(false); - m_actionNew->setVisible(false); - -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) - actionPrint->setVisible(false); - actionPreview->setVisible(false); - actionExport->setVisible(false); -#endif - m_actionToggleCheckState->setVisible(false); - m_actionToggleCheckState->setVisible(false); - m_actionIndentLess->setVisible(false); - m_actionIndentMore->setVisible(false); - m_comboStyleAction->setVisible(false); - m_actionToggleCheckState->setVisible(false); - - -} - -LtsTextEditor2::~LtsTextEditor2() -{ - -} - -QTextEdit *LtsTextEditor2::textEdit() -{ - return m_textEdit; -} -QColor LtsTextEditor2::backgroundColor() const -{ - return m_backgroundColor; -} - -void LtsTextEditor2::setInitialBackgroundColor(const QColor &bgColor) -{ - if (bgColor.isValid()) - m_backgroundColor = bgColor; - slotBackgroundColorChanged(m_backgroundColor); -} -void LtsTextEditor2::enableBackgroundColor(bool onOff) -{ - m_actionBackgroundColor->setVisible(onOff); -} - - - -void LtsTextEditor2::slotBackgroundColorChanged(const QColor &bgColor) -{ - QPixmap pix(16, 16); - pix.fill(bgColor); - m_actionBackgroundColor->setIcon(pix); -} -void LtsTextEditor2::slotBackgroundColor() -{ - QColor col = QColorDialog::getColor(m_backgroundColor.isValid() ? m_backgroundColor : Qt::white, this); - if (!col.isValid()) - return; - - m_backgroundColor = col; - slotBackgroundColorChanged(m_backgroundColor); - emit backgroundColorChanged(m_backgroundColor); -} - - -void LtsTextEditor2::setupFileActions() -{ - QToolBar *tb = addToolBar(tr("File Actions")); - QMenu *menu = menuBar()->addMenu(tr("&File")); - - const QIcon newIcon = QIcon::fromTheme(QStringLiteral("document-new"), QIcon(rsrcPath() + QStringLiteral("/filenew.png"))); - m_actionNew = menu->addAction(newIcon, tr("&New"), this, &LtsTextEditor2::onFileNew); - tb->addAction(m_actionNew); - m_actionNew->setPriority(QAction::LowPriority); - m_actionNew->setShortcut(QKeySequence::New); - - const QIcon openIcon = QIcon::fromTheme(QStringLiteral("document-open"), QIcon(rsrcPath() + QStringLiteral("/fileopen.png"))); - m_actionOpen = menu->addAction(openIcon, tr("&Open..."), this, &LtsTextEditor2::onFileOpen); - m_actionOpen->setShortcut(QKeySequence::Open); - tb->addAction(m_actionOpen); - - menu->addSeparator(); - - const QIcon saveIcon = QIcon::fromTheme(QStringLiteral("document-save"), QIcon(rsrcPath()+ QStringLiteral("/filesave.png"))); - m_actionSave = menu->addAction(saveIcon, tr("&Save"), this, &LtsTextEditor2::onFileSave); - m_actionSave->setShortcut(QKeySequence::Save); - m_actionSave->setEnabled(false); - tb->addAction(m_actionSave); - - m_actionSaveAs = menu->addAction(tr("Save &As..."), this, &LtsTextEditor2::onFileSaveAs); - m_actionSaveAs->setPriority(QAction::LowPriority); - menu->addSeparator(); - -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) - const QIcon printIcon = QIcon::fromTheme(QStringLiteral("document-print"), QIcon(rsrcPath()+ QStringLiteral("/fileprint.png"))); - actionPrint = menu->addAction(printIcon, tr("&Print..."), this, &LtsTextEditor2::filePrint); - actionPrint->setPriority(QAction::LowPriority); - actionPrint->setShortcut(QKeySequence::Print); - tb->addAction(actionPrint); - - const QIcon filePrintIcon = QIcon::fromTheme(QStringLiteral("fileprint"), QIcon(rsrcPath()+ QStringLiteral("/fileprint.png"))); - actionPreview = menu->addAction(filePrintIcon, tr("Print Preview..."), this, &LtsTextEditor2::filePrintPreview); - - const QIcon exportPdfIcon = QIcon::fromTheme(QStringLiteral("exportpdf"), QIcon(rsrcPath()+ QStringLiteral("/exportpdf.png"))); - actionExport = menu->addAction(exportPdfIcon, tr("&Export PDF..."), this, &LtsTextEditor2::filePrintPdf); - actionExport->setPriority(QAction::LowPriority); - actionExport->setShortcut(Qt::CTRL + Qt::Key_D); - tb->addAction(actionExport); - - menu->addSeparator(); -#endif - - m_actionQuit = menu->addAction(tr("&Quit"), this, &QWidget::close); - m_actionQuit->setShortcut(Qt::CTRL + Qt::Key_Q); -} - -void LtsTextEditor2::setupEditActions() -{ - QToolBar *tb = addToolBar(tr("Edit Actions")); - QMenu *menu = menuBar()->addMenu(tr("&Edit")); - - const QIcon undoIcon = QIcon::fromTheme(QStringLiteral("icon:undo.svg")); - m_actionUndo = menu->addAction(undoIcon, {}, m_textEdit, &QTextEdit::undo); - m_actionUndo->setToolTip(tr("&Undo")); - m_actionUndo->setShortcut(QKeySequence::Undo); - tb->addAction(m_actionUndo); - - const QIcon redoIcon = QIcon::fromTheme(QStringLiteral("icon:redo.svg")); - m_actionRedo = menu->addAction(redoIcon, tr("&Redo"), m_textEdit, &QTextEdit::redo); - m_actionRedo->setPriority(QAction::LowPriority); - m_actionRedo->setShortcut(QKeySequence::Redo); - tb->addAction(m_actionRedo); - menu->addSeparator(); - -#ifndef QT_NO_CLIPBOARD - const QIcon cutIcon = QIcon::fromTheme(QStringLiteral("icon:edit-cut.svg")); - m_actionCut = menu->addAction(cutIcon, tr("Cu&t"), m_textEdit, &QTextEdit::cut); - m_actionCut->setPriority(QAction::LowPriority); - m_actionCut->setShortcut(QKeySequence::Cut); - tb->addAction(m_actionCut); - - const QIcon copyIcon = QIcon::fromTheme(QStringLiteral("icon:edit-copy.svg")); - m_actionCopy = menu->addAction(copyIcon, tr("&Copy"), m_textEdit, &QTextEdit::copy); - m_actionCopy->setPriority(QAction::LowPriority); - m_actionCopy->setShortcut(QKeySequence::Copy); - tb->addAction(m_actionCopy); - - const QIcon pasteIcon = QIcon::fromTheme(QStringLiteral("icon:edit-paste.svg")); - m_actionPaste = menu->addAction(pasteIcon, tr("&Paste"), m_textEdit, &QTextEdit::paste); - m_actionPaste->setPriority(QAction::LowPriority); - m_actionPaste->setShortcut(QKeySequence::Paste); - tb->addAction(m_actionPaste); - if (const QMimeData *md = QApplication::clipboard()->mimeData()) - m_actionPaste->setEnabled(md->hasText()); -#endif -} - -void LtsTextEditor2::setupTextActions() -{ - QToolBar *tb = addToolBar(tr("Format Actions")); - QMenu *menu = menuBar()->addMenu(tr("F&ormat")); - - const QIcon boldIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-text-bold.png")); - m_actionTextBold = menu->addAction(boldIcon, tr("&Bold"), this, &LtsTextEditor2::onTextBold); - m_actionTextBold->setShortcut(Qt::CTRL + Qt::Key_B); - m_actionTextBold->setPriority(QAction::LowPriority); - QFont bold; - bold.setBold(true); - m_actionTextBold->setFont(bold); - tb->addAction(m_actionTextBold); - m_actionTextBold->setCheckable(true); - - const QIcon italicIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-text-italic.png")); - m_actionTextItalic = menu->addAction(italicIcon, tr("&Italic"), this, &LtsTextEditor2::onTextItalic); - m_actionTextItalic->setPriority(QAction::LowPriority); - m_actionTextItalic->setShortcut(Qt::CTRL + Qt::Key_I); - QFont italic; - italic.setItalic(true); - m_actionTextItalic->setFont(italic); - tb->addAction(m_actionTextItalic); - m_actionTextItalic->setCheckable(true); - - const QIcon underlineIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-text-underline.png")); - m_actionTextUnderline = menu->addAction(underlineIcon, tr("&Underline"), this, &LtsTextEditor2::onTextUnderline); - m_actionTextUnderline->setShortcut(Qt::CTRL + Qt::Key_U); - m_actionTextUnderline->setPriority(QAction::LowPriority); - QFont underline; - underline.setUnderline(true); - m_actionTextUnderline->setFont(underline); - tb->addAction(m_actionTextUnderline); - m_actionTextUnderline->setCheckable(true); - - menu->addSeparator(); - - const QIcon leftIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-justify-left.png")); - m_actionAlignLeft = new QAction(leftIcon, tr("&Left"), this); - m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); - m_actionAlignLeft->setCheckable(true); - m_actionAlignLeft->setPriority(QAction::LowPriority); - const QIcon centerIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-justify-center.png")); - m_actionAlignCenter = new QAction(centerIcon, tr("C&enter"), this); - m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); - m_actionAlignCenter->setCheckable(true); - m_actionAlignCenter->setPriority(QAction::LowPriority); - const QIcon rightIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-justify-right.png")); - m_actionAlignRight = new QAction(rightIcon, tr("&Right"), this); - m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); - m_actionAlignRight->setCheckable(true); - m_actionAlignRight->setPriority(QAction::LowPriority); - const QIcon fillIcon = QIcon::fromTheme(QStringLiteral(":icons/22x22/format-justify-fill.png")); - m_actionAlignJustify = new QAction(fillIcon, tr("&Justify"), this); - m_actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); - m_actionAlignJustify->setCheckable(true); - m_actionAlignJustify->setPriority(QAction::LowPriority); - const QIcon indentMoreIcon = QIcon::fromTheme(QStringLiteral("format-indent-more"), QIcon(rsrcPath()+ QStringLiteral("/format-indent-more.png"))); - m_actionIndentMore = menu->addAction(indentMoreIcon, tr("&Indent"), this, &LtsTextEditor2::indent); - m_actionIndentMore->setShortcut(Qt::CTRL + Qt::Key_BracketRight); - m_actionIndentMore->setPriority(QAction::LowPriority); - const QIcon indentLessIcon = QIcon::fromTheme(QStringLiteral("format-indent-less"), QIcon(rsrcPath()+ QStringLiteral("/format-indent-less.png"))); - m_actionIndentLess = menu->addAction(indentLessIcon, tr("&Unindent"), this, &LtsTextEditor2::unindent); - m_actionIndentLess->setShortcut(Qt::CTRL + Qt::Key_BracketLeft); - m_actionIndentLess->setPriority(QAction::LowPriority); - - - QActionGroup *alignGroup = new QActionGroup(this); - connect(alignGroup, &QActionGroup::triggered, this, &LtsTextEditor2::onTextAlign); - - if (QApplication::isLeftToRight()) { - alignGroup->addAction(m_actionAlignLeft); - alignGroup->addAction(m_actionAlignCenter); - alignGroup->addAction(m_actionAlignRight); - } else { - alignGroup->addAction(m_actionAlignRight); - alignGroup->addAction(m_actionAlignCenter); - alignGroup->addAction(m_actionAlignLeft); - } - alignGroup->addAction(m_actionAlignJustify); - - tb->addActions(alignGroup->actions()); - menu->addActions(alignGroup->actions()); - tb->addAction(m_actionIndentMore); - tb->addAction(m_actionIndentLess); - menu->addAction(m_actionIndentMore); - menu->addAction(m_actionIndentLess); - - menu->addSeparator(); - - QPixmap pix(16, 16); - pix.fill(Qt::black); - m_actionTextColor = menu->addAction(pix, tr("&Color..."), this, &LtsTextEditor2::onTextColor); - m_actionTextColor->setPriority(QAction::LowPriority); - tb->addAction(m_actionTextColor); - - QPixmap pix2(16, 16); - pix2.fill(Qt::white); - m_actionBackgroundColor = new QAction(pix2, tr("&Background Color"), this); - m_actionBackgroundColor->setIcon(pix2); - m_actionBackgroundColor->setPriority(QAction::LowPriority); - connect(m_actionBackgroundColor, &QAction::triggered, this, &LtsTextEditor2::slotBackgroundColor); - tb->addAction(m_actionBackgroundColor); - menu->addSeparator(); - - const QIcon checkboxIcon = QIcon::fromTheme(QStringLiteral("status-checkbox-checked"), QIcon(rsrcPath()+ QStringLiteral("/checkbox-checked.png"))); - m_actionToggleCheckState = menu->addAction(checkboxIcon, tr("Chec&ked"), this, &LtsTextEditor2::setChecked); - m_actionToggleCheckState->setShortcut(Qt::CTRL + Qt::Key_K); - m_actionToggleCheckState->setCheckable(true); - m_actionToggleCheckState->setPriority(QAction::LowPriority); - tb->addAction(m_actionToggleCheckState); - - - - - - - m_comboStyle = new QComboBox(tb); - m_comboStyleAction = tb->addWidget(m_comboStyle); - m_comboStyle->addItem(QStringLiteral("Standard")); - m_comboStyle->addItem(QStringLiteral("Bullet List (Disc)")); - m_comboStyle->addItem(QStringLiteral("Bullet List (Circle)")); - m_comboStyle->addItem(QStringLiteral("Bullet List (Square)")); - m_comboStyle->addItem(QStringLiteral("Task List (Unchecked)")); - m_comboStyle->addItem(QStringLiteral("Task List (Checked)")); - m_comboStyle->addItem(QStringLiteral("Ordered List (Decimal)")); - m_comboStyle->addItem(QStringLiteral("Ordered List (Alpha lower)")); - m_comboStyle->addItem(QStringLiteral("Ordered List (Alpha upper)")); - m_comboStyle->addItem(QStringLiteral("Ordered List (Roman lower)")); - m_comboStyle->addItem(QStringLiteral("Ordered List (Roman upper)")); - m_comboStyle->addItem(QStringLiteral("Heading 1")); - m_comboStyle->addItem(QStringLiteral("Heading 2")); - m_comboStyle->addItem(QStringLiteral("Heading 3")); - m_comboStyle->addItem(QStringLiteral("Heading 4")); - m_comboStyle->addItem(QStringLiteral("Heading 5")); - m_comboStyle->addItem(QStringLiteral("Heading 6")); - - connect(m_comboStyle, QOverload::of(&QComboBox::activated), this, &LtsTextEditor2::onTextStyle); - - m_comboFont = new QFontComboBox(tb); - tb->addWidget(m_comboFont); - connect(m_comboFont, &QComboBox::textActivated, this, &LtsTextEditor2::onTextFamily); - - m_comboSize = new QComboBox(tb); - m_comboSize->setObjectName(QStringLiteral("comboSize")); - tb->addWidget(m_comboSize); - m_comboSize->setEditable(true); - - const QList standardSizes = QFontDatabase::standardSizes(); - for (int size : standardSizes) - m_comboSize->addItem(QString::number(size)); - m_comboSize->setCurrentIndex(standardSizes.indexOf(QApplication::font().pointSize())); - - connect(m_comboSize, &QComboBox::textActivated, this, &LtsTextEditor2::onTextSize); -} - -bool LtsTextEditor2::load(const QString &f) -{ - if (!QFile::exists(f)) - return false; - QFile file(f); - if (!file.open(QFile::ReadOnly)) - return false; - - QByteArray data = file.readAll(); - QTextCodec *codec = Qt::codecForHtml(data); - QString str = codec->toUnicode(data); - if (Qt::mightBeRichText(str)) { - QUrl baseUrl = (f.front() == QLatin1Char(':') ? QUrl(f) : QUrl::fromLocalFile(f)).adjusted(QUrl::RemoveFilename); - m_textEdit->document()->setBaseUrl(baseUrl); - m_textEdit->setHtml(str); - } else { -#if QT_CONFIG(textmarkdownreader) - QMimeDatabase db; - if (db.mimeTypeForFileNameAndData(f, data).name() == QStringLiteral("text/markdown")) - m_textEdit->setMarkdown(QString::fromUtf8(data)); - else -#endif - m_textEdit->setPlainText(QString::fromUtf8(data)); - } - - setCurrentFileName(f); - return true; -} - -bool LtsTextEditor2::maybeSave() -{ - if (!m_textEdit->document()->isModified()) - return true; - - const QMessageBox::StandardButton ret = - QMessageBox::warning(this, QCoreApplication::applicationName(), - tr("The document has been modified.\n" - "Do you want to save your changes?"), - QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - if (ret == QMessageBox::Save) - return onFileSave(); - else if (ret == QMessageBox::Cancel) - return false; - return true; -} - -void LtsTextEditor2::setCurrentFileName(const QString &fileName) -{ - this->m_fileName = fileName; - m_textEdit->document()->setModified(false); - - QString shownName; - if (fileName.isEmpty()) - shownName = QStringLiteral("untitled.txt"); - else - shownName = QFileInfo(fileName).fileName(); - - setWindowTitle(tr("%1[*] - %2").arg(shownName, QCoreApplication::applicationName())); - setWindowModified(false); -} - -void LtsTextEditor2::onFileNew() -{ - if (maybeSave()) { - m_textEdit->clear(); - setCurrentFileName(QString()); - } -} - -void LtsTextEditor2::onFileOpen() -{ - QFileDialog fileDialog(this, tr("Open File...")); - fileDialog.setAcceptMode(QFileDialog::AcceptOpen); - fileDialog.setFileMode(QFileDialog::ExistingFile); - fileDialog.setMimeTypeFilters(QStringList() - #if QT_CONFIG(texthtmlparser) - << QStringLiteral("text/html") - #endif - #if QT_CONFIG(textmarkdownreader) - - << QStringLiteral("text/markdown") - #endif - << QStringLiteral("text/plain")); - if (fileDialog.exec() != QDialog::Accepted) - return; - const QStringList &fileNameList = fileDialog.selectedFiles(); - if (fileNameList.isEmpty()) - return; - QString fn = fileNameList.constFirst(); - if (load(fn)) - statusBar()->showMessage(tr("Opened \"%1\"").arg(QDir::toNativeSeparators(fn))); - else - statusBar()->showMessage(tr("Could not open \"%1\"").arg(QDir::toNativeSeparators(fn))); -} - -bool LtsTextEditor2::onFileSave() -{ - if (m_fileName.isEmpty()) - return onFileSaveAs(); - if (m_fileName.startsWith(QStringLiteral(":/"))) - return onFileSaveAs(); - - QTextDocumentWriter writer(m_fileName); - bool success = writer.write(m_textEdit->document()); - if (success) { - m_textEdit->document()->setModified(false); - statusBar()->showMessage(tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(m_fileName))); - } else { - statusBar()->showMessage(tr("Could not write to file \"%1\"") - .arg(QDir::toNativeSeparators(m_fileName))); - } - return success; -} - -bool LtsTextEditor2::onFileSaveAs() -{ - QFileDialog fileDialog(this, tr("Save as...")); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - QStringList mimeTypes; - mimeTypes << QStringLiteral("text/plain") - #if QT_CONFIG(textodfwriter) - << QStringLiteral("application/vnd.oasis.opendocument.text") - #endif - #if QT_CONFIG(textmarkdownwriter) - << QStringLiteral("text/markdown") - #endif - << QStringLiteral("text/html"); - fileDialog.setMimeTypeFilters(mimeTypes); -#if QT_CONFIG(textodfwriter) - fileDialog.setDefaultSuffix(QStringLiteral("odt")); -#endif - if (fileDialog.exec() != QDialog::Accepted) - return false; - const QStringList &fileNameList = fileDialog.selectedFiles(); - if (fileNameList.isEmpty()) - return false; - QString fn = fileNameList.constFirst(); - setCurrentFileName(fn); - return onFileSave(); -} - -void LtsTextEditor2::onFilePrint() -{ -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) - QPrinter printer(QPrinter::HighResolution); - QPrintDialog *dlg = new QPrintDialog(&printer, this); - if (textEdit->textCursor().hasSelection()) - dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection); - dlg->setWindowTitle(tr("Print Document")); - if (dlg->exec() == QDialog::Accepted) - textEdit->print(&printer); - delete dlg; -#endif -} - -void LtsTextEditor2::onFilePrintPreview() -{ -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printpreviewdialog) - QPrinter printer(QPrinter::HighResolution); - QPrintPreviewDialog preview(&printer, this); - connect(&preview, &QPrintPreviewDialog::paintRequested, this, &LtsTextEditor2::printPreview); - preview.exec(); -#endif -} - -void LtsTextEditor2::printPreview(QPrinter *printer) -{ -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) - textEdit->print(printer); -#else - Q_UNUSED(printer) -#endif -} - - -void LtsTextEditor2::onFilePrintPdf() -{ -#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) - - QFileDialog fileDialog(this, tr("Export PDF")); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setMimeTypeFilters(QStringList(QStringLiteral("application/pdf"))); - fileDialog.setDefaultSuffix(QStringLiteral("pdf")); - if (fileDialog.exec() != QDialog::Accepted) - return; - const QStringList &fileNameList = fileDialog.selectedFiles(); - if (fileNameList.isEmpty()) - return; - QString fileName = fileNameList.constFirst(); - QPrinter printer(QPrinter::HighResolution); - printer.setOutputFormat(QPrinter::PdfFormat); - printer.setOutputFileName(fileName); - textEdit->document()->print(&printer); - statusBar()->showMessage(tr("Exported \"%1\"") - .arg(QDir::toNativeSeparators(fileName))); - -#endif -} - -void LtsTextEditor2::onTextBold() -{ - QTextCharFormat fmt; - fmt.setFontWeight(m_actionTextBold->isChecked() ? QFont::Bold : QFont::Normal); - mergeFormatOnWordOrSelection(fmt); -} - -void LtsTextEditor2::onTextUnderline() -{ - QTextCharFormat fmt; - fmt.setFontUnderline(m_actionTextUnderline->isChecked()); - mergeFormatOnWordOrSelection(fmt); -} - -void LtsTextEditor2::onTextItalic() -{ - QTextCharFormat fmt; - fmt.setFontItalic(m_actionTextItalic->isChecked()); - mergeFormatOnWordOrSelection(fmt); -} - -void LtsTextEditor2::onTextFamily(const QString &f) -{ - QTextCharFormat fmt; - fmt.setFontFamily(f); - mergeFormatOnWordOrSelection(fmt); -} - -void LtsTextEditor2::onTextSize(const QString &p) -{ - qreal pointSize = p.toFloat(); - if (p.toFloat() > 0) { - QTextCharFormat fmt; - fmt.setFontPointSize(pointSize); - mergeFormatOnWordOrSelection(fmt); - } -} - -void LtsTextEditor2::onTextStyle(int styleIndex) -{ - QTextCursor cursor = m_textEdit->textCursor(); - QTextListFormat::Style style = QTextListFormat::ListStyleUndefined; - QTextBlockFormat::MarkerType marker = QTextBlockFormat::MarkerType::NoMarker; - - switch (styleIndex) { - case 1: - style = QTextListFormat::ListDisc; - break; - case 2: - style = QTextListFormat::ListCircle; - break; - case 3: - style = QTextListFormat::ListSquare; - break; - case 4: - if (cursor.currentList()) - style = cursor.currentList()->format().style(); - else - style = QTextListFormat::ListDisc; - marker = QTextBlockFormat::MarkerType::Unchecked; - break; - case 5: - if (cursor.currentList()) - style = cursor.currentList()->format().style(); - else - style = QTextListFormat::ListDisc; - marker = QTextBlockFormat::MarkerType::Checked; - break; - case 6: - style = QTextListFormat::ListDecimal; - break; - case 7: - style = QTextListFormat::ListLowerAlpha; - break; - case 8: - style = QTextListFormat::ListUpperAlpha; - break; - case 9: - style = QTextListFormat::ListLowerRoman; - break; - case 10: - style = QTextListFormat::ListUpperRoman; - break; - default: - break; - } - - cursor.beginEditBlock(); - - QTextBlockFormat blockFmt = cursor.blockFormat(); - - if (style == QTextListFormat::ListStyleUndefined) { - blockFmt.setObjectIndex(-1); - int headingLevel = styleIndex >= 11 ? styleIndex - 11 + 1 : 0; - blockFmt.setHeadingLevel(headingLevel); - cursor.setBlockFormat(blockFmt); - - int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; - QTextCharFormat fmt; - fmt.setFontWeight(headingLevel ? QFont::Bold : QFont::Normal); - fmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment); - cursor.select(QTextCursor::LineUnderCursor); - cursor.mergeCharFormat(fmt); - m_textEdit->mergeCurrentCharFormat(fmt); - } else { - blockFmt.setMarker(marker); - cursor.setBlockFormat(blockFmt); - QTextListFormat listFmt; - if (cursor.currentList()) { - listFmt = cursor.currentList()->format(); - } else { - listFmt.setIndent(blockFmt.indent() + 1); - blockFmt.setIndent(0); - cursor.setBlockFormat(blockFmt); - } - listFmt.setStyle(style); - cursor.createList(listFmt); - } - - cursor.endEditBlock(); -} - -void LtsTextEditor2::onTextColor() -{ - QColor col = QColorDialog::getColor(m_textEdit->textColor(), this); - if (!col.isValid()) - return; - QTextCharFormat fmt; - fmt.setForeground(col); - mergeFormatOnWordOrSelection(fmt); - colorChanged(col); -} - -void LtsTextEditor2::onTextAlign(QAction *a) -{ - if (a == m_actionAlignLeft) - m_textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); - else if (a == m_actionAlignCenter) - m_textEdit->setAlignment(Qt::AlignHCenter); - else if (a == m_actionAlignRight) - m_textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); - else if (a == m_actionAlignJustify) - m_textEdit->setAlignment(Qt::AlignJustify); -} - -void LtsTextEditor2::setChecked(bool checked) -{ - onTextStyle(checked ? 5 : 4); -} - -void LtsTextEditor2::indent() -{ - modifyIndentation(1); -} - -void LtsTextEditor2::unindent() -{ - modifyIndentation(-1); -} - -void LtsTextEditor2::modifyIndentation(int amount) -{ - QTextCursor cursor = m_textEdit->textCursor(); - cursor.beginEditBlock(); - if (cursor.currentList()) { - QTextListFormat listFmt = cursor.currentList()->format(); - - - QTextCursor above(cursor); - above.movePosition(QTextCursor::Up); - if (above.currentList() && listFmt.indent() + amount == above.currentList()->format().indent()) { - above.currentList()->add(cursor.block()); - } else { - listFmt.setIndent(listFmt.indent() + amount); - cursor.createList(listFmt); - } - } else { - QTextBlockFormat blockFmt = cursor.blockFormat(); - blockFmt.setIndent(blockFmt.indent() + amount); - cursor.setBlockFormat(blockFmt); - } - cursor.endEditBlock(); -} - -void LtsTextEditor2::currentCharFormatChanged(const QTextCharFormat &format) -{ - fontChanged(format.font()); - colorChanged(format.foreground().color()); -} - -void LtsTextEditor2::cursorPositionChanged() -{ - alignmentChanged(m_textEdit->alignment()); - QTextList *list = m_textEdit->textCursor().currentList(); - if (list) { - switch (list->format().style()) { - case QTextListFormat::ListDisc: - m_comboStyle->setCurrentIndex(1); - break; - case QTextListFormat::ListCircle: - m_comboStyle->setCurrentIndex(2); - break; - case QTextListFormat::ListSquare: - m_comboStyle->setCurrentIndex(3); - break; - case QTextListFormat::ListDecimal: - m_comboStyle->setCurrentIndex(6); - break; - case QTextListFormat::ListLowerAlpha: - m_comboStyle->setCurrentIndex(7); - break; - case QTextListFormat::ListUpperAlpha: - m_comboStyle->setCurrentIndex(8); - break; - case QTextListFormat::ListLowerRoman: - m_comboStyle->setCurrentIndex(9); - break; - case QTextListFormat::ListUpperRoman: - m_comboStyle->setCurrentIndex(10); - break; - default: - m_comboStyle->setCurrentIndex(-1); - break; - } - switch (m_textEdit->textCursor().block().blockFormat().marker()) { - case QTextBlockFormat::MarkerType::NoMarker: - m_actionToggleCheckState->setChecked(false); - break; - case QTextBlockFormat::MarkerType::Unchecked: - m_comboStyle->setCurrentIndex(4); - m_actionToggleCheckState->setChecked(false); - break; - case QTextBlockFormat::MarkerType::Checked: - m_comboStyle->setCurrentIndex(5); - m_actionToggleCheckState->setChecked(true); - break; - } - } else { - int headingLevel = m_textEdit->textCursor().blockFormat().headingLevel(); - m_comboStyle->setCurrentIndex(headingLevel ? headingLevel + 10 : 0); - } -} - -void LtsTextEditor2::clipboardDataChanged() -{ -#ifndef QT_NO_CLIPBOARD - if (const QMimeData *md = QApplication::clipboard()->mimeData()) - m_actionPaste->setEnabled(md->hasText()); -#endif -} - -void LtsTextEditor2::about() -{ - QMessageBox::about(this, tr("About"), tr("This example demonstrates Qt's " - "rich text editing facilities in action, providing an example " - "document for you to experiment with.")); -} - -void LtsTextEditor2::mergeFormatOnWordOrSelection(const QTextCharFormat &format) -{ - QTextCursor cursor = m_textEdit->textCursor(); - if (!cursor.hasSelection()) - cursor.select(QTextCursor::WordUnderCursor); - cursor.mergeCharFormat(format); - m_textEdit->mergeCurrentCharFormat(format); -} - -void LtsTextEditor2::fontChanged(const QFont &f) -{ - m_comboFont->setCurrentIndex(m_comboFont->findText(QFontInfo(f).family())); - m_comboSize->setCurrentIndex(m_comboSize->findText(QString::number(f.pointSize()))); - m_actionTextBold->setChecked(f.bold()); - m_actionTextItalic->setChecked(f.italic()); - m_actionTextUnderline->setChecked(f.underline()); -} - -void LtsTextEditor2::colorChanged(const QColor &c) -{ - QPixmap pix(16, 16); - pix.fill(c); - m_actionTextColor->setIcon(pix); -} - -void LtsTextEditor2::alignmentChanged(Qt::Alignment a) -{ - if (a & Qt::AlignLeft) - m_actionAlignLeft->setChecked(true); - else if (a & Qt::AlignHCenter) - m_actionAlignCenter->setChecked(true); - else if (a & Qt::AlignRight) - m_actionAlignRight->setChecked(true); - else if (a & Qt::AlignJustify) - m_actionAlignJustify->setChecked(true); -} - -} diff --git a/Lts/Common/LtsWidgets/lts_text_editor2.h b/Lts/Common/LtsWidgets/lts_text_editor2.h deleted file mode 100755 index f636101b7..000000000 --- a/Lts/Common/LtsWidgets/lts_text_editor2.h +++ /dev/null @@ -1,135 +0,0 @@ - - -#pragma once - - -#include - -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QAction; -class QComboBox; -class QFontComboBox; -class QTextEdit; -class QTextCharFormat; -class QMenu; -class QPrinter; -QT_END_NAMESPACE - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsTextEditor2 : public QMainWindow -{ - Q_OBJECT - -public: - explicit LtsTextEditor2(QWidget *parent = nullptr); - ~LtsTextEditor2(); - - static QString rsrcPath(){return (QStringLiteral(":/images/win"));} - - QColor m_backgroundColor{Qt::white}; - QColor m_foregroundColor{Qt::black}; - - QTextEdit *textEdit(); - QColor backgroundColor() const; - void enableBackgroundColor(bool onOff); - void setInitialBackgroundColor(const QColor &bgColor); - bool load(const QString &f); - - -Q_SIGNALS: - void backgroundColorChanged(const QColor &bgColor); - -public slots: - -private slots: - void onFileNew(); - void onFileOpen(); - bool onFileSave(); - bool onFileSaveAs(); - void onFilePrint(); - void onFilePrintPreview(); - void onFilePrintPdf(); - - void onTextBold(); - void onTextUnderline(); - void onTextItalic(); - void onTextFamily(const QString &f); - void onTextSize(const QString &p); - void onTextStyle(int styleIndex); - void onTextColor(); - void onTextAlign(QAction *a); - void setChecked(bool checked); - void indent(); - void unindent(); - void slotBackgroundColorChanged(const QColor &bgColor); - void slotBackgroundColor(); - - void currentCharFormatChanged(const QTextCharFormat &format); - void cursorPositionChanged(); - - void clipboardDataChanged(); - void about(); - void printPreview(QPrinter *); - -private: - void setupFileActions(); - void setupEditActions(); - void setupTextActions(); - bool maybeSave(); - void setCurrentFileName(const QString &fileName); - void modifyIndentation(int amount); - - void mergeFormatOnWordOrSelection(const QTextCharFormat &format); - void fontChanged(const QFont &f); - void colorChanged(const QColor &c); - void alignmentChanged(Qt::Alignment a); - - QAction *m_actionSave; - QAction *m_actionOpen; - QAction *m_actionQuit; - QAction *m_actionSaveAs; - QAction *m_actionNew; - QAction *m_actionPrint; - QAction *m_actionPreview; - QAction *m_actionExport; - QAction *m_actionTextBold; - QAction *m_actionTextUnderline; - QAction *m_actionTextItalic; - QAction *m_actionTextColor; - QAction *m_actionBackgroundColor; - QAction *m_actionAlignLeft; - QAction *m_actionAlignCenter; - QAction *m_actionAlignRight; - QAction *m_actionAlignJustify; - QAction *m_actionIndentLess; - QAction *m_actionIndentMore; - QAction *m_actionToggleCheckState; - QAction *m_actionUndo; - QAction *m_actionRedo; -#ifndef QT_NO_CLIPBOARD - QAction *m_actionCut; - QAction *m_actionCopy; - QAction *m_actionPaste; -#endif - - QComboBox *m_comboStyle; - QAction *m_comboStyleAction; - QFontComboBox *m_comboFont; - QComboBox *m_comboSize; - - QToolBar *m_tb; - QString m_fileName; - QTextEdit *m_textEdit; -}; - - } diff --git a/Lts/Common/LtsWidgets/lts_tool_bar.cpp b/Lts/Common/LtsWidgets/lts_tool_bar.cpp deleted file mode 100755 index 1bd1fd012..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_bar.cpp +++ /dev/null @@ -1,31 +0,0 @@ - - - -#include "lts_tool_bar.h" - -using namespace LtsWidgets; - -LtsToolBar::LtsToolBar(QWidget *parent) - : QToolBar(parent) -{ - setMaximumHeight(1); - setMinimumHeight(1); -} - -LtsToolBar::~LtsToolBar() -{ -} - -void LtsToolBar::enterEvent(QEvent *e) -{ - setMaximumHeight(50); - setMinimumHeight(50); - QToolBar::enterEvent(e); -} - -void LtsToolBar::leaveEvent(QEvent *e) -{ - setMaximumHeight(1); - setMinimumHeight(1); - QToolBar::leaveEvent(e); -} diff --git a/Lts/Common/LtsWidgets/lts_tool_bar.h b/Lts/Common/LtsWidgets/lts_tool_bar.h deleted file mode 100755 index 40ffe7d70..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_bar.h +++ /dev/null @@ -1,27 +0,0 @@ - - - -#pragma once - -#include -#include "lts_widgets_export.h" - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsToolBar : public QToolBar -{ - - Q_OBJECT -public: - explicit LtsToolBar(QWidget *parent = nullptr); - ~LtsToolBar(); - -protected: - void enterEvent(QEvent *e) override; - void leaveEvent(QEvent *e) override; - - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp b/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp deleted file mode 100755 index b349f2423..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp +++ /dev/null @@ -1,384 +0,0 @@ - - - -#include "lts_tool_bar_area.h" -#include "ui_lts_tool_bar_area.h" -#include "lts_drag_icon.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ - - - -void ShortCut::clear() -{ - name.clear(); - identifier.clear(); - icon = nullptr; - shortcut->disconnect(icon); -} - - - - -LtsToolBarArea::LtsToolBarArea(QWidget *parent) - : QWidget(parent) - , m_dummyLabel(nullptr) - , m_ui(new Ui::LtsToolBarArea) -{ - m_ui->setupUi(this); - - setAcceptDrops(true); - clearToolBarArea(); - - - for(int i=Qt::Key_F5; i<=Qt::Key_F12; ++i) - { - ShortCut sc(this); - sc.shortcut->setKey(QKeySequence(i)); - sc.key = i; - m_functionKeys.insert(i, sc); - } -} - -LtsToolBarArea::~LtsToolBarArea() -{ - delete m_ui; -} - -void LtsToolBarArea::addMenuEntry(LtsDragIcon *entry, LtsDragIcon *after) -{ - if (!entry) - return; - - if (m_foreignButtonMap.contains(entry)) - return; - - delete m_dummyLabel; - m_dummyLabel = nullptr; - - bool isInternalMove = false; - LtsDragIcon *foreignDragIcon = entry; - - for (int i = 0; i < m_buttonMap.size(); ++i) - { - if (m_buttonMap.at(i) == entry) - { - isInternalMove = true; - - if (after == nullptr) - return; - m_buttonMap.removeAt(i); - foreignDragIcon = m_foreignButtonMap.at(i); - m_foreignButtonMap.removeAt(i); - m_ui->layToolbar->removeWidget(entry); - break; - } - } - - - int index = m_buttonMap.size(); - for(int i = 0; i < m_buttonMap.size(); ++i) - { - if (m_buttonMap.at(i) == after || - m_foreignButtonMap.at(i) == after) - { - index = i + 1; - break; - } - } - - - LtsDragIcon *newAction = entry; - - if (!isInternalMove) - { - newAction = new LtsDragIcon(this); - newAction->setIcon(entry->icon()); - newAction->setText(entry->text()); - newAction->setContext(entry->context()); - if (!entry->toolTip().isEmpty()) - newAction->setToolTip(entry->toolTip()); - newAction->setIconSize(ButtonSize::eDefault); - newAction->setButtonStyle(ButtonStyle::eIconOnly); - newAction->setIdentifier(entry->identifier()); - - connect(newAction, &LtsDragIcon::clicked, entry, &LtsDragIcon::clicked); - connect(entry, &LtsDragIcon::destroyed, newAction, - [this, newAction]() { m_buttonMap.removeAll(newAction); newAction->deleteLater(); }); - newAction->setVisible(!entry->text().isEmpty()); - } - newAction->setContextMenuPolicy(Qt::CustomContextMenu); - connect(newAction, &LtsDragIcon::customContextMenuRequested, - this, &LtsToolBarArea::showContextMenu); - - m_foreignButtonMap.insert(index, foreignDragIcon); - m_buttonMap.insert(index, newAction); - - connect(foreignDragIcon, &LtsDragIcon::enabledChanged, newAction, &LtsDragIcon::setEnabled); - - - m_ui->layToolbar->insertWidget(index + 1, newAction); - saveSettings(); -} - -void LtsToolBarArea::clearToolBarArea() -{ - qDeleteAll(m_buttonMap); - m_buttonMap.clear(); - m_foreignButtonMap.clear(); - for(ShortCut &item : m_functionKeys) - item.clear(); - - delete m_dummyLabel; - - m_dummyLabel = new QLabel(tr("Drop menu entry here"), this); - m_ui->layToolbar->addWidget(m_dummyLabel); -} - -void LtsToolBarArea::setMenuEntries(const QVector &actions) -{ - clearToolBarArea(); - - for (LtsDragIcon* ico : actions) - addMenuEntry(ico); -} - -QStringList LtsToolBarArea::menuEntries() const -{ - QStringList ret; - ret.reserve(m_foreignButtonMap.size()); - for (LtsDragIcon *dragIcon : m_foreignButtonMap) - ret << dragIcon->identifier(); - - return ret; -} - - - - -void LtsToolBarArea::dragEnterEvent(QDragEnterEvent *event) -{ - LtsDragIcon *source = - qobject_cast(event->source()); - if (source) - { - if (!m_foreignButtonMap.contains(source)) - { - event->setDropAction(Qt::MoveAction); - event->accept(); - } - } -} - -void LtsToolBarArea::dropEvent(QDropEvent *event) -{ - LtsDragIcon *source = qobject_cast(event->source()); - - LtsDragIcon *after = nullptr; - QWidget *w = childAt(event->pos()); - while (w) - { - after = qobject_cast(w); - if (after) - break; - w = w->parentWidget(); - } - - addMenuEntry(source, after); -} - -QMenu *LtsToolBarArea::functionKeyMenu(QWidget *parent) -{ - QMenu* menu = new QMenu(parent); - menu->setTitle(tr("Function Key")); - QAction *act; - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - ShortCut &shorty = val.value(); - act = menu->addAction(QStringLiteral("%1%2").arg(shorty.keyName(), !shorty.name.isEmpty() ? QStringLiteral(" - %1").arg(shorty.name) : QString())); - act->setData(shorty.shortcut->key()[0]); - connect(act, &QAction::triggered, this, &LtsToolBarArea::functionKeyMenuTriggered); - } - menu->addSeparator(); - act = menu->addAction(tr("Remove Function Key")); - act->setData(-1); - connect(act, &QAction::triggered, this, &LtsToolBarArea::functionKeyMenuTriggered); - return menu; -} - -void LtsToolBarArea::functionKeyMenuTriggered() -{ - QAction* act = qobject_cast(sender()); - if (!act) - return; - int fkey = act->data().toInt(); - - if (fkey == -1) - removeFunctionKey(m_currentIdentifierForContextMenue); - else if (fkey > 0) - setFunctionKey(fkey, m_currentIdentifierForContextMenue); -} - - -bool LtsToolBarArea::removeFunctionKey(const QString &action) -{ - if (action.isEmpty()) - return true; - - - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - ShortCut &itShortcut = val.value(); - if (itShortcut.identifier == action) - { - itShortcut.icon->removeModification(); - itShortcut.clear(); - break; - } - } - saveSettings(); - return true; -} - -bool LtsToolBarArea::setFunctionKey(int fkey, const QString &action) -{ - - if (fkey < Qt::Key_F5 || fkey > Qt::Key_F12) - return false; - - - LtsDragIcon *icon = nullptr; - for (LtsDragIcon *ic : qAsConst(m_buttonMap)) - { - if (ic->identifier() == action) - { - icon = ic; - break; - } - } - - if (!icon) - return false; - - - ShortCut &shorty = m_functionKeys[fkey]; - - - for (LtsDragIcon *ic : qAsConst(m_buttonMap)) - if (ic->identifier() == shorty.identifier) - ic->removeModification(); - - - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - ShortCut &itShortcut = val.value(); - if (itShortcut.identifier == action) - { - itShortcut.clear(); - break; - } - } - - - shorty.clear(); - shorty.identifier = icon->identifier(); - shorty.name = icon->text(); - shorty.icon = icon; - shorty.icon->setModification(shorty.keyName()); - connect(shorty.shortcut, &QShortcut::activated, icon, &LtsDragIcon::clicked); - saveSettings(); - return true; -} - -QMap LtsToolBarArea::functionKeys() const -{ - QMap retVal; - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - const ShortCut &itShortcut = val.value(); - if (!itShortcut.identifier.isEmpty()) - retVal[itShortcut.key] = itShortcut.identifier; - } - return retVal; -} - -void LtsToolBarArea::removeMenuAction(LtsDragIcon *icon) -{ - - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - ShortCut &itShortcut = val.value(); - if (itShortcut.identifier == icon->identifier()) - itShortcut.clear(); - } - for (LtsDragIcon *ic : qAsConst(m_foreignButtonMap)) - { - if(ic->identifier() == icon->identifier()) - { - m_foreignButtonMap.removeAll(ic); - break; - } - } - m_buttonMap.removeAll(icon); - m_ui->layToolbar->removeWidget(icon); - delete icon; - saveSettings(); -} - -void LtsToolBarArea::showContextMenu(const QPoint &pos) -{ - QPointer icon = qobject_cast(sender()); - if (!icon) - return; - - const QPoint &globalPos = icon->mapToGlobal(pos); - - QMenu *contextMenu = new QMenu(this); - contextMenu->setAttribute(Qt::WA_DeleteOnClose); - - - m_currentIdentifierForContextMenue = icon->identifier(); - connect(contextMenu, &QObject::destroyed, this, [this](){ - - m_currentIdentifierForContextMenue.clear(); - }); - - - QAction *delAction = contextMenu->addAction(tr("Remove")); - connect(delAction, &QAction::triggered, this, [this, icon] () { - if (icon) - removeMenuAction(icon); - }); - - - QMenu *fkeyMenu = functionKeyMenu(contextMenu); - contextMenu->addMenu(fkeyMenu); - - contextMenu->popup(globalPos); -} - - -void LtsToolBarArea::saveSettings() -{ - - - - - - - - - -} -} diff --git a/Lts/Common/LtsWidgets/lts_tool_bar_area.h b/Lts/Common/LtsWidgets/lts_tool_bar_area.h deleted file mode 100755 index d45103722..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_bar_area.h +++ /dev/null @@ -1,115 +0,0 @@ - - - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include -#include -#include - - -class QMenu; -class QLabel; - -namespace Ui -{ -class LtsToolBarArea; -} - -namespace LtsWidgets -{ -class LtsDragIcon; - -struct ShortCut -{ - int key = 0; - QShortcut *shortcut = nullptr; - QString name; - QString identifier; - LtsDragIcon *icon = nullptr; - QString keyName() const {return shortcut ? shortcut->key().toString() : QString();} - void clear(); - - explicit ShortCut(QWidget *parent = nullptr) - : shortcut(new QShortcut(parent)) - {} - - ~ShortCut() { delete shortcut; } - ShortCut(const ShortCut &o) - : key(o.key) - , name(o.name) - , identifier(o.identifier) - , icon(o.icon) - { - if (o.shortcut) - { - delete shortcut; - shortcut = new QShortcut(o.shortcut->key(), qobject_cast(o.shortcut->parent()), nullptr, nullptr, o.shortcut->context()); - } - } - - ShortCut &operator=(const ShortCut &o) - { - if (&o == this) - return *this; - - key = o.key; - name = o.name; - identifier = o.identifier; - icon = o.icon; - QWidget *parent = qobject_cast(shortcut->parent()); - delete shortcut; - if (o.shortcut) - shortcut = new QShortcut(o.shortcut->key(), qobject_cast(o.shortcut->parent()), nullptr, nullptr, o.shortcut->context()); - else - shortcut = new QShortcut(parent); - return *this; - } -}; - -class LTSWIDGETS_EXPORT LtsToolBarArea : public QWidget -{ - - Q_OBJECT -public: - explicit LtsToolBarArea(QWidget *parent = nullptr); - ~LtsToolBarArea(); - - void addMenuEntry(LtsDragIcon *entry, LtsDragIcon *after = nullptr); - void clearToolBarArea(); - - void setMenuEntries(const QVector &actions); - QStringList menuEntries() const; - - bool setFunctionKey(int fkey, const QString &action); - QMap functionKeys() const; - - bool removeFunctionKey(const QString &action); - void removeMenuAction(LtsDragIcon *icon); - -public slots: - void showContextMenu(const QPoint &pos); - void functionKeyMenuTriggered(); - -protected: - void dragEnterEvent(QDragEnterEvent *event) override; - void dropEvent(QDropEvent *event) override; - -private: - QMenu* functionKeyMenu(QWidget *parent); - QString m_currentIdentifierForContextMenue; - - QVector m_buttonMap; - QVector m_foreignButtonMap; - QMap m_functionKeys; - QLabel *m_dummyLabel; - - Ui::LtsToolBarArea *m_ui; - - void saveSettings(); -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_tool_bar_area.ui b/Lts/Common/LtsWidgets/lts_tool_bar_area.ui deleted file mode 100755 index 4f0e14e84..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_bar_area.ui +++ /dev/null @@ -1,38 +0,0 @@ - - - LtsToolBarArea - - - - 0 - 0 - 600 - 125 - - - - Form - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/lts_tool_box.cpp b/Lts/Common/LtsWidgets/lts_tool_box.cpp deleted file mode 100755 index 2095b7070..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_box.cpp +++ /dev/null @@ -1,85 +0,0 @@ - - - -#include "lts_tool_box.h" - -#include "lts_drag_icon.h" -#include "lts_grid_layout.h" - -#include -#include -#include - -using namespace LtsWidgets; - - -LtsToolBox::LtsToolBox(QWidget *parent) - : QToolBox(parent) -{ - - setMinimumSize(17, 200); - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &LtsToolBox::customContextMenuRequested, - this, &LtsToolBox::showContextMenu); - - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - -} - -LtsToolBox::~LtsToolBox() -{ -} - -void LtsToolBox::resizeEvent(QResizeEvent *e) -{ - emit resized(e); - QToolBox::resizeEvent(e); - -} - -void LtsToolBox::addItem(QWidget *widget, const QString &text) -{ - setMinimumSize(120, 100); - QToolBox::addItem(widget, text); -} - - -void LtsToolBox::showContextMenu(const QPoint &pos) -{ - - - QPoint globalPos = this->mapToGlobal(pos); - - - - QMenu myMenu; - myMenu.addAction(tr("Icon and Text")); - QAction *to = myMenu.addAction(tr("Text Only")); - QAction *io = myMenu.addAction(tr("Icon Only")); - - - QAction *selectedItem = myMenu.exec(globalPos); - if (selectedItem) - { - ButtonStyle newStyle = ButtonStyle::eTextNextToIcon; - if (selectedItem == to) - newStyle = ButtonStyle::eTextOnly; - if (selectedItem == io) - newStyle = ButtonStyle::eIconOnly; - - for (int i = 0; i < count(); i++) - { - const QList list = widget(i)->findChildren(); - for (LtsDragIcon *icon : list) - icon->setButtonStyle(newStyle); - LtsGridLayout *lay = qobject_cast(widget(i)->layout()); - if (lay) - lay->setAutoSize((newStyle == ButtonStyle::eIconOnly)); - } - - } - else - { - - } -} diff --git a/Lts/Common/LtsWidgets/lts_tool_box.h b/Lts/Common/LtsWidgets/lts_tool_box.h deleted file mode 100755 index b789bb0d8..000000000 --- a/Lts/Common/LtsWidgets/lts_tool_box.h +++ /dev/null @@ -1,31 +0,0 @@ - - - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" - -#include - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT LtsToolBox : public QToolBox -{ - Q_OBJECT -public: - explicit LtsToolBox(QWidget *parent = nullptr); - ~LtsToolBox(); - void resizeEvent(QResizeEvent *e) override; - void addItem(QWidget *widget, const QString &text); - -signals: - void resized(QResizeEvent *e); - -public slots: - void showContextMenu(const QPoint &pos); - -}; - -} diff --git a/Lts/Common/LtsWidgets/lts_wait_dialog.cpp b/Lts/Common/LtsWidgets/lts_wait_dialog.cpp deleted file mode 100755 index cb5e3efa3..000000000 --- a/Lts/Common/LtsWidgets/lts_wait_dialog.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "lts_wait_dialog.h" -#include "lts_global_wait_dialog.h" - - -namespace LtsWidgets -{ -LtsWaitDialog::LtsWaitDialog(const QString &customText) -{ - LtsGlobalWaitDialog::getInstance()->wait(customText); -} - - -LtsWaitDialog::~LtsWaitDialog() -{ - LtsGlobalWaitDialog::getInstance()->done(); -} -} diff --git a/Lts/Common/LtsWidgets/lts_wait_dialog.h b/Lts/Common/LtsWidgets/lts_wait_dialog.h deleted file mode 100755 index c8ce363e8..000000000 --- a/Lts/Common/LtsWidgets/lts_wait_dialog.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "lts_widgets_export.h" - -#include - -#include - - -namespace LtsWidgets -{ -class LTSWIDGETS_EXPORT LtsWaitDialog -{ -public: - LtsWaitDialog(const QString &customText = {}); - ~LtsWaitDialog(); -}; - -typedef std::unique_ptr LtsWaitDialogContainer; - -} diff --git a/Lts/Common/LtsWidgets/lts_widgets_export.h b/Lts/Common/LtsWidgets/lts_widgets_export.h deleted file mode 100755 index ec9e7223b..000000000 --- a/Lts/Common/LtsWidgets/lts_widgets_export.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include - -#ifndef LTSWIDGETS_EXPORT -# ifdef BUILD_STATIC_LIBRARIES -# define LTSWIDGETS_EXPORT -# else -# if defined(MAKE_LTSWIDGETS_LIB) -# define LTSWIDGETS_EXPORT Q_DECL_EXPORT -# else -# define LTSWIDGETS_EXPORT Q_DECL_IMPORT -# endif -# endif -#endif diff --git a/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp b/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp deleted file mode 100755 index e31a1b85c..000000000 --- a/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp +++ /dev/null @@ -1,1196 +0,0 @@ - - - -#include -#include -#include - -#include -#include -#include -#include -#include "lts_dialog_button_box.h" -#include "lts_elided_label.h" -#include "lts_image_editor.h" -#include "lts_picture_view.h" -#include "lts_profile_button.h" -#include "lts_splitter.h" -#include "lts_tab_bar.h" -#include "lts_tab_widget.h" -#include "lts_tool_bar.h" -#include "lts_tool_bar_area.h" -#include "lts_tool_box.h" -#include "lts_frameless_dialog.h" -#include "lts_drag_icon.h" -#include "lts_combo_box.h" -#include "lts_spinbox.h" -#include "lts_multi_range_spinbox.h" -#include "lts_range_slider.h" -#include "lts_msec_date_time_edit.h" -#include "lts_bitfield_widget.h" -#include "lts_tab_sequence_widget.h" - -#ifdef TABLET_MODE -#include "lts_date_time_edit_touch.h" -#else -#include "lts_date_time_edit.h" -#endif - -#include "lts_led_widget.h" -#include "lts_base.h" -#include "LtsImageCropper/lts_image_cropper.h" - -namespace LtsWidgets -{ - -class LtsDesignerCustomWidgetInterface : public QObject, public QDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using QObject::QObject; - - bool isContainer() const override final - { - return false; - } - bool isInitialized() const override final - { - return mInitialized; - } - QIcon icon() const override final - { - return QIcon(QStringLiteral(":style/icons/22x22/pushbutton.png")); - } - QString codeTemplate() const override final - { - return QString(); - } - QString group() const override final - { - return QLatin1String("Lts Widgets"); - } - void initialize(QDesignerFormEditorInterface *) override final - { - mInitialized = true; - } - -private: - bool mInitialized = false; -}; - -class LtsCoordWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsCoordTrans/lts_coord_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsCoordWidget::LtsCoordWidget"); - } - QString toolTip() const override - { - return QLatin1String("Widget for entering a coordinate"); - } - QString whatsThis() const override - { - return QLatin1String("Widget for entering a coordinate"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsCoordWidget::LtsCoordWidget(parent); - } -}; - - -class LtsDialogButtonBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_dialog_button_box.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsDialogButtonBox"); - } - QString toolTip() const override - { - return QLatin1String("Widget for a LTS version of the Button Box"); - } - QString whatsThis() const override - { - return QLatin1String("Widget for a LTS version of the Botton Box"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsDialogButtonBox(parent); - } -}; - -class LtsElidedLabelPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QStringLiteral("LtsWidgets/lts_elided_label.h"); - } - QString name() const override - { - return QStringLiteral("LtsWidgets::LtsElidedLabel"); - } - QString toolTip() const override - { - return QStringLiteral("A QLabel which automatically elided it's content if needed."); - } - QString whatsThis() const override - { - return toolTip(); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsElidedLabel(parent); - } -}; - -class LtsImageEditorPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_image_editor.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsImageEditor"); - } - QString toolTip() const override - { - return QLatin1String("Image Editor"); - } - QString whatsThis() const override - { - return QLatin1String("ImageEditor"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsImageEditor(parent); - } -}; - - -class LtsPictureViewPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_picture_view.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsPictureView"); - } - QString toolTip() const override - { - return QLatin1String("Picture View Widget"); - } - QString whatsThis() const override - { - return QLatin1String("Picture View Widget"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsPictureView(parent); - } -}; - - -class LtsProfileButtonPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_profile_button.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsProfileButton"); - } - QString toolTip() const override - { - return QLatin1String("Profile Button"); - } - QString whatsThis() const override - { - return QLatin1String("Profile Button"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsProfileButton(parent); - } -}; - - -class LtsSplitterPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_splitter.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsSplitter"); - } - QString toolTip() const override - { - return QLatin1String("Splitter with sizes property"); - } - QString whatsThis() const override - { - return QLatin1String("Splitter with sizes property"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsSplitter(parent); - } -}; - - -class LtsTabBarPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tab_bar.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsTabBar"); - } - QString toolTip() const override - { - return QLatin1String("Tab Bar"); - } - QString whatsThis() const override - { - return QLatin1String("Tab Bar"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsTabBar(parent); - } -}; - - -class LtsTabWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tab_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsTabWidget"); - } - QString toolTip() const override - { - return QLatin1String("Tab Widget"); - } - QString whatsThis() const override - { - return QLatin1String("Tab Widget"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsTabWidget(parent); - } -}; - - -class LtsToolBarPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tool_bar.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsToolBar"); - } - QString toolTip() const override - { - return QLatin1String("Tool Bar"); - } - QString whatsThis() const override - { - return QLatin1String("Tool Bar"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsToolBar(parent); - } -}; - - -class LtsToolBarAreaPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tool_bar_area.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsToolBarArea"); - } - QString toolTip() const override - { - return QLatin1String("Tool Bar Area"); - } - QString whatsThis() const override - { - return QLatin1String("Tool Bar Area"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsToolBarArea(parent); - } -}; - - -class LtsToolBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tool_box.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsToolBox"); - } - QString toolTip() const override - { - return QLatin1String("Tool Box"); - } - QString whatsThis() const override - { - return QLatin1String("Tool Box"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsToolBox(parent); - } -}; - - -class LtsDataViewPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsDataView/lts_data_view.h"); - } - QString name() const override - { - return QLatin1String("LtsDataView::LtsDataView"); - } - QString toolTip() const override - { - return QLatin1String("DataView"); - } - QString whatsThis() const override - { - return QLatin1String("DataView"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsDataView::LtsDataView(parent); - } -}; - - -class LtsFramelessDialogPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_frameless_dialog.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsFramelessDialog"); - } - QString toolTip() const override - { - return QLatin1String("Enhanced QDialog"); - } - QString whatsThis() const override - { - return QLatin1String("Dialog Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - \ - 0\ - 0\ - 500\ - 200\ - \ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsFramelessDialog(parent); - } -}; - - -class LtsDragIconPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_drag_icon.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsDragIcon"); - } - QString toolTip() const override - { - return QLatin1String("Class for standard buttons with icons"); - } - QString whatsThis() const override - { - return QLatin1String("Button Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsDragIcon *b = new LtsWidgets::LtsDragIcon(parent); - return b; - } -}; - - -class LtsComboBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_combo_box.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsComboBox"); - } - QString toolTip() const override - { - return QLatin1String("Class for standard ComBoBoxes"); - } - QString whatsThis() const override - { - return QLatin1String("ComboBox Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsComboBox *b = new LtsWidgets::LtsComboBox(parent); - return b; - } -}; - - -class LtsSpinBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_spinbox.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsSpinBox"); - } - QString toolTip() const override - { - return QLatin1String("Class for standard SpinBoxes"); - } - QString whatsThis() const override - { - return QLatin1String("SpinBox Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsSpinBox *b = new LtsWidgets::LtsSpinBox(parent); - return b; - } -}; - -class LtsMultiRangeSpinBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_multi_range_spinbox.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsMultiRangeSpinBox"); - } - QString toolTip() const override - { - return QLatin1String("Class for standard SpinBoxes with multiple ranges"); - } - QString whatsThis() const override - { - return QLatin1String("SpinBox Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsMultiRangeSpinBox *b = new LtsWidgets::LtsMultiRangeSpinBox(parent); - return b; - } -}; - - - -class LtsDoubleSpinBoxPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_spinbox.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsDoubleSpinBox"); - } - QString toolTip() const override - { - return QLatin1String("Class for standard DoubleSpinBoxes"); - } - QString whatsThis() const override - { - return QLatin1String("DoubleSpinBox Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsDoubleSpinBox *b = new LtsWidgets::LtsDoubleSpinBox(parent); - return b; - } -}; - - -class LtsDateTimeEditPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_date_time_edit.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsDateTimeEdit"); - } - QString toolTip() const override - { - return QLatin1String("Class for LTS Date Time Edit"); - } - QString whatsThis() const override - { - return QLatin1String("LtsDateTimeEdit Widget"); - } - QString domXml() const override - { - return QLatin1String("\n" - " \n" - " \n" - " 0\n" - " 0\n" - " \n" - " \n" - ""); - } - QWidget *createWidget(QWidget *parent) override - { - LtsWidgets::LtsDateTimeEdit *b = new LtsWidgets::LtsDateTimeEdit(parent); - return b; - } -}; - -class LtsFileDialogWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsFileDialog/lts_file_dialog.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsFileDialogWidget"); - } - QString toolTip() const override - { - return QLatin1String("File Dialog Widget"); - } - QString whatsThis() const override - { - return QLatin1String("FileDialogWidget"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsFileDialogWidget(parent); - } -}; - -class LtsFileDialogPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsFileDialog/lts_file_dialog.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsFileDialog"); - } - QString toolTip() const override - { - return QLatin1String("File Dialog Widget"); - } - QString whatsThis() const override - { - return QLatin1String("FileDialog"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsFileDialog(parent); - } -}; - -class LtsReportDisplayWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsReportDisplay/lts_report_display_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsReportDisplayWidget"); - } - QString toolTip() const override - { - return QLatin1String("Report Display Widget"); - } - QString whatsThis() const override - { - return QLatin1String("ReportDisplayWidget"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsReportDisplayWidget(parent); - } -}; - -class LtsLedWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_led_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsLedWidget"); - } - QString toolTip() const override - { - return QLatin1String("Class for an LED Widget"); - } - QString whatsThis() const override - { - return QLatin1String("LED Widget"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsLedWidget(parent); - } -}; - -class LtsRangeSliderPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_range_slider.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsRangeSlider"); - } - QString toolTip() const override - { - return QLatin1String("Class for an Range Slider"); - } - QString whatsThis() const override - { - return QLatin1String("Range Slider"); - } - QString domXml() const override - { - return QLatin1String("\ - \ - \ - "); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsRangeSlider(parent); - } -}; - -class LtsImageCropperPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString name() const override - { - return QLatin1String("LtsWidgets::LtsImageCropper"); - } - QString toolTip() const override - { - return QLatin1String("Class for an Image Cropper Widget"); - } - QString whatsThis() const override - { - return QLatin1String("Image Cropper Widget"); - } - QString includeFile() const override - { - return QLatin1String("LtsWidgets/LtsImageCropper/lts_image_cropper.h"); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsImageCropper(parent); - } - QString domXml() const override - { - return QLatin1String("\n" - " \n" - " \n" - " 0\n" - " 0\n" - " \n" - " \n" - " \n" - " \n" - " 100\n" - " 100\n" - " \n" - " \n" - " \n" - " \n" - " 0\n" - " 0\nitfield" - " \n" - " \n" - ""); - } -}; - -class LtsMSecDateTimeEditPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString name() const override - { - return QLatin1String("LtsWidgets::LtsMSecDateTimeEdit"); - } - QString toolTip() const override - { - return QLatin1String("Class for an Date/Time Widget with 40msecs step."); - } - QString whatsThis() const override - { - return QLatin1String("Date/Time Widget with 40msecs step"); - } - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_msec_date_time_edit.h"); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsMSecDateTimeEdit(parent); - } - QString domXml() const override - { - return QLatin1String("\n" - ""); - } -}; - -class LtsBitfieldWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("lts_bitfield_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsBitfieldWidget"); - } - QString toolTip() const override - { - return QLatin1String("Widget for bitwise displaying and editing of quint8"); - } - QString whatsThis() const override - { - return QLatin1String("Widget for bitwise displaying and editing of quint8"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsBitfieldWidget(parent); - } -}; - - -class LtsTabSequenceWidgetPlugin final : public LtsDesignerCustomWidgetInterface -{ - Q_OBJECT - - Q_INTERFACES(QDesignerCustomWidgetInterface) -public: - using LtsDesignerCustomWidgetInterface::LtsDesignerCustomWidgetInterface; - - QString includeFile() const override - { - return QLatin1String("LtsWidgets/lts_tab_sequence_widget.h"); - } - QString name() const override - { - return QLatin1String("LtsWidgets::LtsTabSequenceWidget"); - } - QString toolTip() const override - { - return QLatin1String("Widget for a Tab Sequence"); - } - QString whatsThis() const override - { - return QLatin1String("Widget for a Tab Sequence"); - } - QString domXml() const override - { - return QStringLiteral(""); - } - QWidget *createWidget(QWidget *parent) override - { - return new LtsWidgets::LtsTabSequenceWidget(parent); - } -}; - - -class Plugin final : public QObject, public QDesignerCustomWidgetCollectionInterface -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.rde.sls.ltswidgetsplugin" FILE "ltswidgetsplugin.json") - Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) -public: - explicit Plugin(QObject *parent = nullptr); - - QList customWidgets() const override - { - return m_plugins; - } - -private: - QList m_plugins; -}; - - -Plugin::Plugin(QObject *parent) - : QObject(parent) -{ - m_plugins.append(new LtsCoordWidgetPlugin(this)); - m_plugins.append(new LtsDialogButtonBoxPlugin(this)); - m_plugins.append(new LtsImageEditorPlugin(this)); - m_plugins.append(new LtsElidedLabelPlugin(this)); - m_plugins.append(new LtsPictureViewPlugin(this)); - m_plugins.append(new LtsProfileButtonPlugin(this)); - m_plugins.append(new LtsSplitterPlugin(this)); - m_plugins.append(new LtsTabBarPlugin(this)); - m_plugins.append(new LtsTabWidgetPlugin(this)); - m_plugins.append(new LtsToolBarPlugin(this)); - m_plugins.append(new LtsToolBarAreaPlugin(this)); - m_plugins.append(new LtsToolBoxPlugin(this)); - m_plugins.append(new LtsFramelessDialogPlugin(this)); - m_plugins.append(new LtsDragIconPlugin(this)); - m_plugins.append(new LtsComboBoxPlugin(this)); - m_plugins.append(new LtsSpinBoxPlugin(this)); - m_plugins.append(new LtsMultiRangeSpinBoxPlugin(this)); - m_plugins.append(new LtsDoubleSpinBoxPlugin(this)); - m_plugins.append(new LtsDateTimeEditPlugin(this)); - m_plugins.append(new LtsDataViewPlugin(this)); - m_plugins.append(new LtsFileDialogWidgetPlugin(this)); - m_plugins.append(new LtsFileDialogPlugin(this)); - m_plugins.append(new LtsReportDisplayWidgetPlugin(this)); - m_plugins.append(new LtsLedWidgetPlugin(this)); - m_plugins.append(new LtsRangeSliderPlugin(this)); - m_plugins.append(new LtsImageCropperPlugin(this)); - m_plugins.append(new LtsMSecDateTimeEditPlugin(this)); - m_plugins.append(new LtsBitfieldWidgetPlugin(this)); - m_plugins.append(new LtsTabSequenceWidgetPlugin(this)); -} - -} -#include "ltswidgetsplugin.moc" diff --git a/Lts/Common/LtsWidgets/ltswidgetsplugin.json b/Lts/Common/LtsWidgets/ltswidgetsplugin.json deleted file mode 100755 index a2697da42..000000000 --- a/Lts/Common/LtsWidgets/ltswidgetsplugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name" : "Lts Widgets", - "version" : "1.0.0", - "dependencies" : [] -} diff --git a/Lts/Common/LtsWidgets/resources/DMF.wav b/Lts/Common/LtsWidgets/resources/DMF.wav deleted file mode 100755 index d6f7ccc7132ee21c3ddafccf27bc5338aec02c41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 355352 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Å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%0ApQ8Y%SWIt3v76UcnB2}M$ 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

ooUQKi<>_CgP8~X1vj`wA#?At<^ao8 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µ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

GQ29ekhn%bZyoWH%p~Oh(;BGTWCC57UQOrF z*XU04NO}*nXOi?gss!_Z?#1RX2jPsRFVlm&PEFz3Q$@L*)EYLCF2bhKrlo&o|*j?gzr5 zT$P3Mi9Pv^@$b2Haf>-9lw%cV3AToFDZ{w>F~#HBGKb-Lk2xe$i1H?qfVgi@O5>JFV#3z0cae~K*Y`z9wpPzvB zV#lH*>D_ios+YB%gugekozac_rY|MNz~}wndJJ04Hj*7Q2bouO68n{#_&cQoQAAB5 zzbIYE^Ky{r9NR?Pjm{>T#qJRm<(VX>RG`w8=2V7ym|UdoAeL&Q@UQA^tbh+9C}!*ULbMztGL6A(tPQpqU4s=v`e2vryRflY z6K#NTNy$ zz|>#N*zY}Q^zgjU=H*OPTV==8qS=OaDZ7c@Hpi!}%^9aP%6X@5%-*O@$X3)IITf@) z?qv0)`-XDSoh27>kCSWW^py`}Uy(0lKbJ3Ozle3uPL3AHo*4~f&x_7-_l%zQEQ^-% zo{6sTZjTo9O^&wl4T(Ar2oe!phwe#L4Y zh*^^ZZLETU59TlbPjf?HnYBCk8uSX4M_z>Mrq&#}ueL_dt3S{pFxOH?TZb0XuA`H*7MP@;z~W#YF;lBUoP^e) zUxtVLW^5(@(vyj)s*Z1$Z{mGp+4zcBed37xl31tQBo8Rxs9*AN8t!4~L9w6o_Si(a ztbCs8rUG(4hR{c+Gy3rktvnrx5Ku`3y z?L(F#BqZjUpgSr-FU%%r72axs68>_Lk=AYONYcO^T zG{pMa`LX?WY0PiO!Pewe*uV9n8E67t053(CO z`ovHqgzvZe5r07s>R$UhY)FtMGgFDEug#;!Hw+ zVdY`ZcOX^+go%qtVQM}4p1Oy1rFP)M$=�WE=bw zH5NypF=893~q{ zkijkfi60Wi;md`Rgj*;{M#KPFz%h(k;MhxTcdVmYJF8P=TzM$f*_~?S*hP7SI&@XO zJ+qhV%eLZkasLUmxqi|{wy@&?CjTlpMnWF|!A@6${WvYN->v+Tg^`5K(m&3%Ww-Zy@vQxJ0})VyNNyJ!+hab z3R5-umP`*H#G8iBVUGfHu^fK}w$MKZAM8JXANK!=|Mu6%JNY}~vbPHU%QFc(?x}_5 z_Etf*d6(Kty?4N4PZPj;I)LBqW*<-`WVc!Y$*V0!u0THE4HM?kp?zu@=xtX5kb4CwE|q`^39}a=x$G72LEF?s z?6aK@n*ccx7$m9=L@$8*=xA#Zw%gna2?fuH9cCt3!MZ^mwJOo)t=9B@YdU?;TF;o) zJkGLm3%jkMLde|34>C(~EzQf!3e!Umw7m2z(4HA*PiCA*b7muA&^$`h|DhMiPuMr& z3ErQmPi%+I&6U_4ycf#hwjD)pgDmJsU4=jf36cX&AWb2Q@IBaqxWGf?m&v314GO)h zd!SVWL2qkskdNvbRIh$on9is30Jh4GuzmR;ox-i5 zmUB1ABkXJF9l1gYR3T)Z|I0(XHa!5d5`zAEeH^0U3T{_F&H z1833|go#uUF-n#aM^IaYQ`A$w1$BfwM;>ISkVDyYGKz_} zEZLXaNBqIP!9Q{r@fG|@d>CH~m%0D2GyHFCw%8HhCH;Y~kp^Ih)CzqdE<*N5Eb_@w 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 z%0D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­Pj3&Ur0!0h7Zml*$d-RJZaRMWz}avb}y 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

ylW2}SoWq%?Vp`Ltk>lG&+whx2Q^o@6^g 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 diff --git a/Lts/Common/LtsWidgets/resources/European-Union-Flag.png b/Lts/Common/LtsWidgets/resources/European-Union-Flag.png deleted file mode 100755 index 06afb3da11a58aa5ec034ae6f6d3461270bde36c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25659 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

Wu6ii5 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 m5+q2FAVGoz2@)hYz48Co+0y>bpQ@e!0000 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 K#EZoYzx^K^OL@lt diff --git a/Lts/Common/LtsWidgets/resources/US-Flag.png b/Lts/Common/LtsWidgets/resources/US-Flag.png deleted file mode 100755 index 3e05503d7a9f902a03602fb7b0ec59ec69d7545a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27528 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%0Dn`pXsE4JflOt5rJ4Gq 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 diff --git a/Lts/Common/LtsWidgets/resources/ack.svg b/Lts/Common/LtsWidgets/resources/ack.svg deleted file mode 100755 index 79b40b4ac..000000000 --- a/Lts/Common/LtsWidgets/resources/ack.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/ack_dark.svg b/Lts/Common/LtsWidgets/resources/ack_dark.svg deleted file mode 100755 index 8739fc398..000000000 --- a/Lts/Common/LtsWidgets/resources/ack_dark.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/beep.wav b/Lts/Common/LtsWidgets/resources/beep.wav deleted file mode 100755 index 8343a63e7a68e3e5ebceb5659c5ab99c2cc263d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26876 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 diff --git a/Lts/Common/LtsWidgets/resources/close.svg b/Lts/Common/LtsWidgets/resources/close.svg deleted file mode 100755 index 71d8a3065..000000000 --- a/Lts/Common/LtsWidgets/resources/close.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/close_dark.svg b/Lts/Common/LtsWidgets/resources/close_dark.svg deleted file mode 100755 index cc00a5706..000000000 --- a/Lts/Common/LtsWidgets/resources/close_dark.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/effectarea-enter.wav b/Lts/Common/LtsWidgets/resources/effectarea-enter.wav deleted file mode 100755 index 85d55439d62c71bb2b1f017c8b78d5bbfff3596d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97136 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 _8?_0paUSJkl& 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 diff --git a/Lts/Common/LtsWidgets/resources/entryNew.wav b/Lts/Common/LtsWidgets/resources/entryNew.wav deleted file mode 100755 index 805a6e88710b538245297d345f5c31c0fe5656b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 649808 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

3->#GjC?HfoF3v_d;$H^dl{c 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�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%0A#`1T4^?&lW#@+KFxy`kL^_axZqoa?Z8##iW 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�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%0AIG8&FULqyuncq|M#EGWXGBC-yw?ghulRE8NNwR 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 QgBKls - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/info.svg b/Lts/Common/LtsWidgets/resources/info.svg deleted file mode 100755 index 4657b2788..000000000 --- a/Lts/Common/LtsWidgets/resources/info.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js b/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js deleted file mode 100755 index f6a6a99e6..000000000 --- a/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0, -r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/\s*$/g;function Ca(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Da(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ea(a){var b=Aa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&za.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(m&&(e=oa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(la(e,"script"),Da),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=la(h),f=la(a),d=0,e=f.length;d0&&ma(g,!i&&la(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(la(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!ya.test(a)&&!ka[(ia.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Xa(a,b,c,d,e){return new Xa.prototype.init(a,b,c,d,e)}r.Tween=Xa,Xa.prototype={constructor:Xa,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Xa.propHooks[this.prop];return a&&a.get?a.get(this):Xa.propHooks._default.get(this)},run:function(a){var b,c=Xa.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Xa.propHooks._default.set(this),this}},Xa.prototype.init.prototype=Xa.prototype,Xa.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Xa.propHooks.scrollTop=Xa.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Xa.prototype.init,r.fx.step={};var Ya,Za,$a=/^(?:toggle|show|hide)$/,_a=/queueHooks$/;function ab(){Za&&(a.requestAnimationFrame(ab),r.fx.tick())}function bb(){return a.setTimeout(function(){Ya=void 0}),Ya=r.now()}function cb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=aa[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function db(a,b,c){for(var d,e=(gb.tweeners[b]||[]).concat(gb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?hb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K); -if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),hb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ib[b]||r.find.attr;ib[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ib[g],ib[g]=e,e=null!=c(a,b,d)?g:null,ib[g]=f),e}});var jb=/^(?:input|select|textarea|button)$/i,kb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):jb.test(a.nodeName)||kb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});var lb=/[\t\r\n\f]/g;function mb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,mb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,mb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,mb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=mb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(c)+" ").replace(lb," ").indexOf(b)>-1)return!0;return!1}});var nb=/\r/g,ob=/[\x20\t\r\n\f]+/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(nb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:r.trim(r.text(a)).replace(ob," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type,g=f?null:[],h=f?e+1:d.length,i=e<0?h:f?e:0;i-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ha.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,""),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("