From 855b0a61013690094d6b68af98d34978eb43ad69 Mon Sep 17 00:00:00 2001 From: berrux_dev Date: Tue, 13 Oct 2026 19:19:55 +0200 Subject: [PATCH] Remove modules not compiled by this branch's release build Lts/Common: drop the 17 submodules excluded from the RELEASE_BUILD branch of Lts/Common/CMakeLists.txt (CodeGen, LtsAppManager, LtsBotan, LtsCoordinate, LtsElevation, LtsLogin, LtsMultiMonitor, LtsPluginManager, LtsPrinting, LtsProjectionWorkstation, LtsScriptTimeline, LtsSession, LtsTimeline, LtsTimelineClassic, LtsVirtualKeyboard, LtsWidgets, TopicEditorBase). Only the 13 subdirectories actually built remain. TrainingAreaBase: drop sources of targets excluded by IF(NOT RELEASE_BUILD) / IF(BUILD_RTI) (svcTabClient, svcTabServer, svcTrainingAreaBase exe, RecordReplayPlugin customization) and the 7 plugin directories not added in the release path (AdlerJson, ApacheHlaJson, DebugJsonDir, HlaJson, Nmea, UAS, WlsTetra). Only tabTelegrams, svcTabCommon and the TetraJson plugin are compiled. Cross-checked: no kept compiled source includes headers from the removed modules (GuiPluginInterface - the only LtsWidgets consumer - is itself NOT RELEASE_BUILD-guarded). --- .../Lts/Common/CodeGen/CMakeLists.txt | 173 - .../CodeGen/CppTopicGeneratorGrantlee.cpp | 330 -- .../CodeGen/CppTopicGeneratorGrantlee.h | 41 - .../Common/CodeGen/DaoGeneratorGrantlee.cpp | 62 - .../Lts/Common/CodeGen/DaoGeneratorGrantlee.h | 30 - .../CodeGen/DatabaseGeneratorGrantlee.cpp | 59 - .../CodeGen/DatabaseGeneratorGrantlee.h | 27 - .../Common/CodeGen/GeneratorGrantleeBase.cpp | 200 -- .../Common/CodeGen/GeneratorGrantleeBase.h | 53 - .../Lts/Common/CodeGen/GrantleeParserSmoke.h | 20 - .../Lts/Common/CodeGen/GrantleeWrappers.cpp | 203 -- .../Lts/Common/CodeGen/GrantleeWrappers.h | 932 ----- .../Lts/Common/CodeGen/README.txt | 37 - .../Lts/Common/CodeGen/SqlGenerator.cpp | 892 ----- .../Lts/Common/CodeGen/SqlGenerator.h | 198 -- .../Lts/Common/CodeGen/StructBuilder.cpp | 1371 -------- .../Lts/Common/CodeGen/StructBuilder.h | 385 --- .../Lts/Common/CodeGen/XmlReader.cpp | 893 ----- .../Lts/Common/CodeGen/XmlReader.h | 459 --- .../codegen_simple.cpython-314.pyc | Bin 32703 -> 0 bytes .../Lts/Common/CodeGen/codegen_export.h | 17 - .../Lts/Common/CodeGen/codegen_log4cxx.props | 101 - .../Lts/Common/CodeGen/codegen_simple.py | 498 --- .../Lts/Common/CodeGen/ea_export_script.txt | 1465 -------- .../Lts/Common/CodeGen/main_grantlee.cpp | 167 - .../Lts/Common/CodeGen/main_grantlee_gui.cpp | 147 - .../Lts/Common/CodeGen/main_grantlee_gui.h | 42 - .../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 - .../Common/CodeGen/svc_db_impl.cpp.grantlee | 732 ---- .../Lts/Common/CodeGen/svc_db_impl.h.grantlee | 102 - .../CodeGen/svc_db_structs.cpp.grantlee | 112 - .../Common/CodeGen/svc_db_structs.h.grantlee | 87 - .../Lts/Common/CodeGen/svc_dtc.h.grantlee | 91 - .../Common/CodeGen/svc_dtc_impl.cpp.grantlee | 309 -- .../Common/CodeGen/svc_dtc_impl.h.grantlee | 109 - .../Lts/Common/CodeGen/svc_dts.h.grantlee | 69 - .../Common/CodeGen/svc_dts_impl.cpp.grantlee | 102 - .../Common/CodeGen/svc_dts_impl.h.grantlee | 58 - .../Lts/Common/CodeGen/svc_enums.idl.grantlee | 32 - .../Lts/Common/CodeGen/svc_ftc.h.grantlee | 78 - .../Common/CodeGen/svc_ftc_impl.cpp.grantlee | 254 -- .../Common/CodeGen/svc_ftc_impl.h.grantlee | 81 - .../Lts/Common/CodeGen/svc_fts.h.grantlee | 90 - .../Common/CodeGen/svc_fts_impl.cpp.grantlee | 114 - .../Common/CodeGen/svc_fts_impl.h.grantlee | 67 - .../Lts/Common/CodeGen/svc_json.cpp.grantlee | 172 - .../Lts/Common/CodeGen/svc_json.h.grantlee | 50 - .../svc_record_replay_plugin.cpp.grantlee | 919 ----- ...ay_plugin.cpp.grantlee_possible_extensions | 637 ---- .../svc_record_replay_plugin.h.grantlee | 128 - .../svc_topic_type_plugin.cpp.grantlee | 102 - .../CodeGen/svc_topic_type_plugin.h.grantlee | 41 - .../CodeGen/svc_translation.cpp.grantlee | 43 - .../Common/CodeGen/svc_translation.h.grantlee | 32 - .../CodeGen/tests/GrantleeParserSmoke.cpp | 70 - .../AppChooser/CustomViews/app.cpp | 32 - .../AppChooser/CustomViews/app.h | 28 - .../AppChooser/CustomViews/app.ui | 108 - .../LtsAppManager/AppChooser/app_chooser.cpp | 80 - .../LtsAppManager/AppChooser/app_chooser.h | 45 - .../LtsAppManager/AppChooser/app_chooser.ui | 92 - .../AppChooser/app_chooser_model.cpp | 48 - .../AppChooser/app_chooser_model.h | 25 - .../Lts/Common/LtsAppManager/CMakeLists.txt | 71 - .../Common/LtsAppManager/lts_app_manager.cpp | 963 ------ .../Common/LtsAppManager/lts_app_manager.h | 136 - .../Lts/Common/LtsBotan/CMakeLists.txt | 12 - .../Lts/Common/LtsCoordinate/CMakeLists.txt | 30 - .../UnitTest_Coordinate/CMakeLists.txt | 38 - .../lts_coordinate_unit_test.cpp | 868 ----- .../lts_coordinate_unit_test.h | 60 - .../Common/LtsCoordinate/lts_coord_vector.cpp | 69 - .../Common/LtsCoordinate/lts_coord_vector.h | 62 - .../Common/LtsCoordinate/lts_coordinate.cpp | 143 - .../Lts/Common/LtsCoordinate/lts_coordinate.h | 297 -- .../Lts/Common/LtsElevation/CMakeLists.txt | 52 - .../UnitTest_Elevation/CMakeLists.txt | 33 - .../elevation_unit_test.cpp | 198 -- .../UnitTest_Elevation/elevation_unit_test.h | 38 - .../Lts/Common/LtsElevation/logger.cpp | 30 - .../Lts/Common/LtsElevation/logger.h | 17 - .../LtsElevation/lts_elevation_manager.cpp | 587 ---- .../LtsElevation/lts_elevation_manager.h | 113 - .../Lts/Common/LtsLogin/CMakeLists.txt | 44 - .../Common/LtsLogin/logout_info_dialog.cpp | 92 - .../Lts/Common/LtsLogin/logout_info_dialog.h | 44 - .../Lts/Common/LtsLogin/logout_info_dialog.ui | 76 - .../Lts/Common/LtsLogin/lts_login.cpp | 282 -- .../Lts/Common/LtsLogin/lts_login.h | 71 - .../Lts/Common/LtsMultiMonitor/CMakeLists.txt | 68 - .../LtsMultiMonitor/lts_dummy_widget.cpp | 35 - .../Common/LtsMultiMonitor/lts_dummy_widget.h | 39 - .../LtsMultiMonitor/lts_magic_corner.cpp | 130 - .../Common/LtsMultiMonitor/lts_magic_corner.h | 52 - .../LtsMultiMonitor/lts_multi_monitor.cpp | 602 ---- .../LtsMultiMonitor/lts_multi_monitor.h | 95 - .../lts_multi_monitor_export.h | 17 - .../lts_multi_monitor_selector_base.cpp | 343 -- .../lts_multi_monitor_selector_base.h | 72 - .../lts_multi_monitor_selector_normal.cpp | 81 - .../lts_multi_monitor_selector_normal.h | 36 - .../lts_multi_monitor_selector_wide.cpp | 280 -- .../lts_multi_monitor_selector_wide.h | 35 - .../LtsMultiMonitor/lts_screen_base.cpp | 52 - .../Common/LtsMultiMonitor/lts_screen_base.h | 79 - .../LtsMultiMonitor/lts_screen_container.cpp | 416 --- .../LtsMultiMonitor/lts_screen_container.h | 65 - .../LtsMultiMonitor/lts_screen_normal.cpp | 135 - .../LtsMultiMonitor/lts_screen_normal.h | 63 - .../LtsMultiMonitor/lts_screen_wide.cpp | 199 -- .../Common/LtsMultiMonitor/lts_screen_wide.h | 65 - .../LtsMultiMonitor/ltsmultimonitor_export.h | 2 - .../Common/LtsPluginManager/CMakeLists.txt | 77 - .../Ui/lts_plugin_manager_dialog.cpp | 93 - .../Ui/lts_plugin_manager_dialog.h | 37 - .../Ui/lts_plugin_manager_dialog.ui | 71 - .../Ui/lts_plugin_manager_widget.cpp | 117 - .../Ui/lts_plugin_manager_widget.h | 41 - .../Ui/lts_plugin_manager_widget.ui | 196 -- .../LtsPluginManager/lts_plugin_loader.cpp | 468 --- .../LtsPluginManager/lts_plugin_loader.h | 94 - .../LtsPluginManager/lts_plugin_manager.cpp | 191 - .../LtsPluginManager/lts_plugin_manager.h | 140 - .../lts_plugin_manager_export.h | 17 - .../Lts/Common/LtsPrinting/CMakeLists.txt | 42 - .../Lts/Common/LtsPrinting/printing.cpp | 386 --- .../Lts/Common/LtsPrinting/printing.h | 66 - .../Lts/Common/LtsPrinting/printing_base.cpp | 1126 ------ .../Lts/Common/LtsPrinting/printing_base.h | 229 -- .../LtsProjectionWorkstation/CMakeLists.txt | 57 - .../WebSocket/client_message.cpp | 56 - .../WebSocket/client_message.h | 68 - .../WebSocket/json_keys.h | 100 - .../WebSocket/message_factory.cpp | 53 - .../WebSocket/message_factory.h | 29 - .../WebSocket/messages.cpp | 205 -- .../WebSocket/messages.h | 209 -- .../WebSocket/reply_message.cpp | 20 - .../WebSocket/reply_message.h | 23 - .../LtsProjectionWorkstation/logger.cpp | 30 - .../Common/LtsProjectionWorkstation/logger.h | 19 - .../lts_projection_workstation.cpp | 226 -- .../lts_projection_workstation.h | 101 - .../lts_projection_workstation_export.h | 17 - .../vnc_server_manager.cpp | 179 - .../vnc_server_manager.h | 67 - .../Common/LtsScriptTimeline/CMakeLists.txt | 84 - .../LtsScriptTimeline/Test/CMakeLists.txt | 24 - .../Common/LtsScriptTimeline/Test/main.cpp | 12 - .../LtsScriptTimeline/Test/mainwindow.cpp | 86 - .../LtsScriptTimeline/Test/mainwindow.h | 33 - .../LtsScriptTimeline/Test/mainwindow.ui | 564 --- .../Ui/filter_track_widget.ui | 126 - .../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 | 163 - .../filter_background_widget.h | 65 - .../LtsScriptTimeline/filter_track_widget.cpp | 107 - .../LtsScriptTimeline/filter_track_widget.h | 68 - .../Common/LtsScriptTimeline/indicator.cpp | 73 - .../Lts/Common/LtsScriptTimeline/indicator.h | 41 - .../ltsscripttimeline_export.h | 12 - .../Common/LtsScriptTimeline/newsrc.tar.xz | Bin 52292 -> 0 bytes .../LtsScriptTimeline/script_timeline.cpp | 1050 ------ .../LtsScriptTimeline/script_timeline.h | 136 - .../temporary_pointer_vector.h | 43 - .../Lts/Common/LtsScriptTimeline/tools.cpp | 75 - .../Lts/Common/LtsScriptTimeline/tools.h | 29 - .../track_content_widget.cpp | 1430 -------- .../LtsScriptTimeline/track_content_widget.h | 243 -- .../LtsScriptTimeline/track_drag_handler.cpp | 164 - .../LtsScriptTimeline/track_drag_handler.h | 32 - .../Common/LtsScriptTimeline/track_entry.h | 131 - .../track_entry_painters.cpp | 328 -- .../LtsScriptTimeline/track_entry_painters.h | 89 - .../track_entry_preview_widget.cpp | 89 - .../track_entry_preview_widget.h | 39 - .../track_entry_previewer.cpp | 76 - .../LtsScriptTimeline/track_entry_previewer.h | 37 - .../Common/LtsScriptTimeline/track_header.cpp | 158 - .../Common/LtsScriptTimeline/track_header.h | 50 - .../track_resize_handler.cpp | 183 - .../LtsScriptTimeline/track_resize_handler.h | 38 - .../track_select_handler.cpp | 169 - .../LtsScriptTimeline/track_select_handler.h | 33 - .../Common/LtsScriptTimeline/track_widget.cpp | 476 --- .../Common/LtsScriptTimeline/track_widget.h | 90 - .../Lts/Common/LtsSession/CMakeLists.txt | 156 - .../Lts/Common/LtsSession/lts_session.cpp | 1843 ---------- .../lts_session.cpp.backup_session_1172 | 1822 ---------- .../Lts/Common/LtsSession/lts_session.h | 497 --- .../Common/LtsSession/lts_session_data.cpp | 92 - .../Lts/Common/LtsSession/lts_session_data.h | 277 -- .../LtsSession/lts_session_data_dds.cpp | 479 --- .../Common/LtsSession/lts_session_data_dds.h | 356 -- .../LtsSession/lts_session_lock_handler.cpp | 95 - .../LtsSession/lts_session_lock_handler.h | 106 - .../Common/LtsSession/lts_session_manager.cpp | 238 -- .../Common/LtsSession/lts_session_manager.h | 126 - .../Lts/Common/LtsTimeline/CMakeLists.txt | 94 - .../Lts/Common/LtsTimeline/item.cpp | 36 - .../Lts/Common/LtsTimeline/item.h | 125 - .../Lts/Common/LtsTimeline/item_delegate.cpp | 171 - .../Lts/Common/LtsTimeline/item_delegate.h | 40 - .../Common/LtsTimeline/log4cxx_timeline.props | 66 - .../Lts/Common/LtsTimeline/main.cpp | 218 -- .../LtsTimeline/timeline_header_view.cpp | 636 ---- .../Common/LtsTimeline/timeline_header_view.h | 302 -- .../timeline_header_view_mouse_handler.cpp | 147 - .../timeline_header_view_mouse_handler.h | 52 - .../Lts/Common/LtsTimeline/timeline_model.cpp | 709 ---- .../Lts/Common/LtsTimeline/timeline_model.h | 300 -- .../Lts/Common/LtsTimeline/timeline_utils.cpp | 334 -- .../Lts/Common/LtsTimeline/timeline_utils.h | 86 - .../Lts/Common/LtsTimeline/timeline_view.cpp | 634 ---- .../Lts/Common/LtsTimeline/timeline_view.h | 261 -- .../timeline_view_drag_handler.cpp | 515 --- .../LtsTimeline/timeline_view_drag_handler.h | 95 - .../timeline_view_resize_handler.cpp | 287 -- .../timeline_view_resize_handler.h | 42 - .../timeline_view_selection_handler.cpp | 334 -- .../timeline_view_selection_handler.h | 47 - .../timeline_view_track_handler.cpp | 98 - .../LtsTimeline/timeline_view_track_handler.h | 42 - .../Common/LtsTimeline/timeline_widget.cpp | 369 -- .../Lts/Common/LtsTimeline/timeline_widget.h | 107 - .../Lts/Common/LtsTimeline/timeline_widget.ui | 434 --- .../Lts/Common/LtsTimeline/track.cpp | 657 ---- .../Lts/Common/LtsTimeline/track.h | 286 -- .../AbstractTimelineModel.cpp | 230 -- .../AbstractTimelineModel.h | 82 - .../Common/LtsTimelineClassic/CMakeLists.txt | 96 - .../Lts/Common/LtsTimelineClassic/Event.cpp | 162 - .../Lts/Common/LtsTimelineClassic/Event.h | 199 -- .../LtsTimelineClassic/EventDelegate.cpp | 55 - .../Common/LtsTimelineClassic/EventDelegate.h | 40 - .../LtsTimelineClassic/EventGraphic.cpp | 154 - .../Common/LtsTimelineClassic/EventGraphic.h | 46 - .../Lts/Common/LtsTimelineClassic/EventList.h | 16 - .../Common/LtsTimelineClassic/EventTools.cpp | 399 --- .../Common/LtsTimelineClassic/EventTools.h | 108 - .../Lts/Common/LtsTimelineClassic/Item.cpp | 337 -- .../Lts/Common/LtsTimelineClassic/Item.h | 84 - .../Lts/Common/LtsTimelineClassic/LineMap.h | 12 - .../PassiveEventContainer.cpp | 99 - .../PassiveEventContainer.h | 40 - .../Lts/Common/LtsTimelineClassic/Range.h | 18 - .../LtsTimelineClassic/TestTimelineModel.cpp | 33 - .../LtsTimelineClassic/TestTimelineModel.h | 34 - .../LtsTimelineClassic/TimelineHeaderView.cpp | 138 - .../LtsTimelineClassic/TimelineHeaderView.h | 47 - .../LtsTimelineClassic/TimelineIndicator.cpp | 453 --- .../LtsTimelineClassic/TimelineIndicator.h | 94 - .../LtsTimelineClassic/TimelineView.cpp | 321 -- .../Common/LtsTimelineClassic/TimelineView.h | 72 - .../LtsTimelineClassic/TimelineWidget.cpp | 276 -- .../LtsTimelineClassic/TimelineWidget.h | 86 - .../LtsTimelineClassic/Ui/TimelineWidgetUi.ui | 167 - .../LtsTimelineClassic/log4cxx_timeline.props | 66 - .../ltstimelineclassic_export.h | 8 - .../Lts/Common/LtsTimelineClassic/main.cpp | 71 - .../Common/LtsVirtualKeyboard/CMakeLists.txt | 55 - .../LtsVirtualKeyboard/LtsKeyPushButton.cpp | 145 - .../LtsVirtualKeyboard/LtsKeyPushButton.h | 82 - .../LtsVirtualKeyboard/LtsVirtualKeyboard.cpp | 886 ----- .../LtsVirtualKeyboard/LtsVirtualKeyboard.h | 111 - .../Lts/Common/LtsWidgets/CMakeLists.txt | 216 -- .../Lts/Common/LtsWidgets/CMakeLists_rc.txt | 208 -- .../LtsImageCropper/lts_image_cropper.cpp | 137 - .../LtsImageCropper/lts_image_cropper.h | 108 - .../lts_abstract_sequence_widget.cpp | 129 - .../LtsWidgets/lts_abstract_sequence_widget.h | 126 - .../LtsWidgets/lts_abstract_table_model.h | 296 -- .../Lts/Common/LtsWidgets/lts_angle_edit.cpp | 281 -- .../Lts/Common/LtsWidgets/lts_angle_edit.h | 77 - .../Lts/Common/LtsWidgets/lts_angle_edit.ui | 49 - .../LtsWidgets/lts_background_widget.cpp | 144 - .../Common/LtsWidgets/lts_background_widget.h | 60 - .../Lts/Common/LtsWidgets/lts_base.h | 288 -- .../Lts/Common/LtsWidgets/lts_base_layout.ui | 645 ---- .../LtsWidgets/lts_base_layout_widget.cpp | 1839 ---------- .../LtsWidgets/lts_base_layout_widget.h | 450 --- .../Common/LtsWidgets/lts_bitfield_widget.cpp | 135 - .../Common/LtsWidgets/lts_bitfield_widget.h | 141 - .../LtsWidgets/lts_checkable_table_model.h | 137 - .../lts_column_filter_proxy_model.cpp | 165 - .../lts_column_filter_proxy_model.h | 129 - .../LtsWidgets/lts_column_organiser_widget.ui | 75 - .../Lts/Common/LtsWidgets/lts_combo_box.cpp | 125 - .../Lts/Common/LtsWidgets/lts_combo_box.h | 76 - .../Common/LtsWidgets/lts_date_time_edit.cpp | 1793 ---------- .../Common/LtsWidgets/lts_date_time_edit.h | 220 -- .../LtsWidgets/lts_date_time_edit_p.cpp | 118 - .../Common/LtsWidgets/lts_date_time_edit_p.h | 43 - .../LtsWidgets/lts_date_time_edit_rc.cpp | 1799 ---------- .../Common/LtsWidgets/lts_date_time_edit_rc.h | 213 -- .../LtsWidgets/lts_date_time_edit_touch.cpp | 396 --- .../LtsWidgets/lts_date_time_edit_touch.h | 147 - .../LtsWidgets/lts_date_time_edit_touch.ui | 181 - .../Lts/Common/LtsWidgets/lts_dialog.cpp | 819 ----- .../Lts/Common/LtsWidgets/lts_dialog.h | 544 --- .../LtsWidgets/lts_dialog_button_box.cpp | 224 -- .../Common/LtsWidgets/lts_dialog_button_box.h | 252 -- .../LtsWidgets/lts_dialog_button_box.ui | 326 -- .../Common/LtsWidgets/lts_dialog_helper.cpp | 180 - .../Lts/Common/LtsWidgets/lts_dialog_helper.h | 60 - .../Common/LtsWidgets/lts_dialog_helper.ui | 70 - .../Lts/Common/LtsWidgets/lts_drag_icon.cpp | 494 --- .../Lts/Common/LtsWidgets/lts_drag_icon.h | 339 -- .../Lts/Common/LtsWidgets/lts_drop_handler.h | 141 - .../Common/LtsWidgets/lts_elided_label.cpp | 66 - .../Lts/Common/LtsWidgets/lts_elided_label.h | 42 - .../Lts/Common/LtsWidgets/lts_events.cpp | 63 - .../Lts/Common/LtsWidgets/lts_events.h | 99 - .../LtsWidgets/lts_frameless_dialog.cpp | 159 - .../Common/LtsWidgets/lts_frameless_dialog.h | 61 - .../LtsWidgets/lts_global_wait_dialog.cpp | 226 -- .../LtsWidgets/lts_global_wait_dialog.h | 77 - .../Lts/Common/LtsWidgets/lts_grid_layout.cpp | 135 - .../Lts/Common/LtsWidgets/lts_grid_layout.h | 92 - .../LtsWidgets/lts_height_plot_widget.cpp | 626 ---- .../LtsWidgets/lts_height_plot_widget.h | 97 - .../Common/LtsWidgets/lts_image_editor.cpp | 123 - .../Lts/Common/LtsWidgets/lts_image_editor.h | 74 - .../Lts/Common/LtsWidgets/lts_info_dialog.cpp | 88 - .../Lts/Common/LtsWidgets/lts_info_dialog.h | 35 - .../Lts/Common/LtsWidgets/lts_info_dialog.ui | 135 - .../Lts/Common/LtsWidgets/lts_led_widget.cpp | 154 - .../Lts/Common/LtsWidgets/lts_led_widget.h | 178 - .../Common/LtsWidgets/lts_message_dialog.cpp | 133 - .../Common/LtsWidgets/lts_message_dialog.h | 104 - .../LtsWidgets/lts_message_dialog_helper.cpp | 155 - .../LtsWidgets/lts_message_dialog_helper.h | 55 - .../LtsWidgets/lts_msec_date_time_edit.cpp | 114 - .../LtsWidgets/lts_msec_date_time_edit.h | 41 - .../LtsWidgets/lts_multi_range_spinbox.cpp | 337 -- .../LtsWidgets/lts_multi_range_spinbox.h | 60 - .../LtsWidgets/lts_notification_box.cpp | 197 -- .../Common/LtsWidgets/lts_notification_box.h | 50 - .../Common/LtsWidgets/lts_notification_box.ui | 182 - .../Common/LtsWidgets/lts_operator_menu.cpp | 121 - .../Lts/Common/LtsWidgets/lts_operator_menu.h | 49 - .../Common/LtsWidgets/lts_operator_menu.ui | 247 -- .../Common/LtsWidgets/lts_overlay_loading.cpp | 54 - .../Common/LtsWidgets/lts_overlay_loading.h | 48 - .../Common/LtsWidgets/lts_overlay_loading.ui | 76 - .../Common/LtsWidgets/lts_picture_view.cpp | 345 -- .../Lts/Common/LtsWidgets/lts_picture_view.h | 66 - .../Common/LtsWidgets/lts_profile_button.cpp | 140 - .../Common/LtsWidgets/lts_profile_button.h | 70 - .../LtsWidgets/lts_progress_dialog_helper.cpp | 154 - .../LtsWidgets/lts_progress_dialog_helper.h | 82 - .../Common/LtsWidgets/lts_range_slider.cpp | 848 ----- .../Lts/Common/LtsWidgets/lts_range_slider.h | 219 -- .../Common/LtsWidgets/lts_selection_popup.cpp | 219 -- .../Common/LtsWidgets/lts_selection_popup.h | 112 - .../Common/LtsWidgets/lts_selection_popup.ui | 232 -- .../Lts/Common/LtsWidgets/lts_spinbox.cpp | 180 - .../Lts/Common/LtsWidgets/lts_spinbox.h | 67 - .../Lts/Common/LtsWidgets/lts_splash.cpp | 81 - .../Lts/Common/LtsWidgets/lts_splash.h | 28 - .../Lts/Common/LtsWidgets/lts_splitter.cpp | 62 - .../Lts/Common/LtsWidgets/lts_splitter.h | 42 - .../Common/LtsWidgets/lts_splitter_handle.cpp | 92 - .../Common/LtsWidgets/lts_splitter_handle.h | 44 - .../Lts/Common/LtsWidgets/lts_tab_bar.cpp | 125 - .../Lts/Common/LtsWidgets/lts_tab_bar.h | 51 - .../LtsWidgets/lts_tab_sequence_widget.cpp | 65 - .../LtsWidgets/lts_tab_sequence_widget.h | 43 - .../Lts/Common/LtsWidgets/lts_tab_widget.cpp | 907 ----- .../Lts/Common/LtsWidgets/lts_tab_widget.h | 244 -- .../LtsWidgets/lts_text_edit_dialog.cpp | 143 - .../Common/LtsWidgets/lts_text_edit_dialog.h | 61 - .../Lts/Common/LtsWidgets/lts_text_editor.cpp | 389 --- .../Lts/Common/LtsWidgets/lts_text_editor.h | 61 - .../Lts/Common/LtsWidgets/lts_text_editor.ui | 598 ---- .../Common/LtsWidgets/lts_text_editor2.cpp | 926 ----- .../Lts/Common/LtsWidgets/lts_text_editor2.h | 140 - .../Lts/Common/LtsWidgets/lts_tool_bar.cpp | 36 - .../Lts/Common/LtsWidgets/lts_tool_bar.h | 32 - .../Common/LtsWidgets/lts_tool_bar_area.cpp | 390 --- .../Lts/Common/LtsWidgets/lts_tool_bar_area.h | 120 - .../Common/LtsWidgets/lts_tool_bar_area.ui | 38 - .../Lts/Common/LtsWidgets/lts_tool_box.cpp | 90 - .../Lts/Common/LtsWidgets/lts_tool_box.h | 36 - .../Lts/Common/LtsWidgets/lts_wait_dialog.cpp | 17 - .../Lts/Common/LtsWidgets/lts_wait_dialog.h | 21 - .../Common/LtsWidgets/lts_widgets_export.h | 21 - .../Lts/Common/LtsWidgets/ltswidgets_export.h | 10 - .../Common/LtsWidgets/ltswidgetsplugin.cpp | 1201 ------- .../Common/LtsWidgets/ltswidgetsplugin.json | 5 - .../Common/LtsWidgets/requirement_widget.cpp | 142 - .../Common/LtsWidgets/requirement_widget.h | 62 - .../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 .../Common/LtsWidgets/resources/US-Flag.png | Bin 27528 -> 0 bytes .../Lts/Common/LtsWidgets/resources/ack.svg | 56 - .../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 .../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 -------- .../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 .../Common/LtsWidgets/resources/rde_logo.ico | Bin 106291 -> 0 bytes .../Common/LtsWidgets/resources/rde_logo.png | Bin 3649 -> 0 bytes .../resources/rde_splitter_left.svg | 1 - .../Common/LtsWidgets/resources/scripts.js | 112 - .../Common/LtsWidgets/resources/start.html | 600 ---- .../resources/stylesheet-branch-closed.png | Bin 361 -> 0 bytes .../resources/stylesheet-branch-open.png | Bin 345 -> 0 bytes .../Common/LtsWidgets/resources/success.svg | 66 - .../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 | 227 -- .../LtsWidgets/tests/lts_picture_editing.h | 66 - .../LtsWidgets/tests/lts_picture_editing.ui | 363 -- .../tests/lts_picture_editing_main.cpp | 45 - .../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 | 160 - .../Lts/Common/LtsWidgets/ui_requirement.h | 98 - .../Lts/Common/TopicEditorBase/CMakeLists.txt | 61 - .../TopicEditorBase/topic_editor_base.cpp | 100 - .../TopicEditorBase/topic_editor_base.h | 161 - .../TopicEditorBase/topic_editor_interface.h | 28 - .../TopicEditorBase/topic_editor_utils.cpp | 296 -- .../TopicEditorBase/topic_editor_utils.h | 83 - .../TopicEditorBase/topic_preview_widget.cpp | 103 - .../TopicEditorBase/topic_preview_widget.h | 46 - .../TopicEditorBase/topic_preview_widget.ui | 109 - .../TopicEditorBase/topic_previewer.cpp | 71 - .../Common/TopicEditorBase/topic_previewer.h | 39 - .../Plugins/AdlerJson/CMakeLists.txt | 118 - .../Plugins/AdlerJson/ICD_UFom_HLA.md | 79 - .../Plugins/AdlerJson/JSON_SCHEMA/EX-AQ.json | 23 - .../AdlerJson/JSON_SCHEMA/EX-AQ.struct | 92 - .../JSON_SCHEMA/EX-Erkundungsergebnisse.json | 56 - .../EX-Erkundungsergebnisse.struct | 191 - .../Plugins/AdlerJson/JSON_SCHEMA/EX-FH.json | 30 - .../Plugins/AdlerJson/JSON_SCHEMA/EX-FH.stuct | 120 - .../AdlerJson/JSON_SCHEMA/EX-FQ-BA-BB-FK.json | 100 - .../JSON_SCHEMA/EX-FQ-BA-BB-FK.struct | 361 -- .../AdlerJson/JSON_SCHEMA/EX-FQ-FN.json | 25 - .../AdlerJson/JSON_SCHEMA/EX-FQ-FN.struct | 100 - .../JSON_SCHEMA/EX-Flugbahnberechnung.json | 45 - .../JSON_SCHEMA/EX-Flugbahnberechnung.struct | 180 - .../JSON_SCHEMA/EX-Flugbahndaten.json | 63 - .../JSON_SCHEMA/EX-Flugbahndaten.struct | 223 -- .../AdlerJson/JSON_SCHEMA/EX-HQ-G.json | 58 - .../AdlerJson/JSON_SCHEMA/EX-HQ-G.struct | 211 -- .../AdlerJson/JSON_SCHEMA/EX-HQ-K.json | 63 - .../AdlerJson/JSON_SCHEMA/EX-HQ-K.struct | 231 -- .../AdlerJson/JSON_SCHEMA/EX-SQ-FS.json | 71 - .../AdlerJson/JSON_SCHEMA/EX-SQ-FS.struct | 222 -- .../AdlerJson/JSON_SCHEMA/EX-SQ-LP.txt | 44 - .../AdlerJson/JSON_SCHEMA/EX-SQ-WS.json | 56 - .../AdlerJson/JSON_SCHEMA/EX-SQ-WS.struct | 195 -- .../AdlerJson/JSON_SCHEMA/EX-Status.json | 23 - .../AdlerJson/JSON_SCHEMA/EX-Status.struct | 92 - .../Plugins/AdlerJson/JSON_SCHEMA/readme.txt | 40 - .../JSON_SCHEMA/tabModuleTestAdler.result | 530 --- .../AdlerJson/ModuleTest/CMakeLists.txt | 85 - .../AdlerJson/ModuleTest/ex_abmeldung.cpp | 66 - .../AdlerJson/ModuleTest/ex_abmeldung.h | 24 - .../ModuleTest/ex_anmeldebestaetigung.cpp | 38 - .../ModuleTest/ex_anmeldebestaetigung.h | 27 - .../AdlerJson/ModuleTest/ex_anmeldung.cpp | 72 - .../AdlerJson/ModuleTest/ex_anmeldung.h | 26 - .../AdlerJson/ModuleTest/ex_aq_freitext.cpp | 46 - .../AdlerJson/ModuleTest/ex_aq_freitext.h | 26 - .../ModuleTest/ex_erkundungsanfrage.cpp | 39 - .../ModuleTest/ex_erkundungsanfrage.h | 26 - .../ModuleTest/ex_erkundungsergebnisse.cpp | 59 - .../ModuleTest/ex_erkundungsergebnisse.h | 26 - .../AdlerJson/ModuleTest/ex_fh_feuerhalt.cpp | 46 - .../AdlerJson/ModuleTest/ex_fh_feuerhalt.h | 26 - .../ModuleTest/ex_flugbahnberechnung.cpp | 44 - .../ModuleTest/ex_flugbahnberechnung.h | 26 - .../AdlerJson/ModuleTest/ex_flugbahndaten.cpp | 44 - .../AdlerJson/ModuleTest/ex_flugbahndaten.h | 26 - .../AdlerJson/ModuleTest/ex_fq_abgefeuert.cpp | 47 - .../AdlerJson/ModuleTest/ex_fq_abgefeuert.h | 26 - .../ModuleTest/ex_fq_feuerbereit.cpp | 47 - .../AdlerJson/ModuleTest/ex_fq_feuerbereit.h | 26 - .../AdlerJson/ModuleTest/ex_fq_feuerkampf.cpp | 38 - .../AdlerJson/ModuleTest/ex_fq_feuerkampf.h | 26 - .../AdlerJson/ModuleTest/ex_fq_feuern.cpp | 47 - .../AdlerJson/ModuleTest/ex_fq_feuern.h | 26 - .../ModuleTest/ex_fq_vollzugsmeldung.cpp | 48 - .../ModuleTest/ex_fq_vollzugsmeldung.h | 26 - .../ModuleTest/ex_fq_wirkfeldplanung.cpp | 59 - .../ModuleTest/ex_fq_wirkfeldplanung.h | 26 - .../ModuleTest/ex_hq_fuehrungslinien.cpp | 46 - .../ModuleTest/ex_hq_fuehrungslinien.h | 26 - .../ex_hq_koordinierungsmassnahmen.cpp | 46 - .../ex_hq_koordinierungsmassnahmen.h | 26 - .../ModuleTest/ex_mq_manuelle_quittung.cpp | 44 - .../ModuleTest/ex_mq_manuelle_quittung.h | 26 - .../ModuleTest/ex_sq_stellungsmeldung_fs.cpp | 46 - .../ModuleTest/ex_sq_stellungsmeldung_fs.h | 26 - .../ModuleTest/ex_sq_stellungsmeldung_lp.cpp | 46 - .../ModuleTest/ex_sq_stellungsmeldung_lp.h | 26 - .../ModuleTest/ex_sq_stellungsmeldung_ws.cpp | 46 - .../ModuleTest/ex_sq_stellungsmeldung_ws.h | 26 - .../ModuleTest/ex_sq_stellungswechsel.cpp | 47 - .../ModuleTest/ex_sq_stellungswechsel.h | 26 - .../AdlerJson/ModuleTest/ex_status.cpp | 70 - .../Plugins/AdlerJson/ModuleTest/ex_status.h | 23 - .../ModuleTest/ex_tq_technische_quittung.cpp | 46 - .../ModuleTest/ex_tq_technische_quittung.h | 26 - .../AdlerJson/ModuleTest/msgcompares.cpp | 328 -- .../AdlerJson/ModuleTest/msgcompares.h | 38 - .../ModuleTest/test_telegram_decoding.cpp | 63 - .../AdlerJson/Tests/ddsview/MountMount.spl | 129 - .../AdlerJson/Tests/ddsview/MountUnmount.spl | 66 - .../Plugins/AdlerJson/adler_export.h | 17 - .../Plugins/AdlerJson/aes_handler.cpp | 1193 ------- .../Plugins/AdlerJson/aes_handler.h | 106 - .../Plugins/AdlerJson/asm_handler.cpp | 57 - .../Plugins/AdlerJson/asm_handler.h | 46 - .../Plugins/AdlerJson/flm_handler.cpp | 272 -- .../Plugins/AdlerJson/flm_handler.h | 62 - .../Plugins/AdlerJson/generatetelegrams.cpp | 845 ----- .../Plugins/AdlerJson/generatetelegrams.h | 54 - .../AdlerJson/interface_controller.cpp | 6 - .../Plugins/AdlerJson/interface_controller.h | 15 - .../Plugins/AdlerJson/json_dispatcher.cpp | 1004 ------ .../Plugins/AdlerJson/json_dispatcher.h | 160 - .../Plugins/AdlerJson/json_handler_rx.cpp | 349 -- .../Plugins/AdlerJson/json_handler_rx.h | 53 - .../Plugins/AdlerJson/json_handler_tx.cpp | 399 --- .../Plugins/AdlerJson/json_handler_tx.h | 101 - .../Plugins/AdlerJson/logger.cpp | 32 - .../Plugins/AdlerJson/logger.h | 17 - .../Plugins/AdlerJson/main_dispatcher.cpp | 110 - .../Plugins/AdlerJson/main_dispatcher.h | 86 - .../Plugins/AdlerJson/pdm_handler.cpp | 80 - .../Plugins/AdlerJson/pdm_handler.h | 39 - .../Plugins/AdlerJson/plugin.cpp | 20 - .../Plugins/AdlerJson/plugin.h | 27 - .../Plugins/AdlerJson/ppm_handler.cpp | 1092 ------ .../Plugins/AdlerJson/ppm_handler.h | 106 - .../Plugins/AdlerJson/saa_interface.cpp | 264 -- .../Plugins/AdlerJson/saa_interface.h | 99 - .../Plugins/AdlerJson/settings.cpp | 57 - .../Plugins/AdlerJson/settings.h | 28 - .../Plugins/AdlerJson/telegrams.cpp | 1929 ----------- .../Plugins/AdlerJson/telegrams.h | 1596 --------- .../Plugins/AdlerJson/ws_handler.cpp | 239 -- .../Plugins/AdlerJson/ws_handler.cpp.b | 249 -- .../Plugins/AdlerJson/ws_handler.h | 78 - .../Plugins/AdlerJson/ws_interface.h | 45 - .../Plugins/ApacheHlaJson/CMakeLists.txt | 61 - .../ApacheHlaJson/ICD_apache_hla_telegrams.md | 120 - .../Plugins/ApacheHlaJson/aes_handler.cpp | 67 - .../Plugins/ApacheHlaJson/aes_handler.h | 42 - .../Plugins/ApacheHlaJson/apache_hla.qmodel | 3059 ----------------- .../Plugins/ApacheHlaJson/dds_handler.cpp | 40 - .../Plugins/ApacheHlaJson/dds_handler.h | 39 - .../Plugins/ApacheHlaJson/hla_dispatcher.cpp | 120 - .../Plugins/ApacheHlaJson/hla_dispatcher.h | 71 - .../Plugins/ApacheHlaJson/hla_handler_rx.cpp | 157 - .../Plugins/ApacheHlaJson/hla_handler_rx.h | 51 - .../Plugins/ApacheHlaJson/hla_handler_tx.cpp | 337 -- .../Plugins/ApacheHlaJson/hla_handler_tx.h | 87 - .../Plugins/ApacheHlaJson/logger.cpp | 32 - .../Plugins/ApacheHlaJson/logger.h | 17 - .../Plugins/ApacheHlaJson/main_dispatcher.cpp | 83 - .../Plugins/ApacheHlaJson/main_dispatcher.h | 85 - .../Plugins/ApacheHlaJson/main_handler_tx.cpp | 216 -- .../Plugins/ApacheHlaJson/main_handler_tx.h | 70 - .../Plugins/ApacheHlaJson/pdm_handler.cpp | 84 - .../Plugins/ApacheHlaJson/pdm_handler.h | 36 - .../Plugins/ApacheHlaJson/plugin.cpp | 24 - .../Plugins/ApacheHlaJson/plugin.h | 25 - .../Plugins/ApacheHlaJson/ppm_handler.cpp | 238 -- .../Plugins/ApacheHlaJson/ppm_handler.h | 43 - .../Plugins/DebugJsonDir/CMakeLists.txt | 41 - .../DebugJsonDir/XDR_QtHandler/CMakeLists.txt | 31 - .../XDR_QtHandler/XDR_DirHandler.cpp | 54 - .../XDR_QtHandler/XDR_DirHandler.h | 58 - .../XDR_QtHandler/XDR_DirHandler_ifc.cpp | 15 - .../XDR_QtHandler/XDR_DirHandler_ifc.h | 23 - .../XDR_QtHandler/XDR_DirHandler_linux.cpp | 195 -- .../XDR_QtHandler/XDR_DirHandler_linux.h | 25 - .../XDR_QtHandler/XDR_DirHandler_win.cpp | 74 - .../XDR_QtHandler/XDR_DirHandler_win.h | 19 - .../XDR_QtHandler/XDR_QtHandler_export.h | 17 - .../DebugJsonDir/convert_to_json_file.cpp | 272 -- .../DebugJsonDir/convert_to_json_file.h | 75 - .../DebugJsonDir/directory_cleaner.cpp | 157 - .../Plugins/DebugJsonDir/directory_cleaner.h | 45 - .../DebugJsonDir/directory_dispatcher.cpp | 77 - .../DebugJsonDir/directory_dispatcher.h | 53 - .../Plugins/DebugJsonDir/plugin.cpp | 25 - .../Plugins/DebugJsonDir/plugin.h | 26 - .../Plugins/HlaJson/CMakeLists.txt | 43 - .../Plugins/HlaJson/hla_dispatcher.cpp | 108 - .../Plugins/HlaJson/hla_dispatcher.h | 62 - .../Plugins/HlaJson/hla_handler_rx.cpp | 149 - .../Plugins/HlaJson/hla_handler_rx.h | 35 - .../Plugins/HlaJson/hla_handler_tx.cpp | 323 -- .../Plugins/HlaJson/hla_handler_tx.h | 84 - .../Plugins/HlaJson/logger.cpp | 32 - .../TrainingAreaBase/Plugins/HlaJson/logger.h | 17 - .../Plugins/HlaJson/main_dispatcher.cpp | 46 - .../Plugins/HlaJson/main_dispatcher.h | 46 - .../Plugins/HlaJson/main_handler_tx.cpp | 235 -- .../Plugins/HlaJson/main_handler_tx.h | 77 - .../Plugins/HlaJson/plugin.cpp | 23 - .../TrainingAreaBase/Plugins/HlaJson/plugin.h | 26 - .../Plugins/Nmea/CMakeLists.txt | 47 - .../Plugins/Nmea/ICD_TAB_COMMASTER_MD.html | 113 - .../TrainingAreaBase/Plugins/Nmea/logger.cpp | 32 - .../TrainingAreaBase/Plugins/Nmea/logger.h | 17 - .../Plugins/Nmea/main_dispatcher.cpp | 51 - .../Plugins/Nmea/main_dispatcher.h | 47 - .../Plugins/Nmea/main_handler_tx.cpp | 228 -- .../Plugins/Nmea/main_handler_tx.h | 71 - .../Plugins/Nmea/nmea_dispatcher.cpp | 142 - .../Plugins/Nmea/nmea_dispatcher.h | 66 - .../Plugins/Nmea/nmea_handler_rx.cpp | 430 --- .../Plugins/Nmea/nmea_handler_rx.h | 131 - .../Plugins/Nmea/nmea_handler_tx.cpp | 259 -- .../Plugins/Nmea/nmea_handler_tx.h | 82 - .../TrainingAreaBase/Plugins/Nmea/plugin.cpp | 21 - .../TrainingAreaBase/Plugins/Nmea/plugin.h | 25 - .../Plugins/UAS/CMakeLists.txt | 56 - .../Plugins/UAS/Test/CMakeLists.txt | 87 - .../Plugins/UAS/Test/test1.cpp | 163 - .../Plugins/UAS/Test/test2.cpp | 74 - .../Plugins/UAS/Test/test3.cpp | 114 - .../Plugins/UAS/Test/test4.cpp | 36 - .../Plugins/UAS/Test/test4_2.cpp | 60 - .../Plugins/UAS/Test/test5.cpp | 16 - .../Plugins/UAS/Test/test6.cpp | 37 - .../Plugins/UAS/TmsIfAdapter/CMakeLists.txt | 15 - .../Plugins/UAS/TmsIfAdapter/module_loader.h | 72 - .../UAS/TmsIfAdapter/src/CMakeLists.txt | 83 - .../TmsIfAdapter/src/abstract_dispatcher.h | 78 - .../TmsIfAdapter/src/communication_master.h | 24 - .../src/dispatchers/report_cbit_status.cpp | 52 - .../src/dispatchers/report_cbit_status.h | 32 - .../src/dispatchers/report_device_status.cpp | 71 - .../src/dispatchers/report_device_status.h | 33 - .../src/dispatchers/report_mission_status.cpp | 102 - .../src/dispatchers/report_mission_status.h | 31 - .../src/dispatchers/report_point.cpp | 40 - .../src/dispatchers/report_point.h | 30 - .../src/dispatchers/report_positions.cpp | 71 - .../src/dispatchers/report_positions.h | 30 - .../report_radio_link_connection_state.cpp | 53 - .../report_radio_link_connection_state.h | 32 - .../Plugins/UAS/TmsIfAdapter/src/logger.cpp | 31 - .../Plugins/UAS/TmsIfAdapter/src/logger.h | 18 - .../Plugins/UAS/TmsIfAdapter/src/reader.h | 84 - .../UAS/TmsIfAdapter/src/tms_if_adapter.cpp | 192 -- .../TmsIfAdapter/src/uav_commander_impl.cpp | 784 ----- .../UAS/TmsIfAdapter/src/uav_commander_impl.h | 258 -- .../Plugins/UAS/TmsIfAdapter/tms_if_adapter.h | 25 - .../Plugins/UAS/TmsIfAdapter/uav_commander.h | 218 -- .../TrainingAreaBase/Plugins/UAS/from_tab.cpp | 242 -- .../TrainingAreaBase/Plugins/UAS/from_tab.h | 96 - .../TrainingAreaBase/Plugins/UAS/logger.cpp | 31 - .../TrainingAreaBase/Plugins/UAS/logger.h | 18 - .../TrainingAreaBase/Plugins/UAS/plugin.cpp | 49 - .../TrainingAreaBase/Plugins/UAS/plugin.h | 32 - .../Plugins/UAS/plugin_instance.cpp | 111 - .../Plugins/UAS/plugin_instance.h | 81 - .../TrainingAreaBase/Plugins/UAS/uav_ia.cpp | 24 - .../TrainingAreaBase/Plugins/UAS/uav_ia.h | 65 - .../Plugins/UAS/uav_ia_manager.cpp | 480 --- .../Plugins/UAS/uav_ia_manager.h | 72 - .../Plugins/WlsTetra/CMakeLists.txt | 56 - .../Plugins/WlsTetra/PrtCnv.dat | 101 - .../Plugins/WlsTetra/logger.cpp | 32 - .../Plugins/WlsTetra/logger.h | 17 - .../Plugins/WlsTetra/main_dispatcher.cpp | 69 - .../Plugins/WlsTetra/main_dispatcher.h | 48 - .../Plugins/WlsTetra/main_handler_tx.cpp | 293 -- .../Plugins/WlsTetra/main_handler_tx.h | 74 - .../Plugins/WlsTetra/playerIds.dat | 1 - .../Plugins/WlsTetra/plugin.cpp | 22 - .../Plugins/WlsTetra/plugin.h | 25 - .../Plugins/WlsTetra/tetra.qrc | 6 - .../Plugins/WlsTetra/tetra_dispatcher.cpp | 110 - .../Plugins/WlsTetra/tetra_dispatcher.h | 65 - .../Plugins/WlsTetra/tetra_handler_rx.cpp | 219 -- .../Plugins/WlsTetra/tetra_handler_rx.h | 44 - .../Plugins/WlsTetra/tetra_handler_tx.cpp | 269 -- .../Plugins/WlsTetra/tetra_handler_tx.h | 77 - .../Plugins/WlsTetra/tetra_prtcnv.cpp | 190 - .../Plugins/WlsTetra/tetra_prtcnv.h | 51 - .../Plugins/WlsTetra/wls_dispatcher.cpp | 116 - .../Plugins/WlsTetra/wls_dispatcher.h | 64 - .../Plugins/WlsTetra/wls_handler_rx.cpp | 253 -- .../Plugins/WlsTetra/wls_handler_rx.h | 41 - .../Plugins/WlsTetra/wls_handler_tx.cpp | 355 -- .../Plugins/WlsTetra/wls_handler_tx.h | 84 - .../Messages/client_message.cpp | 68 - .../Messages/client_message.h | 61 - .../Messages/reply_message.cpp | 122 - .../Messages/reply_message.h | 160 - .../WebSocketInterface/data_manager.cpp | 43 - .../WebSocketInterface/data_manager.h | 32 - .../WebSocketInterface/json_keys.h | 72 - .../websocket_interface_handler.cpp | 76 - .../websocket_interface_handler.h | 61 - .../Proxies/TrainingAreaBase/dds_handler.cpp | 101 - .../Proxies/TrainingAreaBase/dds_handler.h | 72 - .../Proxies/TrainingAreaBase/exm_handler.cpp | 73 - .../Proxies/TrainingAreaBase/exm_handler.h | 35 - .../Proxies/TrainingAreaBase/ism_handler.cpp | 618 ---- .../Proxies/TrainingAreaBase/ism_handler.h | 92 - .../TrainingAreaBase/log4cxx_tab.props | 185 - .../Lts/Proxies/TrainingAreaBase/main.cpp | 39 - .../Proxies/TrainingAreaBase/main_handler.cpp | 527 --- .../Proxies/TrainingAreaBase/main_handler.h | 117 - .../Proxies/TrainingAreaBase/pdm_handler.cpp | 307 -- .../Proxies/TrainingAreaBase/pdm_handler.h | 67 - .../TrainingAreaBase/position_handler.cpp | 284 -- .../TrainingAreaBase/position_handler.h | 80 - .../Proxies/TrainingAreaBase/ppm_handler.cpp | 94 - .../Proxies/TrainingAreaBase/ppm_handler.h | 43 - .../TrainingAreaBase/recv_from_dds_aes.cpp | 1375 -------- .../TrainingAreaBase/recv_from_dds_aes.h | 376 -- .../TrainingAreaBase/recv_from_dds_fts.cpp | 800 ----- .../TrainingAreaBase/recv_from_dds_fts.h | 173 - .../Proxies/TrainingAreaBase/recv_from_tab.h | 58 - .../Proxies/TrainingAreaBase/send_to_dds.cpp | 1401 -------- .../Proxies/TrainingAreaBase/send_to_dds.h | 103 - .../Proxies/TrainingAreaBase/send_to_tab.h | 75 - .../Lts/Proxies/TrainingAreaBase/settings.cpp | 135 - .../Lts/Proxies/TrainingAreaBase/settings.h | 47 - .../tab_record_replay_plugin_custom.cpp | 70 - .../tab_record_replay_plugin_custom.h | 26 - .../Lts/Proxies/TrainingAreaBase/tx_mixin.cpp | 350 -- .../Lts/Proxies/TrainingAreaBase/tx_mixin.h | 81 - 765 files changed, 128732 deletions(-) delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeParserSmoke.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/README.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/__pycache__/codegen_simple.cpython-314.pyc delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_log4cxx.props delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_simple.py delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/ea_export_script.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc.idl.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_enums.idl.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.cpp.grantlee delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.h.grantlee delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsBotan/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/ltsmultimonitor_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/main.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/indicator.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/ltsscripttimeline_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/newsrc.tar.xz delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/script_timeline.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/script_timeline.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp.backup_session_1172 delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/log4cxx_timeline.props delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/main.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventList.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/LineMap.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Range.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/ltstimelineclassic_export.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/main.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists_rc.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_table_model.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_checkable_table_model.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drop_handler.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_widgets_export.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgets_export.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.json delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/DMF.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/European-Union-Flag.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/Germany-Flag.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/Great-Britain-Flag.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ScriptEditor-Changelog-1.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/US-Flag.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack_dark.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/beep.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close_dark.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/effectarea-enter.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/effectarea-leave.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/entryNew.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/error.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/info.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/jquery.orgchart.js delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/leaveNew.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/lts_resources.qrc delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/qwebchannel.js delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_icon_256.ico delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_icon_256.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_internal_dds_logo_256x256.ico delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_internal_dds_logo_256x256.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.ico delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_internal_sql_logo_256x256.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_komplett.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_logo.ico delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_logo.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/rde_splitter_left.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/scripts.js delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/start.html delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/stylesheet-branch-closed.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/stylesheet-branch-open.png delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/success.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/warning.svg delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/warningrange-enter.wav delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/warningrange-leave.wav delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/lts_picture_editing.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/lts_picture_editing.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/lts_picture_editing.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/lts_picture_editing_main.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/main.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/window.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/tests/window.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ui_requirement.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ui_requirement.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/CMakeLists.txt delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_editor_base.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_editor_base.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_editor_interface.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_editor_utils.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_editor_utils.h delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_preview_widget.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_preview_widget.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_preview_widget.ui delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_previewer.cpp delete mode 100644 qt6-after/qt6-migrated-legatus/Lts/Common/TopicEditorBase/topic_previewer.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ICD_UFom_HLA.md delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-AQ.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-AQ.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Erkundungsergebnisse.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Erkundungsergebnisse.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FH.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FH.stuct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FQ-BA-BB-FK.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FQ-BA-BB-FK.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FQ-FN.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-FQ-FN.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Flugbahnberechnung.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Flugbahnberechnung.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Flugbahndaten.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Flugbahndaten.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-HQ-G.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-HQ-G.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-HQ-K.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-HQ-K.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-SQ-FS.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-SQ-FS.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-SQ-LP.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-SQ-WS.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-SQ-WS.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Status.json delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/EX-Status.struct delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/readme.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/JSON_SCHEMA/tabModuleTestAdler.result delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_abmeldung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_abmeldung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_anmeldebestaetigung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_anmeldebestaetigung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_anmeldung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_anmeldung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_aq_freitext.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_aq_freitext.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_erkundungsanfrage.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_erkundungsanfrage.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_erkundungsergebnisse.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_erkundungsergebnisse.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fh_feuerhalt.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fh_feuerhalt.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_flugbahnberechnung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_flugbahnberechnung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_flugbahndaten.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_flugbahndaten.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_abgefeuert.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_abgefeuert.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuerbereit.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuerbereit.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuerkampf.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuerkampf.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuern.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_feuern.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_vollzugsmeldung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_vollzugsmeldung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_wirkfeldplanung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_fq_wirkfeldplanung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_hq_fuehrungslinien.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_hq_fuehrungslinien.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_hq_koordinierungsmassnahmen.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_hq_koordinierungsmassnahmen.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_mq_manuelle_quittung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_mq_manuelle_quittung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_fs.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_fs.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_lp.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_lp.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_ws.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungsmeldung_ws.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungswechsel.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_sq_stellungswechsel.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_status.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_status.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_tq_technische_quittung.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/ex_tq_technische_quittung.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/msgcompares.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/msgcompares.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ModuleTest/test_telegram_decoding.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/Tests/ddsview/MountMount.spl delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/Tests/ddsview/MountUnmount.spl delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/adler_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/aes_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/aes_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/asm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/asm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/flm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/flm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/generatetelegrams.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/generatetelegrams.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/interface_controller.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/interface_controller.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/main_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/main_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/pdm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/pdm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ppm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ppm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/saa_interface.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/saa_interface.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/settings.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/settings.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/telegrams.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/telegrams.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ws_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ws_handler.cpp.b delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ws_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ws_interface.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/ICD_apache_hla_telegrams.md delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/aes_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/aes_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/apache_hla.qmodel delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/dds_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/dds_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/hla_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/main_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/main_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/main_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/main_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/pdm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/pdm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/ppm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/ApacheHlaJson/ppm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_ifc.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_ifc.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_linux.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_linux.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_win.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_DirHandler_win.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/XDR_QtHandler/XDR_QtHandler_export.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/convert_to_json_file.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/convert_to_json_file.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/directory_cleaner.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/directory_cleaner.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/directory_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/directory_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/DebugJsonDir/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/hla_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/main_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/main_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/main_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/main_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/HlaJson/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/ICD_TAB_COMMASTER_MD.html delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/main_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/main_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/main_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/main_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/nmea_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/Nmea/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test1.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test2.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test3.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test4.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test4_2.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test5.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/Test/test6.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/module_loader.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/abstract_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/communication_master.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_cbit_status.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_cbit_status.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_device_status.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_device_status.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_mission_status.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_mission_status.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_point.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_point.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_positions.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_positions.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_radio_link_connection_state.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/dispatchers/report_radio_link_connection_state.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/reader.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/tms_if_adapter.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/uav_commander_impl.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/src/uav_commander_impl.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/tms_if_adapter.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/TmsIfAdapter/uav_commander.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/from_tab.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/from_tab.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/plugin_instance.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/plugin_instance.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/uav_ia.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/uav_ia.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/uav_ia_manager.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/UAS/uav_ia_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/CMakeLists.txt delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/PrtCnv.dat delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/logger.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/logger.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/main_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/main_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/main_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/main_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/playerIds.dat delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/plugin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/plugin.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra.qrc delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_prtcnv.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/tetra_prtcnv.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_dispatcher.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_dispatcher.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_handler_rx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_handler_rx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_handler_tx.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/Plugins/WlsTetra/wls_handler_tx.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/Messages/client_message.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/Messages/client_message.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/Messages/reply_message.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/Messages/reply_message.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/data_manager.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/data_manager.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/json_keys.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/websocket_interface_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/WebSocketInterface/websocket_interface_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/dds_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/dds_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/exm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/exm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/ism_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/ism_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/log4cxx_tab.props delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/main.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/main_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/main_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/pdm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/pdm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/position_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/position_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/ppm_handler.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/ppm_handler.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/recv_from_dds_aes.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/recv_from_dds_aes.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/recv_from_dds_fts.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/recv_from_dds_fts.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/recv_from_tab.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/send_to_dds.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/send_to_dds.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/send_to_tab.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/settings.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/settings.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/tab_record_replay_plugin_custom.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/tab_record_replay_plugin_custom.h delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/tx_mixin.cpp delete mode 100755 qt6-after/qt6-migrated-legatus/Lts/Proxies/TrainingAreaBase/tx_mixin.h diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CMakeLists.txt deleted file mode 100755 index ee1ba2fb6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CMakeLists.txt +++ /dev/null @@ -1,173 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -FIND_PACKAGE(RTI) - -# KTextTemplate is Qt6-compatible replacement for Grantlee5 -IF(BUILD_GRANTLEE) - FIND_PACKAGE(KF6TextTemplate REQUIRED) -ENDIF() - -NGL_ADD_LIBRARY2(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 - Qt6::Xml - NGLLogger - ngltools - PUBLIC - Qt6::Core -) -NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_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 - $ - RTI_DDS_AVAILABLE - ) - message(STATUS "RTI found; building codegen_base with RTI support") -else() - message(STATUS "RTI not found; building codegen_base without RTI support") -endif() -NGL_INSTALL_TARGET(codegen_base) - -# KTextTemplate is Qt6-compatible replacement for Grantlee5 -IF(BUILD_GRANTLEE AND TARGET KF6::TextTemplate) - 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 - NGLLogger - ProjectConfig - PUBLIC - codegen_base - KF6::TextTemplate - ) - NGL_TARGET_INCLUDE_DIRECTORIES(codegen_grantlee - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_BINARY_DIR}/Z/src/NGLLogger - ) - - # Smoke-test: parse all .grantlee templates in this directory to detect parse/render regressions - find_package(Qt6 COMPONENTS Core QUIET) - if (Qt6_FOUND) - set(QTCORE_TARGET Qt6::Core) - else() - find_package(Qt6 COMPONENTS Core REQUIRED) - set(QTCORE_TARGET Qt6::Core) - endif() - - add_executable(grantlee_parser_smoke tests/GrantleeParserSmoke.cpp) - target_link_libraries(grantlee_parser_smoke PRIVATE KF6::TextTemplate ${QTCORE_TARGET}) - add_test(NAME GrantleeParserSmoke COMMAND grantlee_parser_smoke ${CMAKE_CURRENT_SOURCE_DIR}) - - NGL_ADD_EXECUTABLE(ALL ngl_codegen - main_grantlee.cpp - ) - NGL_TARGET_LINK_LIBRARIES(ngl_codegen - codegen_grantlee - LtsCommandLineParser - nglcoreapplication - Qt6::Core - KF6::TextTemplate - ) - - 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 - ) -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(codegen_gui - codegen_grantlee - LtsCommandLineParser - nglapplication - ${Qt6_Widgets} - KF6::TextTemplate - ) -endif() -ELSE() - message(STATUS "BUILD_GRANTLEE disabled or Grantlee not found; skipping codegen_grantlee, ngl_codegen, and codegen_gui targets") -ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp deleted file mode 100644 index c4a316967..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.cpp +++ /dev/null @@ -1,330 +0,0 @@ - - -#include "CppTopicGeneratorGrantlee.h" - -#include "StructBuilder.h" - -#include -#include -#include - -#ifdef RTI_HEADER_AVAILABLE -#include -#endif - -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()) - { -#ifdef RTI_HEADER_AVAILABLE - 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; - } -#else - // RTI not available - skip extensibility check (assume EXTENSIBLE) - Q_UNUSED(ctti.extensibility); -#endif - 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 QStringList sl = tti.name.split(QStringLiteral("::")); - baseValueType = sl.at(0) + QLatin1String("::") + tti.baseValueType; - } - DEBUG_LOG(m_logger) << "topicTypeInformationByStructName(" << baseValueType << ")"; - return m_structBuilder->topicTypeInformationByStructType(baseValueType); -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h deleted file mode 100644 index 59ea13e39..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/CppTopicGeneratorGrantlee.h +++ /dev/null @@ -1,41 +0,0 @@ - - -#pragma once -#include "GeneratorGrantleeBase.h" -#include "GrantleeWrappers.h" -#include "StructBuilder.h" - -#include - -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; -}; -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp deleted file mode 100644 index f7355237d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @RDE_HEADER@ - * Generate cpp structs from xml - * $Id: DaoGeneratorGrantlee.cpp 50446 2020-03-18 10:29:57Z ehrlicher $ - * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp $ - * $Author: ehrlicher $ - */ - -#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; - - // Is Service exercise-independent? - 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.h deleted file mode 100644 index 67d6a2460..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DaoGeneratorGrantlee.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @RDE_HEADER@ - * Generate cpp structs from xml - * $Id: DaoGeneratorGrantlee.h 50446 2020-03-18 10:29:57Z ehrlicher $ - * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/DaoGeneratorGrantlee.h $ - * $Author: ehrlicher $ - */ -#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; -}; -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.cpp deleted file mode 100644 index 3fd7d0da7..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h deleted file mode 100644 index d6f244f94..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/DatabaseGeneratorGrantlee.h +++ /dev/null @@ -1,27 +0,0 @@ - - -#pragma once -#include "GeneratorGrantleeBase.h" -#include "GrantleeWrappers.h" -#include "StructBuilder.h" - -#include - -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; -}; -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp deleted file mode 100644 index a43ef7a31..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.cpp +++ /dev/null @@ -1,200 +0,0 @@ - - -#include "GeneratorGrantleeBase.h" - -#include "GrantleeWrappers.h" -#include "XmlReader.h" - -#include -#include -#include - -using namespace CodeGen; - -GeneratorGrantleeBase::GeneratorGrantleeBase(const char* loggerName) - : m_engine(new KTextTemplate::Engine) - , m_ctx(new KTextTemplate::Context) - , m_structBuilder(nullptr) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName)) -{ - // Add KTextTemplate plugin path for loading default tags/filters - // KTextTemplate plugins are in /usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexttemplate - // QT_PLUGIN_PATH should point to the parent directory containing the category folders - m_engine->addPluginPath(QStringLiteral("/usr/lib/x86_64-linux-gnu/qt6/plugins")); -} - -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); - const QRegularExpression 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.mid(idx, endIdx - idx).trimmed(); - - 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.h deleted file mode 100644 index faecfa634..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GeneratorGrantleeBase.h +++ /dev/null @@ -1,53 +0,0 @@ - - -#ifndef GENERATORGRANTLEEBASE_H -#define GENERATORGRANTLEEBASE_H - -#include "XmlReader.h" - -#include -#include - -#include -#include -#include - -namespace NGL -{ -class INGLLogger; -} - -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: - KTextTemplate::Engine* m_engine; - KTextTemplate::Context* m_ctx; - KTextTemplate::Template m_template; - StructBuilder* m_structBuilder; - NGL::INGLLogger& m_logger; -}; -} // namespace CodeGen - -#endif // GENERATORGRANTLEEBASE_H diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeParserSmoke.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeParserSmoke.h deleted file mode 100644 index c0bf6d4c1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeParserSmoke.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef GRANTLEEPARSERSMOKE_H -#define GRANTLEEPARSERSMOKE_H - -/** - * @brief GrantleeParserSmoke - Minimal header for test compilation - * - * This header exists solely to allow test_GrantleeParserSmoke.cpp to compile. - * The actual Grantlee parser smoke test is implemented in - * Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp as a standalone executable. - * - * This file was accidentally deleted and needs to be restored for BUILD_TESTING. - */ - -namespace GrantleeParserSmoke -{ - // Placeholder namespace for test compilation - // Actual implementation is in Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp -} - -#endif // GRANTLEEPARSERSMOKE_H \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.cpp deleted file mode 100644 index 8c2734e52..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.cpp +++ /dev/null @@ -1,203 +0,0 @@ - - -#include "GrantleeWrappers.h" - -#include -#include - -using namespace CodeGen; - -#ifdef RTI_HEADER_AVAILABLE -QString IdlStructWrapper::extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity) -{ - switch (extensiblity.underlying()) - { - case dds::core::xtypes::ExtensibilityKind_def::type::FINAL: - return QStringLiteral("FINAL_EXTENSIBILITY"); - case dds::core::xtypes::ExtensibilityKind_def::type::MUTABLE: - return QStringLiteral("MUTABLE_EXTENSIBILITY"); - case dds::core::xtypes::ExtensibilityKind_def::type::EXTENSIBLE: - return QStringLiteral("EXTENSIBLE_EXTENSIBILITY"); - } - Q_ASSERT_X(false, "IdlStructWrapper::extensibility2String", "Unknown extensiblity kind"); - return QString(); -} -#else -QString IdlStructWrapper::extensibility2String(int /*extensiblity*/) -{ - // RTI not available - return placeholder - return QStringLiteral("EXTENSIBLE_EXTENSIBILITY"); -} -#endif - -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 -{ - // rtiddsgen 7.7 generates plain public members - access them directly. - 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;"); - - // rtiddsgen 7.7 generates plain public members for every kind of - // member (struct or not), so no getter call is emitted. - const QString memberAccess = cppMemberName(); - if (!isOptional()) - return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() + - QStringLiteral("element.") + memberAccess + cppToJsonPost(); - - const QString optionalAccess = cppMemberName() + QStringLiteral(".is_set()"); - const QString getValue = cppMemberName(); - return QStringLiteral("if (element.") + optionalAccess + QStringLiteral(") ") + - QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() + - QStringLiteral("*element.") + getValue + 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()) "); - - // rtiddsgen 7.7 generates plain public members - assign instead of - // calling a setter. - ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral(" = ") + cppFromJsonPre2() + - QStringLiteral("val") + cppFromJsonPost(); - - 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 : std::as_const(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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.h deleted file mode 100644 index b464d3c12..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/GrantleeWrappers.h +++ /dev/null @@ -1,932 +0,0 @@ - - -#pragma once - -#include "StructBuilder.h" - -#include -#include -#include -#include - -#ifdef RTI_HEADER_AVAILABLE -#include -#endif - -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 : std::as_const(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) - Q_PROPERTY(bool isCppStruct READ isCppStruct) - 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; - } - - bool isCppStruct() const - { - return m_mi.cpp.usedForCppStruct; - } - - 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& notification : std::as_const(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 - { - // rtiddsgen 7.7 generates public data members (no getters); optional - // members are omg::types::optional and are unwrapped via .get(). - 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 : std::as_const(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; -#ifdef RTI_HEADER_AVAILABLE - static QString extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity); -#else - static QString extensibility2String(int extensiblity); -#endif -}; - -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& params) - { - for (const auto& param : params) - m_requestParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this))); - } - - QVariantList requestParams() const - { - return m_requestParams; - } - - void addReplyParams(const QVector& params) - { - for (const auto& param : 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& params) - { - for (const auto& param : 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)); - } -}; - -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/README.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/README.txt deleted file mode 100755 index f128ada89..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.cpp deleted file mode 100644 index 7bb9b1622..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.cpp +++ /dev/null @@ -1,892 +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_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")), - m_xmlReader(xmlReader), m_structBuilder(structBuilder), m_createdByXmlFile(false), m_isValid(true), - m_exerciseId(0) -{ -} - -SqlGenerator::SqlGenerator(const QString& xmlFile) - : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")), - m_xmlReader(new XmlReader), m_structBuilder(new StructBuilder(m_xmlReader)), m_createdByXmlFile(true), - m_isValid(false), m_exerciseId(0) -{ - 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, static_cast(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& notifications, - ExerciseDependency ed) -{ - for (const DbNotifcationInformation& notification : 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& notification, 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& notification, - 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& notification) 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.h deleted file mode 100644 index d06b8b559..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/SqlGenerator.h +++ /dev/null @@ -1,198 +0,0 @@ - - -#pragma once - -#include "StructBuilder.h" -#include "codegen_base_export.h" - -#include -#include - -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: - NGL::INGLLogger& m_logger; - - 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& notifications, - ExerciseDependency ed); - void generateDbNotification(const DbNotifcationInformation& notification, ExerciseDependency ed); - bool generateDbNotificationTrigger(const QString& tableName, const DbNotifcationInformation& notification, - ExerciseDependency ed); - QString getNotificationFunctionName(const DbNotifcationInformation& notification) 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; -}; - -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.cpp deleted file mode 100644 index 574cbaee7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.cpp +++ /dev/null @@ -1,1371 +0,0 @@ - - -#include "StructBuilder.h" - -#include -#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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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.data(); - 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 : std::as_const(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.data(); - 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 : std::as_const(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 : std::as_const(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 : std::as_const(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(">("); - mi.cpp.fromVariantPost = QStringLiteral(".toInt())"); - mi.cpp.toVariantPre = QStringLiteral("static_cast("); - mi.cpp.toVariantPost = QStringLiteral(")"); - mi.cpp.toJsonPre = QStringLiteral("toJson(static_cast("); - mi.cpp.toJsonPost = QStringLiteral("))"); - 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"); - // IDL `long long` is int64_t (long) in the rtiddsgen 4.7 output, while the Qt side is - // qint64 (long long). Those are distinct types, so assignments such as - // `optional = optional` do not compile. Carry the generated type - // separately, as the sequence branch below already does, so the DDS-facing code - // emitted from cppDdsType() names the type the struct member really has. The spelling - // is the one rtiddsgen itself writes, matching what the `long` branch does with int32_t. - mi.cpp.ddsType = QStringLiteral("int64_t"); - mi.cpp.fromVariantPost = QStringLiteral(".toLongLong()"); - mi.cpp.fromJsonPost = QStringLiteral(".toDouble()"); - mi.cpp.toJsonPre = QStringLiteral("static_cast("); - mi.cpp.toJsonPost = QStringLiteral(")"); - 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 : std::as_const(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 : std::as_const(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 : std::as_const(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 QStringList sl = QString(type).split(QStringLiteral("::")); - QStringView extServiceRef; - QStringView 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.toString().split(QLatin1Char('<')).at(1); - else - ret.service = extServiceRef.toString(); - } - if (!extTypeRef.isEmpty()) - { - if (extTypeRef.contains(QLatin1Char('>'))) - ret.type = extTypeRef.toString().split(QLatin1Char('>')).at(0); - 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.h deleted file mode 100644 index 77819848d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/StructBuilder.h +++ /dev/null @@ -1,385 +0,0 @@ - - -#pragma once - -#include "XmlReader.h" -#include "codegen_base_export.h" - -#include - -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; -}; -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.cpp deleted file mode 100644 index dc91a9e66..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.cpp +++ /dev/null @@ -1,893 +0,0 @@ - - -#include "XmlReader.h" - -#include -#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; - - QDomDocument doc; - const auto parseResult = doc.setContent(&f); - if (!parseResult) - { - m_errorString = QLatin1String("Error parsing service file '") + svcFile + QLatin1String("' at line ") + - QString::number(int(parseResult.errorLine)) + QLatin1Char(':') + - QString::number(int(parseResult.errorColumn)); - 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(','), Qt::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 QStringList sl = isi.baseValueType.split(QStringLiteral("::")); - if (sl.size() == 2) - loadExternalTopicType(sl.at(0), sl.at(1)); - - const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true); - -#ifdef RTI_HEADER_AVAILABLE - 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; -#else - // RTI not available - skip extensibility parsing - Q_UNUSED(tmp) -#endif - - 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 : std::as_const(m_functionTopics)) - { - const QStringList sl1 = ft.requestTopicType.split(QStringLiteral("::")); - if (sl1.size() == 2) - loadExternalTopicType(sl1.at(0), sl1.at(1)); - const QStringList sl2 = ft.replyTopicType.split(QStringLiteral("::")); - if (sl2.size() == 2) - loadExternalTopicType(sl2.at(0), sl2.at(1)); - } -} - -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 QStringList parts = fileName.split(QStringLiteral("/")); - 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] = fileName; - DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation << " = " << fileName; - } - else - { - const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).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 QStringList& parts) -{ - if (parts.size() < 2) - return; - - const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first(); - const QString serviceName = parts.at(parts.size() - 2); - 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.h deleted file mode 100644 index 17ab28861..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/XmlReader.h +++ /dev/null @@ -1,459 +0,0 @@ - - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#ifdef RTI_DDS_AVAILABLE -// Only include RTI headers if they are actually available -#if __has_include() -#include -#define RTI_HEADER_AVAILABLE 1 -#else -#undef RTI_DDS_AVAILABLE -#endif -#endif - -#include "codegen_base_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; - -#ifdef RTI_DDS_AVAILABLE - dds::core::xtypes::ExtensibilityKind extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE; -#else - int extensibility = 0; // Placeholder when RTI DDS is not available -#endif - - 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: - NGL::INGLLogger& m_logger; - 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 QStringList& 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}; -}; -} // namespace CodeGen diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/__pycache__/codegen_simple.cpython-314.pyc b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/__pycache__/codegen_simple.cpython-314.pyc deleted file mode 100644 index 7d455a11ed40b025b8bca13e8aa5c60eeebe82fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32703 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_export.h deleted file mode 100644 index 4be10620b..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_log4cxx.props b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_log4cxx.props deleted file mode 100755 index 889482757..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_simple.py b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_simple.py deleted file mode 100755 index 8ddcb59c7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/codegen_simple.py +++ /dev/null @@ -1,498 +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 ): - outfile.write('\n') - outfile.write('/**\n') - outfile.write('* %s definitions\n' % 'DTC') - 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') - outfile.write('#pragma once\n') - outfile.write('\n') - # Include Qt headers FIRST to avoid namespace conflicts with DDS - outfile.write('#include \n') - outfile.write('#include \n') - outfile.write('\n') - # Include DDS headers BEFORE project headers to avoid namespace conflicts - outfile.write('#include \n') - outfile.write('#include \n') - outfile.write('#include \n') - outfile.write('\n') - outfile.write('#include "%s.hpp"\n' % ( parser.service_abbr )) - 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(r'\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 = out_dir + '/' + 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(), out_dir + '/' + parser.service_abbr + '_operator_rights.h') - generate(parser, OperatorRightsCpp(), out_dir + '/' + parser.service_abbr + '_operator_rights.cpp') - else: - usage("invalid option '%s'" % option_str) - sys.exit(0) - -if __name__ == "__main__": - main() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/ea_export_script.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/ea_export_script.txt deleted file mode 100755 index 2700b3ec5..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee.cpp deleted file mode 100644 index c9c3f3cfc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee.cpp +++ /dev/null @@ -1,167 +0,0 @@ - - -#include "CppTopicGeneratorGrantlee.h" -#include "DaoGeneratorGrantlee.h" -#include "DatabaseGeneratorGrantlee.h" -#include "SqlGenerator.h" -#include "StructBuilder.h" -#include "XmlReader.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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.cpp deleted file mode 100644 index 9eda5c869..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.cpp +++ /dev/null @@ -1,147 +0,0 @@ - - -#include "main_grantlee_gui.h" - -#include "CppTopicGeneratorGrantlee.h" -#include "DatabaseGeneratorGrantlee.h" -#include "StructBuilder.h" -#include "XmlReader.h" -#include "ui_main_grantlee_gui.h" - -#include -#include -#include -#include - -MainWindow::MainWindow(QWidget* parent) - : QMainWindow(parent), m_xmlReader(nullptr), m_structBuilder(nullptr), - m_generator(new CodeGen::CppTopicGeneratorGrantlee), m_engine(new KTextTemplate::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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.h b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.h deleted file mode 100644 index ad177c7f3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.h +++ /dev/null @@ -1,42 +0,0 @@ - - -#pragma once -#include -#include - -namespace CodeGen -{ -class XmlReader; -class StructBuilder; -class GeneratorGrantleeBase; -} // namespace CodeGen - -namespace KTextTemplate -{ -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; - KTextTemplate::Engine* m_engine; - QString m_idlServiceInfoFile; - QString m_templateFile; - Ui_MainGrantleeGui* m_ui; -}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/main_grantlee_gui.ui deleted file mode 100755 index c34b9fe73..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc.idl.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc.idl.grantlee deleted file mode 100755 index bdcf031d6..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.cpp.grantlee deleted file mode 100755 index 510fad917..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dao.h.grantlee deleted file mode 100755 index 49855abbd..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db.h.grantlee deleted file mode 100755 index 30ead9d31..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee deleted file mode 100755 index ee32ddbe2..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_impl.h.grantlee deleted file mode 100755 index 558c05dfc..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee deleted file mode 100755 index c21d03c87..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.cpp.grantlee +++ /dev/null @@ -1,112 +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; -} - -// Basic type toJson overloads -QJsonValue toJson(int val) -{ - return QJsonValue(val); -} -QJsonValue toJson(double val) -{ - return QJsonValue(val); -} -QJsonValue toJson(qint64 val) -{ - return QJsonValue(QString::number(val)); -} -QJsonValue toJson(const std::string &val) -{ - return QJsonValue(QString::fromStdString(val)); -} - -// 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_db_structs.h.grantlee deleted file mode 100755 index 1782c0bd8..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc.h.grantlee deleted file mode 100755 index 538aeeffc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc.h.grantlee +++ /dev/null @@ -1,91 +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> - -// Bring Core namespace types into scope for Interface namespace -using namespace {{ namespace }}; - -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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee deleted file mode 100755 index 6eb0b7bc5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.cpp.grantlee +++ /dev/null @@ -1,309 +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 -#include -#include - -using namespace NGL; -using namespace Core; - -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 ret; // moved implicitly (C++17; explicit std::move on a local triggers -Wredundant-move) -} - -// 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee deleted file mode 100755 index 3deba5006..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dtc_impl.h.grantlee +++ /dev/null @@ -1,109 +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 -#include -#include - -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc.h> -#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h> -#include <{{ include_prefix }}/logger.h> -using namespace NGL; - -#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; - -signals: -{% for dt in dataTopics.member %} -{% if dt.dtcImpl %} - void {{ dt.nameFlc }}Changed(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType); -{% endif %} {% endfor %} -}; - -} // namespace Interface -} // namespace {{ namespace }} -{% endautoescape %} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts.h.grantlee deleted file mode 100755 index 224a680ea..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee deleted file mode 100755 index b73806515..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.cpp.grantlee +++ /dev/null @@ -1,102 +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; - // rtiddsgen -stl generates plain public data members, so the key is - // assigned directly instead of the old setter call data.{{ dt.key.name }}(...). - 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_dts_impl.h.grantlee deleted file mode 100755 index 06852a4c7..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_enums.idl.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_enums.idl.grantlee deleted file mode 100755 index 2ea8976a5..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc.h.grantlee deleted file mode 100755 index 20c31ae53..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee deleted file mode 100755 index a3311b5cc..000000000 --- a/qt6-after/qt6-migrated-legatus/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 ret; // moved implicitly (C++17; explicit std::move on a local triggers -Wredundant-move) -} - -// 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.h.grantlee deleted file mode 100755 index ce1afe2cf..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts.h.grantlee deleted file mode 100755 index aa5752fe7..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.cpp.grantlee deleted file mode 100755 index 884d063c9..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.h.grantlee deleted file mode 100755 index d15d3fbdb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_fts_impl.h.grantlee +++ /dev/null @@ -1,67 +0,0 @@ -/** - * {{ namespace }} Function Topic Server implementation - * $Id$ - * $HeadURL$ - * $Author$ - */ - -{% autoescape off %} -// filename: {{ namespace_abbr }}_fts_impl.h -#pragma once -#include -#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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.cpp.grantlee deleted file mode 100755 index 2f3f082e4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.cpp.grantlee +++ /dev/null @@ -1,172 +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)); -} -// IDL 64-bit integers map to int64_t, which is 'long' on LP64 and therefore -// distinct from qint64 ('long long'); without this overload the generic -// toJson(std::vector) is ambiguous for sequence members. -QJsonValue toJson(long vec) -{ - return QJsonValue(QString::number(vec)); -} -template<> -long fromJson(const QJsonValue &val, type) -{ - return val.toString().toLong(); -} - -// 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_json.h.grantlee deleted file mode 100755 index b59958565..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee deleted file mode 100755 index 47765cf7d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee +++ /dev/null @@ -1,919 +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; -} - -// Primary fallback for any migrated plain-`enum class` enumerators that have no -// explicit specialization below - most importantly the nested foreign -// ProjectDataManagement / Core enums a plugin reads back from the database (they are -// not in this service's idl_enums, so the per-own-enum specializations do not cover -// them). Giving the primary template a body (rather than leaving it declaration-only) -// is what lets those foreign-enum instantiations resolve, so the plugin .so links. The -// explicit specializations for primitives / own enums / structs / vectors / optionals -// all take precedence over this fallback, so it is only ever instantiated for an enum -// type, where `static_cast(val.toInt())` is the correct conversion. -template -T fromQVariant(const QVariant &val, type) -{ - return static_cast(val.toInt()); -} - -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(); -} - -// rtiddsgen 4.7 spells the 64 bit integer members int64_t, which is `long` on LP64 and thus -// a different type from the Qt side's qint64 (`long long`). The tag dispatch above is keyed on -// the exact type, so the generated type needs its own conversion even though the two are the -// same width. GrantleeWrappers::cppDdsType() names the generated member type, so this is the -// overload the record replay code instantiates for those members. -template<> -int64_t fromQVariant(const QVariant &val, type) -{ - return static_cast(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{}); -} - -// rtiddsgen 7.7 spells the migrated enumerators as plain `enum class` in the service -// namespace (the legacy dds::core::safe_enum wrapper is gone). The record_replay -// plugin still instantiates fromQVariant() for each enum member it reads back, -// and none of the generic overloads above are ever deduced for those types, so each of -// this service's own enumerations needs its own explicit specialization. `idl_enums` -// carries the service's own enumerations by local name and this block runs inside -// `namespace {{ namespace }}`, so the local name resolves to the fully-qualified enum -// type. (Nested foreign PDM enumerations are still not covered - same limitation as the -// documented svcBuildings exclusion.) -{% for enumeration in idl_enums %} -template<> -{{ enumeration.name }} fromQVariant(const QVariant &val, type<{{ enumeration.name }}>) -{ - return static_cast<{{ enumeration.name }}>(val.toInt()); -} -{% endfor %} - -{% 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; - - // rtiddsgen 4.7 declares sample_time as int64_t (long), while the record - // boundary is qint64 (long long); std::min requires both arguments to be - // the same type, so narrow the generated member explicitly. - minRecordSampleTime = std::min(minRecordSampleTime, static_cast(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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee_possible_extensions deleted file mode 100755 index 97a6e9240..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee deleted file mode 100755 index 7c3b26268..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_record_replay_plugin.h.grantlee +++ /dev/null @@ -1,128 +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> - -// Resolved from the Lts include root (see include_directories(BEFORE .../Lts) -// in the root CMakeLists); the old <../../Common/...> spelling only worked -// for in-source builds. -#include - -#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 %} - - NGL::INGLLogger& m_logger; -}; - -} -{% endautoescape %} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.cpp.grantlee deleted file mode 100755 index 8f944ce20..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee deleted file mode 100755 index 9a9d3ff9f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_topic_type_plugin.h.grantlee +++ /dev/null @@ -1,41 +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 -// Resolved from the Lts include root (see include_directories(BEFORE .../Lts) -// in the root CMakeLists); the old <../../DDS/...> spelling only worked for -// in-source builds because moc/gcc do not resolve it relative to this file. -#include - -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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.cpp.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.cpp.grantlee deleted file mode 100755 index da3eeb68e..000000000 --- a/qt6-after/qt6-migrated-legatus/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) - {{% for idl_member in idl_enum.member %} - case {{ idl_enum.name }}::{{ idl_member.key }}: return QCoreApplication::translate("{{ idl_enum.name }}", "{{ idl_member.key }}"); - {% endfor %} - default: break; - } - return QCoreApplication::translate("{{ idl_enum.name }}", "Unknown (%1)").arg(static_cast(state)); -} -{% 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.h.grantlee b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.h.grantlee deleted file mode 100755 index d747fc9e1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_translation.h.grantlee +++ /dev/null @@ -1,32 +0,0 @@ -/** -* @RDE_HEADER@ -* Convert DSS enumerations to translatable strings -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -{% autoescape off %} -#pragma once - -// filename: {{ namespace_abbr }}_translation.h - -#include -#include <{{ include_prefix }}/{{ 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/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp deleted file mode 100644 index c99309e84..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp +++ /dev/null @@ -1,70 +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]); - } - - KTextTemplate::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) - { - KTextTemplate::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() != KTextTemplate::NoError) - { - qDebug() << "Parse error in" << f << ":" << t->errorString(); - ok = false; - continue; - } - // Render with empty context to ensure rendering path also works. - KTextTemplate::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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp deleted file mode 100644 index df3025333..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.cpp +++ /dev/null @@ -1,32 +0,0 @@ - - -#include "app.h" - -#include "AppChooser/app_chooser_model.h" -#include "ui_app.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; -} - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h deleted file mode 100644 index 089cb5f22..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.h +++ /dev/null @@ -1,28 +0,0 @@ - - -#pragma once -#include -#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; -}; - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/CustomViews/app.ui deleted file mode 100755 index d4653b571..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp deleted file mode 100644 index 5888476fc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.cpp +++ /dev/null @@ -1,80 +0,0 @@ - - -#include "AppChooser/app_chooser.h" - -#include "AppChooser/CustomViews/app.h" -#include "AppChooser/app_chooser_model.h" -#include "lts_app_manager.h" -#include "ui_app_chooser.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); -} - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.h deleted file mode 100644 index e89a33b50..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.h +++ /dev/null @@ -1,45 +0,0 @@ - -#pragma once -#include -#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(); -}; - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser.ui deleted file mode 100755 index 841d60178..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp deleted file mode 100644 index 00f8728cc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.cpp +++ /dev/null @@ -1,48 +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(); -} -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h deleted file mode 100644 index bb43784d1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/AppChooser/app_chooser_model.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once -#include -#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; -}; -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/CMakeLists.txt deleted file mode 100755 index 973bdd6dd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -# -# $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 -) - -IF(BUILD_RTI) -NGL_ADD_LIBRARY2(WS LtsAppManager SHARED ${LtsAppManager_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppManager - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_SOURCE_DIR}/AppChooser/ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsAppManager - PRIVATE - NGLLogger - LtsDataView - LtsWidgets - LtsLogin - LtsPluginManager - LtsSession - LtsSessionData - LtsUtilsSettings - LtsMultiMonitor - ProjectConfig - ${Qt6_Gui} - ${Qt6_Widgets} - Qt6::Network # debug server - OpmSettingsGuiPluginInterface - LtsPluginLoader - PUBLIC - Qt6::Core - LtsAppDescriptor - ) -endif() - -NGL_INSTALL_TARGET(LtsAppManager) -ENDIF(BUILD_RTI) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.cpp deleted file mode 100644 index ffb0eff43..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.cpp +++ /dev/null @@ -1,963 +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 -#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(' '), Qt::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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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; -} - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.h deleted file mode 100644 index d4f657e66..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsAppManager/lts_app_manager.h +++ /dev/null @@ -1,136 +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 LtsSession - -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); -}; - -} // namespace LtsAppManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsBotan/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsBotan/CMakeLists.txt deleted file mode 100755 index ff9f098c7..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/CMakeLists.txt deleted file mode 100755 index ede50d7cc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/CMakeLists.txt +++ /dev/null @@ -1,30 +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_INCLUDE_DIRECTORIES(LtsCoordinate - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../LtsCoordTrans -) -NGL_TARGET_LINK_LIBRARIES(LtsCoordinate - PRIVATE - LtsCoordTrans - PUBLIC - $,ddscore,> - LtsUtilsCoreHeaderOnly - Qt6::Core -) - -ADD_SUBDIRECTORY(UnitTest_Coordinate) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt deleted file mode 100755 index ee102b9c3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# -# @RDE_HEADER@ -# $Id$ -# $HeadURL$ -# $Author$ -# - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - - -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 - Qt6::Core - ${Qt6_Gui} - Qt6::Test) - -ADD_TEST(NAME commonUnitTestCoordinate COMMAND $) - -ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp deleted file mode 100644 index 73a8f42b4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.cpp +++ /dev/null @@ -1,868 +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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h deleted file mode 100644 index 3e8a238e1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/UnitTest_Coordinate/lts_coordinate_unit_test.h +++ /dev/null @@ -1,60 +0,0 @@ - - -#pragma once -#include -#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: - NGL::INGLLogger& m_logger; -}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.cpp deleted file mode 100644 index a9e0435aa..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.cpp +++ /dev/null @@ -1,69 +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)); -} - -} // namespace LtsCoordTrans diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.h deleted file mode 100644 index e34354777..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coord_vector.h +++ /dev/null @@ -1,62 +0,0 @@ - - -#pragma once - -#include - -#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 : std::as_const(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; -}; - -} // namespace LtsCoordTrans diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.cpp deleted file mode 100644 index 388db610c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.cpp +++ /dev/null @@ -1,143 +0,0 @@ - - -#include "lts_coordinate.h" - -#include -#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; -} - -} // namespace LtsCoordTrans diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.h deleted file mode 100644 index 31adb9ac5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsCoordinate/lts_coordinate.h +++ /dev/null @@ -1,297 +0,0 @@ - - -#pragma once - -#include - -#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; - } - - Core::GeoPosition2D geoPosition2D() const - { - return Core::GeoPosition2D(m_geo.lon, m_geo.lat); - } - - 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 : std::as_const(contIn)) - out.push_back(LtsCoordinate(posGeo)); - return out; -} - -LTSCOORDINATE_EXPORT std::ostream& operator<<(std::ostream& stream, const LtsCoordinate& value); - -} // namespace LtsCoordTrans diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/CMakeLists.txt deleted file mode 100755 index a10601ca6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - - -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 - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_SOURCE_DIR}/Lts/Common - ${CMAKE_SOURCE_DIR}/Lts/Common/LtsUtils - ${CMAKE_SOURCE_DIR}/Lts/Viewer/SituationMap - ${CMAKE_SOURCE_DIR}/Lts/Viewer/SituationMap/Common -) -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsElevation - PRIVATE - NGLLogger - ## LtsSessionDataDds - PUBLIC - LtsUtilsCoreHeaderOnly - Qt6::Core - ${Qt6_Gui} - ) -endif() - -if(BUILD_RTI) - # All ElevationMapManager / resource_path / exm-dtc usages live inside - # #ifdef BUILD_RTI in lts_elevation_manager.cpp (Qt5 dependencies restored). - target_link_libraries(LtsElevation PRIVATE - AsmDataPluginInterface - ExmDataPluginInterface - situationMapCommon - situationMapElevationMap - ) -endif() - -ADD_SUBDIRECTORY(UnitTest_Elevation) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt deleted file mode 100755 index 27cbd31fd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - - -# -# @RDE_HEADER@ -# $Id$ -# $HeadURL$ -# $Author$ -# - -# External dependency - missing: situationMapCommon, situationMapElevationMap -# Test disabled until external dependencies are available -# IF(BUILD_TESTING AND TARGET LtsElevation) -# ADD_EXECUTABLE(commenUnitTestElevation ${LinkedSources} ${UnitTestSources} ) -# -# TARGET_LINK_LIBRARIES(commenUnitTestElevation -# NGLLogger -# nglcoreapplication -# LtsElevation -# LtsCoordTrans -# situationMapCommon -# situationMapElevationMap -# Qt6::Core -# ${Qt6_Gui} -# Qt6::Test) -# -# ADD_TEST(NAME commenUnitTestElevation COMMAND $) -# ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp deleted file mode 100644 index a3496268b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.cpp +++ /dev/null @@ -1,198 +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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h deleted file mode 100644 index 3cec0a272..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/UnitTest_Elevation/elevation_unit_test.h +++ /dev/null @@ -1,38 +0,0 @@ - - -#pragma once - -#include -#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: - NGL::INGLLogger& m_logger; -}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.cpp deleted file mode 100644 index 885ac8f86..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.cpp +++ /dev/null @@ -1,30 +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(); - } -} - -} // namespace LtsElevation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.h deleted file mode 100644 index e9bbe0487..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/logger.h +++ /dev/null @@ -1,17 +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()); -} -} // namespace LtsElevation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.cpp deleted file mode 100644 index 526f6ce23..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.cpp +++ /dev/null @@ -1,587 +0,0 @@ - - -#include "lts_elevation_manager.h" - -#ifdef BUILD_RTI -#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"); -#endif - -namespace LtsElevation -{ -class LtsElevationManager; - -using LtsElevatioMgrMapTrainingAreaId = QMap; - -static LtsElevatioMgrMapTrainingAreaId& ltsElevatioMgrMapTrainingAreaId() -{ - 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() - { -#ifdef BUILD_RTI - delete m_elevationManager; -#endif - } - - std::shared_ptr m_exmDtc; -#ifdef BUILD_RTI - std::shared_ptr m_asmDtc; - SituationMap::ElevationMapManager* m_elevationManager{nullptr}; -#endif - - QStringList m_filePaths_rough_to_fine; -}; - -LtsElevationManager::LtsElevationManager() - : QObject(nullptr) - , d(new Private) - , m_logger(getLogger("LtsElevationManager")) -{ - ++s_counter; - TRACE_LOG(m_logger) << Q_FUNC_INFO << " START, s_counter=" << s_counter << " this=" << this; - TRACE_LOG(m_logger) << Q_FUNC_INFO << " END, s_counter=" << s_counter << " this=" << this; -} - -LtsElevationManager::~LtsElevationManager() -{ - --s_counter; - TRACE_LOG(m_logger) << Q_FUNC_INFO << " START, s_counter=" << s_counter << " this=" << this; - delete d; - TRACE_LOG(m_logger) << Q_FUNC_INFO << " END, s_counter=" << s_counter << " this=" << this; -} - -LTS_ELEVATION_MANAGER_TYPE -LtsElevationManager::createInstance(int exerciseId, const std::shared_ptr& exmDtc, -#ifdef BUILD_RTI - const std::shared_ptr& asmDtc -#else - const std::shared_ptr& asmDtc -#endif -) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << Q_FUNC_INFO << " START, exerciseId=" << exerciseId; - - if (exerciseId < 0) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " exerciseId invalid, exerciseId=" << exerciseId; - return nullptr; - } - - // Qt5 logic restored (the qt6 port had dropped the exerciseId->trainingAreaId - // caching and always returned nullptr here). -#ifdef BUILD_RTI - int trainingAreaId = -1; - - // remember for future calls - // (so createInstance should work WITHOUT dtcs) - // Idea: MainHandler: createInstance with exerciseId and valid dtcs - // Plugin or simular: createInstance with exerciseId and no dtcs - static QHash s_exIdToTaIdStore; - - const auto exTaIt = s_exIdToTaIdStore.find(exerciseId); - if (exTaIt != s_exIdToTaIdStore.end()) - { - // CASE 1: - // already created: - trainingAreaId = *exTaIt; - } - else - { - // CASE 2: - // ... or create on demand: - if (!exmDtc || !asmDtc) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " no dtcs for exerciseId=" << exerciseId - << ", asmDtc=" << asmDtc - << ", exmDtc=" << exmDtc; - return nullptr; - } - - // exId to taId - trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, exmDtc); - if (trainingAreaId <= 0) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return nullptr; - } - - // store and remember: - s_exIdToTaIdStore[exerciseId] = trainingAreaId; - } - - // CASE 1: Following should work even with invalid asmDtc: - LTS_ELEVATION_MANAGER_TYPE temp = createInstanceForTrainingAreaId(trainingAreaId, asmDtc); - if (!temp) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " Error on createInstanceForTrainingAreaId" - << ", exerciseId=" << exerciseId - << ", trainingAreaId=" << trainingAreaId; - return nullptr; - } - - // add exmDtc - temp->initDDS(exmDtc, asmDtc); - - TRACE_LOG(logger) << Q_FUNC_INFO << " END, exerciseId=" << exerciseId; - return temp; -#else - Q_UNUSED(exmDtc); - Q_UNUSED(asmDtc); - ERROR_LOG(logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, cannot create instance"; - return nullptr; -#endif -} - -LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::getInstance(int exerciseId) -{ - return LtsElevationManager::createInstance(exerciseId); -} - -#ifdef BUILD_RTI -LTS_ELEVATION_MANAGER_TYPE -LtsElevationManager::createInstanceForTrainingAreaId(int trainingAreaId, - const std::shared_ptr& asmDtc) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << Q_FUNC_INFO << " START, trainingAreaId=" << trainingAreaId; - - if (trainingAreaId < 0) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " no filenames for trainingAreaId=" << trainingAreaId; - delete temp; - return nullptr; - } - - bool bInitFilenames = temp->init(filenames); - if (!bInitFilenames) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " END, trainingAreaId=" << trainingAreaId; - return map[trainingAreaId]; -} -#endif - -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) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " END, trainingAreaId=" << id; - return map[id]; -} - -void LtsElevationManager::cleanupAll() -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - TRACE_LOG(logger) << Q_FUNC_INFO << " START"; - cleanupLtsElevatioMgrMapTrainingAreaId(); - TRACE_LOG(logger) << Q_FUNC_INFO << " END"; -} - -#ifdef BUILD_RTI -void LtsElevationManager::initDDS(const std::shared_ptr& exmDtc, - const std::shared_ptr& asmDtc) -{ - TRACE_LOG(m_logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " END"; -} -#else -void LtsElevationManager::initDDS(const std::shared_ptr& exmDtc, - const std::shared_ptr& asmDtc) -{ - Q_UNUSED(exmDtc); - Q_UNUSED(asmDtc); - TRACE_LOG(m_logger) << Q_FUNC_INFO << " START (BUILD_RTI=OFF, no DDS init)"; - - // BUILD_RTI=OFF: No DDS initialization - TRACE_LOG(m_logger) << Q_FUNC_INFO << " END"; -} -#endif - -#ifdef BUILD_RTI -QStringList LtsElevationManager::getFilenamesForExercise(int exerciseId) -{ - TRACE_LOG(m_logger) << Q_FUNC_INFO << " START"; - - if (!d->m_exmDtc || !d->m_asmDtc) - { - ERROR_LOG(m_logger) << Q_FUNC_INFO << " no dtcs" - << ", exerciseId=" << exerciseId; - return {}; - } - - int trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, d->m_exmDtc); - if (trainingAreaId <= 0) - { - ERROR_LOG(m_logger) << Q_FUNC_INFO << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId << ", trainingAreaId=" << trainingAreaId; - return {}; - } - - QStringList filePaths_rough_to_fine = getFilenamesForTrainingArea(trainingAreaId); - - TRACE_LOG(m_logger) << Q_FUNC_INFO - << " 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) << Q_FUNC_INFO << " START"; - - if (!d->m_asmDtc) - { - ERROR_LOG(m_logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " 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 : std::as_const(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) << Q_FUNC_INFO - << " END, filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(";")); - return filePaths_rough_to_fine; -} -#else -QStringList LtsElevationManager::getFilenamesForExercise(int /*exerciseId*/) -{ - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, getFilenamesForExercise not available"; - return {}; -} - -QStringList LtsElevationManager::getFilenamesForTrainingArea(int /*trainingAreaId*/) -{ - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, getFilenamesForTrainingArea not available"; - return {}; -} -#endif - -bool LtsElevationManager::init(const QStringList& filePaths_rough_to_fine) -{ -#ifdef BUILD_RTI - 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) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!"; - FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!"; - return false; - } - return true; -#else - Q_UNUSED(filePaths_rough_to_fine); - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, init not available"; - return false; -#endif -} - -#ifdef BUILD_RTI -int LtsElevationManager::exerciseIdToTrainingAreaId(int exerciseId, - const std::shared_ptr& exmDtc) -{ - LOGGER_T logger = getLogger("LtsElevationManager"); - - if (!exmDtc) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " no dtcs" - << ", exerciseId=" << exerciseId; - return -1; - } - - int trainingAreaId = -1; - { - exmDtc->waitForHistoricalDataExercise(1000); - - const ExerciseManagement::Exercise& exm = exmDtc->exercise(exerciseId); - if (exm.id != exerciseId) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " Error on m_exmDtc->exercise" - << ", exerciseId=" << exerciseId; - return -1; - } - trainingAreaId = exm.trainingAreaId; - - if (trainingAreaId <= 0) - { - ERROR_LOG(logger) << Q_FUNC_INFO << " Error getting trainingAreaId" - << ", exerciseId=" << exerciseId << ", trainingAreaId=" << trainingAreaId; - return -1; - } - } - return trainingAreaId; -} -#else -int LtsElevationManager::exerciseIdToTrainingAreaId(int exerciseId, - const std::shared_ptr& exmDtc) -{ - Q_UNUSED(exerciseId); - Q_UNUSED(exmDtc); - LOGGER_T logger = getLogger("LtsElevationManager"); - ERROR_LOG(logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, cannot convert exerciseId to trainingAreaId"; - return -1; -} -#endif - -bool LtsElevationManager::waitForInitialization() -{ -#ifdef BUILD_RTI - if (!d->m_elevationManager) - return false; - return d->m_elevationManager->waitForInitialization(); -#else - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, waitForInitialization not available"; - return false; -#endif -} - -bool LtsElevationManager::isValid() const -{ -#ifdef BUILD_RTI - if (!d->m_elevationManager) - return false; - return d->m_elevationManager->isValid(); -#else - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, isValid not available"; - return false; -#endif -} - -double LtsElevationManager::elevation(double lon_rad, double lat_rad, double defaultValue, bool* bIsCovered) const -{ -#ifdef BUILD_RTI - if (!d->m_elevationManager) - return defaultValue; - return d->m_elevationManager->elevation(lon_rad, lat_rad, defaultValue, bIsCovered); -#else - Q_UNUSED(lon_rad); - Q_UNUSED(lat_rad); - Q_UNUSED(bIsCovered); - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, elevation not available"; - return defaultValue; -#endif -} - -LineOfSight LtsElevationManager::lineOfSightCalculation(const LtsUtils::QPoint3D& startPointGeo_absZ, - const LtsUtils::QPoint3D& endPointGeo_absZ) -{ -#ifdef BUILD_RTI - TRACE_LOG(m_logger) << Q_FUNC_INFO << " START" - << ", startPointGeo_absZ = " << startPointGeo_absZ - << ", endPointGeo_absZ = " << endPointGeo_absZ; - - if (!isValid()) - { - ERROR_LOG(m_logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " END" - << ", startPointGeo = " << startPointGeo_absZ << ", endPointGeo = " << endPointGeo_absZ - << " -> LineOfSight " << result; - return result; -#else - Q_UNUSED(startPointGeo_absZ); - Q_UNUSED(endPointGeo_absZ); - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, lineOfSightCalculation not available"; - return LineOfSight::LOS_ERROR; -#endif -} - -LineOfSight LtsElevationManager::lineOfSightCalculation(const QPointF& startPointGeo, double startHat, - const QPointF& endPointGeo, double endHat) -{ -#ifdef BUILD_RTI - TRACE_LOG(m_logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " END" - << ", startPointGeo = " << startPointGeo << ", startHat = " << startHat - << ", endPointGeo = " << endPointGeo << ", endHat = " << endHat << " -> LineOfSight " - << static_cast(eleMapMgrResult); - return result; -#else - Q_UNUSED(startPointGeo); - Q_UNUSED(startHat); - Q_UNUSED(endPointGeo); - Q_UNUSED(endHat); - ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, lineOfSightCalculation not available"; - return LineOfSight::LOS_ERROR; -#endif -} - -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; -} - -} // namespace LtsElevation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.h deleted file mode 100644 index 0e8748c98..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsElevation/lts_elevation_manager.h +++ /dev/null @@ -1,113 +0,0 @@ - - -#pragma once - -#include "logger.h" -#include "ltselevation_export.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsSession -{ -class LtsSessionDataDds; -} - -namespace ExerciseManagement -{ -namespace Interface -{ -class Dtc; -} -} // namespace ExerciseManagement - -#ifdef BUILD_RTI -namespace AssetManagement -{ -namespace Interface -{ -class Dtc; -} -} // namespace AssetManagement -#endif - -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 = {}, -#ifdef BUILD_RTI - const std::shared_ptr& asmDtc = {} -#else - const std::shared_ptr& asmDtc = {} -#endif - ); - - static LTS_ELEVATION_MANAGER_TYPE getInstance(int exerciseId); - -#ifdef BUILD_RTI - static LTS_ELEVATION_MANAGER_TYPE - createInstanceForTrainingAreaId(int trainingAreaId, - const std::shared_ptr& asmDtc = nullptr); -#endif - - 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, -#ifdef BUILD_RTI - const std::shared_ptr& asmDtc -#else - const std::shared_ptr& asmDtc -#endif - ); - 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); - -} // namespace LtsElevation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/CMakeLists.txt deleted file mode 100755 index e4ffa8cab..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/CMakeLists.txt +++ /dev/null @@ -1,44 +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 -) - -IF(BUILD_RTI) -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 - Qt6::Core -) -ENDIF(BUILD_RTI) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.cpp deleted file mode 100644 index 261b86754..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.cpp +++ /dev/null @@ -1,92 +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) - { - 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); -} - -} // namespace LtsLogin diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.h deleted file mode 100644 index 3adb732e5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.h +++ /dev/null @@ -1,44 +0,0 @@ - - -#pragma once -#include -#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; -}; - -} // namespace LtsLogin diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/logout_info_dialog.ui deleted file mode 100755 index dbd122e6e..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.cpp deleted file mode 100644 index dd5cd2d48..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.cpp +++ /dev/null @@ -1,282 +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) - { - case AssetManagement::ShutdownAction::Shutdown: -#ifdef LTS_DEBUG - cmd = QStringLiteral("xeyes"); -#else - cmd = QStringLiteral("systemctl poweroff"); -#endif - break; - case AssetManagement::ShutdownAction::Reboot: -#ifdef LTS_DEBUG - cmd = QStringLiteral("kcalc"); -#else - cmd = QStringLiteral("systemctl reboot"); -#endif - break; - case AssetManagement::ShutdownAction::Logout: - case AssetManagement::ShutdownAction::NoAction: - emit signalLogoffDone(); - break; - } - - if (!cmd.isEmpty()) - { - QProcess::startDetached(cmd); - QCoreApplication::quit(); - } -} - -} // namespace LtsLogin diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.h deleted file mode 100644 index c9714e107..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsLogin/lts_login.h +++ /dev/null @@ -1,71 +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(); -}; - -} // namespace LtsLogin diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/CMakeLists.txt deleted file mode 100755 index e5db3a811..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2016-11-01 -# - - -# LtsMultiMonitor -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -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 -) - -# LtsMultiMonitor is needed by AvmShared regardless of BUILD_RTI -NGL_ADD_LIBRARY2(WS LtsMultiMonitor SHARED ${LtsMultiMonitor_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsMultiMonitor - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_SOURCE_DIR}/Lts/Common/LtsUtils -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsMultiMonitor - PUBLIC - Qt6::Core - ${Qt6_Widgets} - PRIVATE - ${Qt6_Gui} - LtsWidgets - LtsSessionData - LtsUtilsCore - LtsUtilsGuiDds - ) -endif() - -NGL_INSTALL_TARGET(LtsMultiMonitor) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp deleted file mode 100644 index b1b22e485..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.cpp +++ /dev/null @@ -1,35 +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); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h deleted file mode 100644 index 46ffc05a9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_dummy_widget.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "ltsmultimonitor_export.h" - -#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(); -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp deleted file mode 100644 index 6000d371a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.cpp +++ /dev/null @@ -1,130 +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(); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.h deleted file mode 100644 index 2a69212de..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_magic_corner.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include "lts_screen_base.h" - -#include -#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; -}; -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp deleted file mode 100644 index 0502b57fb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp +++ /dev/null @@ -1,602 +0,0 @@ - - -#include "lts_multi_monitor.h" - -#include "lts_dummy_widget.h" -#include "lts_multi_monitor_selector_normal.h" -#include "lts_multi_monitor_selector_wide.h" -#include "lts_screen_container.h" -#include "lts_screen_normal.h" -#include "lts_screen_wide.h" - -#include -#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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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; -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h deleted file mode 100644 index aaeee6a79..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "ltsmultimonitor_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(); -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_export.h deleted file mode 100644 index 0cfa34c00..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp deleted file mode 100644 index 27c29c39b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.cpp +++ /dev/null @@ -1,343 +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.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(); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h deleted file mode 100644 index 3d632097c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_base.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include -#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; -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp deleted file mode 100644 index 5d0483b99..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.cpp +++ /dev/null @@ -1,81 +0,0 @@ - - -#include "lts_multi_monitor_selector_normal.h" - -#include "lts_dummy_widget.h" -#include "lts_multi_monitor.h" -#include "lts_screen_container.h" -#include "lts_screen_normal.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); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h deleted file mode 100644 index fbbc69731..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_normal.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "lts_multi_monitor_selector_base.h" - -#include -#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; -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp deleted file mode 100644 index 5c72e6de5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.cpp +++ /dev/null @@ -1,280 +0,0 @@ - - -#include "lts_multi_monitor_selector_wide.h" - -#include "lts_multi_monitor.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); - - 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(); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h deleted file mode 100644 index 7159bb004..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_multi_monitor_selector_wide.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "LtsMultiMonitor/lts_multi_monitor_selector_base.h" - -#include -#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; -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp deleted file mode 100644 index 11f7b4532..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.cpp +++ /dev/null @@ -1,52 +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(); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.h deleted file mode 100644 index 2a249b988..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_base.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include "ltsmultimonitor_export.h" - -#include -#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; -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp deleted file mode 100644 index 3c686a52f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.cpp +++ /dev/null @@ -1,416 +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->setContentsMargins(0, 0, 0, 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"; -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.h deleted file mode 100644 index 41682f201..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_container.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include "lts_screen_base.h" -#include "ltsmultimonitor_export.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(); -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp deleted file mode 100644 index e98ab4134..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "lts_screen_normal.h" - -#include "lts_dummy_widget.h" -#include "lts_magic_corner.h" -#include "lts_screen_container.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->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(); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.h deleted file mode 100644 index 8f75efba2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_normal.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include "lts_screen_base.h" -#include "ltsmultimonitor_export.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(); -}; -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp deleted file mode 100644 index 107a6442d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.cpp +++ /dev/null @@ -1,199 +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->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()); -} - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.h deleted file mode 100644 index e4e411487..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/lts_screen_wide.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include "lts_screen_base.h" -#include "ltsmultimonitor_export.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(); -}; - -} // namespace LtsMultiMonitor diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/ltsmultimonitor_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/ltsmultimonitor_export.h deleted file mode 100644 index e76bab3d0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsMultiMonitor/ltsmultimonitor_export.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "lts_multi_monitor_export.h" \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/CMakeLists.txt deleted file mode 100755 index 4289e0c74..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-05-20 -# - - -# LtsPluginLoader -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -NGL_ADD_LIBRARY2(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 - Qt6::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 -) - -IF(BUILD_RTI) -NGL_ADD_LIBRARY2(WS LtsPluginManager SHARED ${ltspluginmanager_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsPluginManager - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsPluginManager - PRIVATE - NGLLogger - LtsPluginLoader - GuiPluginInterface - DataPluginInterface - LtsWidgets - LtsSession - LtsSessionData - ${Qt6_Widgets} - PUBLIC - ProjectConfig - LtsAppDescriptor - Qt6::Core - ) -endif() - -NGL_INSTALL_TARGET(LtsPluginManager) -ENDIF(BUILD_RTI) - -NGL_INSTALL_TARGET(LtsPluginLoader) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp deleted file mode 100644 index 37e2bc9ba..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.cpp +++ /dev/null @@ -1,93 +0,0 @@ - - -#include "lts_plugin_manager_dialog.h" - -#include "../lts_plugin_manager.h" -#include "lts_plugin_manager_widget.h" -#include "ui_lts_plugin_manager_dialog.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(); -} - -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h deleted file mode 100644 index be03b9690..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.h +++ /dev/null @@ -1,37 +0,0 @@ - - -#pragma once -#include "lts_plugin_manager_widget.h" -#include "ltspluginmanager_export.h" - -#include -#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); -}; -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_dialog.ui deleted file mode 100755 index 1afb9c845..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp deleted file mode 100644 index 5c4aae4dc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.cpp +++ /dev/null @@ -1,117 +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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h deleted file mode 100644 index 49ff4c8d0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.h +++ /dev/null @@ -1,41 +0,0 @@ - - -#pragma once -#include -#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; -}; -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/Ui/lts_plugin_manager_widget.ui deleted file mode 100755 index 2c93dac33..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp deleted file mode 100644 index 9960dda29..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/** - * @RDE_HEADER@ - * plugin loader helper function - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Christian Ehrlicher, SLSE, 2016-07-07 - */ - -#include "lts_plugin_loader.h" -#include -#include - -#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 : std::as_const(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 /* out */) -{ - 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; - } - - - - // check for duplicate iids per plugin type - 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); - - - // check if all in apps.json requested plugins are available - for (auto i1 = appVector.constBegin(); i1 != appVector.constEnd(); ++i1) // Apps - { - const LtsAppDescriptor::AppDescription &app = *i1; - - //WARNING_LOG(logger) << "Checking app '"< &piVector = app.pluginInfos; - - for (auto i2 = piVector.constBegin(); i2 != piVector.constEnd(); i2++) // App's Required Plugins - { - const LtsAppDescriptor::PluginInfo &pi = *i2; - bool found = false; - - for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3) // Available Plugins - { - 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); // do not allow to look for unknown - - QStringList requiredPluginsList = requiredPlugins; // copy ... - QStringList optionalPluginsList = optionalPlugins; // copy ... - bool checkPluginsLists = (!requiredPluginsList.isEmpty()) || (!optionalPluginsList.isEmpty()); - - if (checkPluginsLists && !m_loadedPlugins.isEmpty()) // only lookup of there is something to lookup TODO: faster to NOT do that check? - { - { - QMutableListIterator i1(requiredPluginsList); // delete all plugins from the to-load list that were cached - - 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); // delete all plugins from the to-load list that were cached - - 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()) - { - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * In case of windows systems all libs are located in application directory. But can't - * use QCoreApplication::applicationDirPath here, because it points some times to the - * wrong directory. - * 2016/10/18 JT - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - 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"), - QStringLiteral("gui_element")}); // #warning TODO: recordreplay is a data plugin ? - - 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; - - // if (!filename.contains(type)) - // { - // pluginInfo.resultType = ResultType::WRONG_TYPE; - // pluginInfos.append(pluginInfo); - // continue; - // } - - std::unique_ptr 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(); // LOWERCASE ! (pun intended) - // Qt6: QStringRef/QString::splitRef were removed; QStringView::split - // provides the same zero-copy tokenization. - const QList iidParameters = QStringView{jsonIid}.split(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; // no need to look further, just a waste of time, like writing this long unnecessary explanation - } - } - } - - - if (!found) - { - for (auto i = optionalPluginsList.constBegin(); i != optionalPluginsList.constEnd(); i++) - { - const QString optionalPlugin = (*i).toLower(); - - if (pluginName == optionalPlugin) - { - found = true; - break; // no need to look further, just a waste of time, like writing this long unnecessary explanation - } - } - } - - - if (!found) - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " was not requested"; - continue; // plugin was not requested to be loaded - } - } - - - // before we create a new instance of a plugin, which is not needed as one instance is perfectly fine to use for many apps, look it up in the loaded plugins list - - if (loadPlugin && !m_loadedPlugins.isEmpty()) // only lookup if there is something to lookup TODO: faster to NOT do that check? - { - 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; // found, next plugin! - } - } - - // plugin not already loaded: - 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.get(); - - if (loadPlugin && !loadPlugin(pi)) // does the dev want this plugin? if no, unload it! TODO: really unload? or still cache it?? - { - DEBUG_LOG(logger) << "Plugin " << pluginName << " unloaded"; - delete pluginLoader->instance(); - pluginLoader->unload(); - continue; // next plugin - } - pi.pluginLoader = pluginLoader.release(); - - // the dev decided to use that plugin, so cache it - m_loadedPlugins.insert(PluginKey(requestedPluginType, pluginName), pi); - -// if (m_loadedPlugins.count() > 100) -// WARNING_LOG(logger) << "There are more than 100 plugins cached!"; - - DEBUG_LOG(logger) << "Loaded " << jsonIid << " - File: " << filename; - } // end of >> pluginDir.hasNext(); - - - 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; -} - -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.h deleted file mode 100644 index a5e96f82e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @RDE_HEADER@ - * plugin loader helper function - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Christian Ehrlicher, SLSE, 2016-07-07 - */ - -#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, // should not happen - DATA, // 1 - GUI, // 2 - TOPIC_TYPE, - PROJECT, - STYLE, - SPECIAL, // TODO: rm - GUI_ELEMENT, - -}; - - -struct PluginInstance // one instance for one plugin -{ - 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 /* out */); - - bool loadPlugins(PluginType requestedPluginType, - const std::function &loadPlugin, - const QStringList &requiredPlugins = QStringList(), /* leave empty to get all plugins of the given type */ - const QString &specialTopicTypeFilter = QString(), - const QStringList &optionalPlugins = QStringList()); - - typedef QPair PluginKey; - -private: - friend struct LtsPluginLoaderHandler; - - LtsPluginLoader() = default; - ~LtsPluginLoader(); - - QMap m_loadedPlugins; // TODO: meh... -}; - -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp deleted file mode 100644 index fdfac12cc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.cpp +++ /dev/null @@ -1,191 +0,0 @@ - - -#include "lts_plugin_manager.h" - -#include "Ui/lts_plugin_manager_dialog.h" - -#include -#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 : std::as_const(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; -} - -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.h deleted file mode 100644 index 847335579..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager.h +++ /dev/null @@ -1,140 +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 LtsSession - -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: - NGL::INGLLogger& m_logger; - friend class DeveloperTools::DevDialog; - - LtsAppDescriptor::AppDescription m_app; - 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); -}; - -} // namespace LtsPluginManager diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_manager_export.h deleted file mode 100644 index c4a08b941..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/CMakeLists.txt deleted file mode 100755 index 96f2828ae..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Christian Ehrlicher, SLSE, 2017-12-05 -# - -if(QT6_GUI_AVAILABLE) - find_package(KDReports QUIET) -endif() - -SET(SRCS - printing_base.cpp - printing_base.h - printing.cpp - printing.h -) - -if (KDReports_FOUND AND QT6_GUI_AVAILABLE AND BUILD_CODIFICATION) - NGL_ADD_LIBRARY2(WS LtsPrinting SHARED ${SRCS}) - NGL_TARGET_INCLUDE_DIRECTORIES(LtsPrinting - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ) -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsPrinting - PRIVATE - KDReports::KDReports - LtsSessionData - LtsSession - nglsettings - ngltools - PUBLIC - Qt6::PrintSupport - Qt6::Core - ) -endif() - NGL_INSTALL_TARGET(LtsPrinting) -else() - message(STATUS "KDReports not found or BUILD_RTI disabled; skipping LtsPrinting target") -endif() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.cpp deleted file mode 100644 index 45d5d77f0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.cpp +++ /dev/null @@ -1,386 +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(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); -} - -} // namespace LtsPrinting diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.h deleted file mode 100644 index cfb841b7e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing.h +++ /dev/null @@ -1,66 +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& images, QVector& imageTitles, - QVector& 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); - -} // namespace LtsPrinting diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.cpp deleted file mode 100644 index 728aeec48..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.cpp +++ /dev/null @@ -1,1126 +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 -#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())).getNGLLogger("NGLPrinting")), - m_currentPage(1), - m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) -{ - 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 : std::as_const(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 : std::as_const(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 : std::as_const(tableHeader)) - str += QLatin1String(""); - str += QLatin1String(""); - - QVector> spannedL; - for (const QModelIndex& mi : std::as_const(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 : std::as_const(tableHeader)) - str += QLatin1String(""); - str += QLatin1String(""); - for (const QModelIndex& miColumn0 : std::as_const(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.setContentsMargins(0, 0, 0, 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(); -} -} // namespace LtsPrinting diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.h deleted file mode 100644 index ae079a054..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPrinting/printing_base.h +++ /dev/null @@ -1,229 +0,0 @@ - - -#pragma once - -#include -#include - -namespace NGL -{ -class INGLLogger; -class Settings; -} // namespace NGL - -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: - NGL::INGLLogger& m_logger; - 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; - QString m_exerciseName; - QString m_operatorName; - int m_currentPage; -}; -} // namespace LtsPrinting diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt deleted file mode 100755 index f2341e8be..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Christian Roeskens, NLSE, 2018-03-02 - - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - - -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_LIBRARY2(WS LtsProjectionWorkstation SHARED ${LtsProjectionWorkstation_SRCS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsProjectionWorkstation - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsProjectionWorkstation - PUBLIC - Qt6::Core - PRIVATE - ${Qt6_Gui} - NGLLogger - nglsettings - LtsWebSocketServer - LtsSession - LtsSessionData - ) -endif() - -NGL_INSTALL_TARGET(LtsProjectionWorkstation) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp deleted file mode 100644 index a3b60419d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.cpp +++ /dev/null @@ -1,56 +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; -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h deleted file mode 100644 index 1a6a4aacb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/client_message.h +++ /dev/null @@ -1,68 +0,0 @@ - - -#pragma once - -#include "logger.h" - -#include - -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; -}; - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h deleted file mode 100644 index 4c74b7952..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/json_keys.h +++ /dev/null @@ -1,100 +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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp deleted file mode 100644 index a217724f8..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "message_factory.h" - -#include "json_keys.h" -#include "messages.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); -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h deleted file mode 100644 index 9444c285c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/message_factory.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "ltsprojectionworkstation_export.h" - -#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; -}; - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp deleted file mode 100644 index 8a78e3761..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.cpp +++ /dev/null @@ -1,205 +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; -} - -LtsProjectionWorkstation::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 LtsProjectionWorkstation::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 LtsProjectionWorkstation::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; -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h deleted file mode 100644 index 62de2dcfd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/messages.h +++ /dev/null @@ -1,209 +0,0 @@ - - -#pragma once - -#include "json_keys.h" -#include "LtsProjectionWorkstation/ltsprojectionworkstation_export.h" - -#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 STC - -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}; -}; - -} // namespace CTS - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp deleted file mode 100644 index 83b1d4f60..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.cpp +++ /dev/null @@ -1,20 +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); -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h deleted file mode 100644 index 65747f167..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/WebSocket/reply_message.h +++ /dev/null @@ -1,23 +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); -}; - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.cpp deleted file mode 100644 index 171c856e3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.cpp +++ /dev/null @@ -1,30 +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(); - } -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.h deleted file mode 100644 index ade1b4979..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/logger.h +++ /dev/null @@ -1,19 +0,0 @@ - - -#pragma once - -#include - -#include - -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()); -} -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp deleted file mode 100644 index 1b8e693c6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.cpp +++ /dev/null @@ -1,226 +0,0 @@ - - -#include "lts_projection_workstation.h" - -#include "WebSocket/json_keys.h" -#include "WebSocket/message_factory.h" - -#include -#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("LtsProjectionWorkstation")), - 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 : std::as_const(m_connections)) - con->webSocket()->close(); -} - -void LtsProjectionWorkstation::clientDisconnected(LtsWebSocket::ClientConnection* client) -{ - INFO_LOG(m_logger) << Q_FUNC_INFO << " 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) << Q_FUNC_INFO << " reset m_lastLoginRequest"; - m_lastLoginRequest = LoginInformation(); - } - INFO_LOG(m_logger) << Q_FUNC_INFO << " 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; -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h deleted file mode 100644 index 262e43285..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation.h +++ /dev/null @@ -1,101 +0,0 @@ - - -#pragma once - -#include "WebSocket/messages.h" -#include "logger.h" -#include -#include "vnc_server_manager.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; -}; - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/lts_projection_workstation_export.h deleted file mode 100644 index 133e5df14..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp deleted file mode 100644 index f8e172a0d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.cpp +++ /dev/null @@ -1,179 +0,0 @@ - - -#include "vnc_server_manager.h" - -#include -#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 : std::as_const(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 : std::as_const(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 : std::as_const(m_vncScreens)) - { - s.proc->disconnect(this); - s.proc->terminate(); - } - for (const VncScreen& s : std::as_const(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.captured(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(); -} - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h deleted file mode 100644 index e7800e6ab..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsProjectionWorkstation/vnc_server_manager.h +++ /dev/null @@ -1,67 +0,0 @@ - - -#pragma once - -#include "logger.h" - -#include -#include -#include -#include -#include - -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; -}; - -} // namespace LtsProjectionWorkstation diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/CMakeLists.txt deleted file mode 100755 index 27bacc8dd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/CMakeLists.txt +++ /dev/null @@ -1,84 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2017-03-15 -# - - -# Test -#ADD_SUBDIRECTORY(Test) Not needed anymore + old 'api' - - -# Library -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -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}) - -SET(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Ui) - -IF(BUILD_RTI) -NGL_ADD_LIBRARY2(WS LtsScriptTimeline SHARED ${LtsScriptTimeline_SRCS} ${LtsScriptTimeline_UICS}) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsScriptTimeline - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsScriptTimeline - Qt6::Core - ${Qt6_Widgets} - LtsWidgets - LtsSessionData - ) -endif() -ENDIF(BUILD_RTI) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/CMakeLists.txt deleted file mode 100755 index 76ec481d5..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/main.cpp deleted file mode 100644 index c3fbc3ce1..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp deleted file mode 100644 index ed9fd0a5d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.cpp +++ /dev/null @@ -1,86 +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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.h deleted file mode 100644 index e78d7490f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Test/mainwindow.ui deleted file mode 100755 index 7057f2935..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/filter_track_widget.ui deleted file mode 100755 index ba98d128d..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/indicator.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/indicator.ui deleted file mode 100755 index f614f31f8..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/script_timeline.ui deleted file mode 100755 index 3aef25760..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_entry_preview_widget.ui deleted file mode 100755 index 88eb706df..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/Ui/track_widget.ui deleted file mode 100755 index d00bd2b5e..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp deleted file mode 100644 index 48f1f4942..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "filter_background_widget.h" - -#include "script_timeline.h" -#include "tools.h" -#include "track_content_widget.h" -#include "track_entry.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); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.h deleted file mode 100644 index b904dc48e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_background_widget.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include "track_content_widget.h" - -#include -#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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp deleted file mode 100644 index c359d181d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "filter_track_widget.h" - -#include "script_timeline.h" -#include "track_entry.h" -#include "ui_filter_track_widget.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); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.h deleted file mode 100644 index b8b2656c9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/filter_track_widget.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once -#include "ltsscripttimeline_export.h" -#include "track_entry_painters.h" - -#include -#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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.cpp deleted file mode 100644 index c03102759..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.cpp +++ /dev/null @@ -1,73 +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); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.h deleted file mode 100644 index b5271a05f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/indicator.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#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(); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/ltsscripttimeline_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/ltsscripttimeline_export.h deleted file mode 100644 index e9ef2d0a2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/ltsscripttimeline_export.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef LTSSCRIPTTIMELINE_EXPORT_H -#define LTSSCRIPTTIMELINE_EXPORT_H - -#include - -#if defined(LTSSCRIPTTIMELINE_LIBRARY) -# define LTSSCRIPTTIMELINE_EXPORT Q_DECL_EXPORT -#else -# define LTSSCRIPTTIMELINE_EXPORT Q_DECL_IMPORT -#endif - -#endif // LTSSCRIPTTIMELINE_EXPORT_H \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/newsrc.tar.xz b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/newsrc.tar.xz deleted file mode 100755 index 25e76c824604fc14d90b787838b229c7beedece6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52292 zcmV(zK<2;wH+ooF000E$*0e?f03iVu0001VFXf}?NB_(MT>uvg#HboA%c{>e#R~yK zcE6JRd@&SOfRI=;FeY0QWoLteyKUtg`O=(eWPv$8ljSE3nKSWq1j76G<3JtIf9mvS ztqXg7$}Ic?(|j|sbNR)~gcJ_hGxm7~(Dc>+8r3jRr&V)3d>Yc=l?YnP8-AoCHrTq? zLA7jJmVhCMsrw<#pNJUW`LGLsB5^QY{(D@R35B#D9eKmt?8)^j#D+eJF0mb@Hm0;c z|AkD{lz@TcdO0M+W|HdWPkp_sOldbYRX3o6SP^n@C$hGoqnwq?uP1p^`ey7X_HQT5j9OLwSt@z)I!6 zD6MofZ+YPesx3QtXkjjki$C=yM{G9~`jPL2-L0kXPUX&4#jQ$|G4<5Sls{6$1&;J} zGt$^X&#r+U(=gaIEj2bRsknGJ^6^WVK^{Z?gF8lUreeS?yWviJbIH>OOe>_(mldOM zD>mWg^G)xh-zj;Eeo7Q4ph?1@njIbMNgFVgAoL)nt+c{>K)7s?fAJs53oz0WcY_J1 z_<6YP*N(3`Px?Usf`wY>NjK+H#HqF751K%ny*;TG*k?n4q(H7@#G{IiKEc*F&55Pm zWL`C=wWi0kVQ*}&$3)4c>=y&qcu`8Z0s^@TjpXTpSv`Z>$u{@S=GTf+LKv`K@=|w0oLX)%gOaD|AjM(I$qIIJa5Cxl{r76_d*UT9D?$mO- zL+5E2@`um$NT~6RNyK6rT*Yl#5Z$D4Gt{cB?@8b$)HqHG|#4RsgU#12?>twtXqpv z9>XRO`ufLsiF+#Afoxo<9Hk3jnf<&3%J6V|J(=%w_af{wE7z)e^6~aq^tl)ah8NHB zz-)B><*z^(Kn2{$JK7fD^k(e!OW-TEc}C(ojdxwl|x*4f~D)T*qm;Jy)*NT+Vxpu*bB>Y!HK$M#crB30vl_)8uwU&j|2qC5kRmE>^c=mG57eGe zR*96z`M>G;y;r8B)L(RJ{3nSXbUehEZ(@pT?gQ>WuOyIz`QF!nkARO1k5A&|V+Go> zIfxu^C_-b-z&8SI&Vfii&n}zopzm62E6whpadEEnL@@ECPv-A=v;EKAR2a6%LVWMH zG9@3tTTH??pA0%j+z?|2z{zHgZnl#Pb`!%Y=yhK`tm?npO~%^eD3w zPv*Iy^I@;N@=KSys^w&19C>s3p_A%epSL+?bH2E#xQEmcEhYmwOcn~H1heZea+j$s zo-6W?EUwj(fU|J(6qC9n$5Hx``m^e8mY8MU>Ai(=t8N>j4`Y3p2F^&h2c<|ZJ+YWI{12m&HA5XI!&`LpUF!J8AyEgMdxR8|98hXOe z-Qkw>`zRU6jAeH3M3{lJl4!0rqD6iX{6w^RF_D}&9;W9W*C5q@Yp4qQc*})@?5@%F zerX+bEx44DbF`vcR}7yeT_aer9`sd9WJrtv-*Q)6%(dO2qcN^ep76MQ(~-(#-USXJ z%!<_@5pEF@{s|8BJl)=#{nF^v-oQ_bpHc+}(@SxVa;&~_vVc6g{x_|Uc```~)4Knw z39o1;#-n5EeWj@xBIUg)yBEFGpkRP80pHKgQ+1cBV#&)l-)VrsJQDnuxx!!}>iP3`hB;dD2*q{F5MLp?o*gQ!7F=%xw~(8D(ui=|+x!x=d_kp;Pf@|PO+lV`<%~6GIt#*7WTUlfqG@_}~6oEL^ zVN1`jhAi=zQZ9ij6ycGGUg>8?>ADsu|7*G#uvt>bkS@*_V3i>y-NNYyjl_3GS#*KZ z7U2sP?uk&A{;AX|ly2~A{5)I^TYNjP2i_zVRzaRh5%u%bsT%|dXI2k* zVO+mEWiAknfQ8+IB5F&h;`3{mC874%){9e7!y#yl+HnM_YDA*Peb=@3)T{?*9kH!1 zn;}dG1p#}@2iq@(?G7D`Zr)F4mN6run;UlmB$5n3YhB69wZY^B_O zej=Zq7fyvPgnGyPnmH9@fcUg1;#(I5R`u8mrOj`MR90Cp>jF4TgLzGz))UR}V56Y_ zr|9>+ceH|4sXlk!6LOU{agrpmv~rSgr;V*s4~))Tr$tlC#O1z20|-}eGwjVl&)Kjf zb5kj#Km0F#9K_X%mOHE%TG1k}(bTdICJvjgNxVszT?K8@#Lb-LDf}d!dRvcfyRN zp*%CYp{fn8ln63)orpCx#?g`04M1em&!SGf5gQJK!)M-;B2xpbiHOXXPWAgMl%Ov+ ztb@a&F*LlqrLx0GugN|VqO}YweHI{3jq{ETlh#B`{&CuXLq;DZ)QiW}6i)l&9#?iBF@i-O>^|h96Nh zT~AY6Pdx8(z^+s%83(b-TrW^Y?&<>KeJ!x;ZfZQD_e7sC&Z>-`uJOp=T%=*|hJ^21 z#*S&lH~Pc)_?K@#t!~&GvOst15;R02KxCz4Z9Gs+zQ_il*3FTN-6%hXBOoT1}%3F;9mlxz^bNFw2*9xO<=)geL8*|e#N zEXk90u-f7_r^Cx|l%TEJ7&?)E8bS`uTO$h#` zuoS&}sD>NrT)8|d`;JY0r#{EcFp-^Wh7(C~67>)A3)1EdXOv?$m2vt5`d+>@=MKa^3T*YwjH;YOTm?pgT?syoy1vOsx*3+ zs1}3%4||<#H6Z^~1HVP=jP?k7n@qtK`%A1cNbPFbx~P$*r1_PSu0@d}Bq9)!N8bdD z1Yj0MOl-y2(JU>@jeT1VhLn!7T-FW{)c_um8^qY>4m+M$0wgjgTDb^#rAq;Gb8)nV zd=yKU-(U)blEN)-m_B&_jYt@#jx75U`wfL5)5$d-9C#=ClRd@5A1p*zb}^Hhy=<^m}xn>1(jEocBuFf}SEwhO0vrgtoL zh>YLoMAXOzE~6W{%*f9sDTTp(%$Cco&^eCwOkk}hPI#E@3t~Vh1&4=}i@2uS9F)sO zIbC3qu|{g6MgoSotIYeV0gSOE8T$OtCDNbBq zwW#?fJROm=c!vM=Ha|^h4#YdeByojZ(Vx5lSD<(ra#$AOlm3zgNcGPnw4#tgBgL+-t#kdUQm3-v-VSHLaYIBPVadsw&Lbl4&oO@FU z;PpuQNCp!f_jq9;fX>xMCG&@{SUJ(Rf+t~lKu_c}E6h|me!;!a2+$hIc@zbqowv&F#2}*|L@7hm{|bJ8W*#T8U)6}wrpXT0?pFHj%PIU))bLlreb1p zwO+#17W@Q*rebXUtKHo(i&E=rSG6ftDZnzQ7#B=rhSU1UL(mvf#DDbuQ}|v5pwbP_ zmn#2|O)~my(|NMoz4+FvkwQ`k$kWVrYCd%6f{ST)>@kYFMK8hhx74ZAOAECe77nqa zJBVcise9ViYIdtxU;6-we@);vq?WVOA!Bu?3+(fIF-=e@I7-OOT&hz6X~b4=)1eZ5 z_c6__3f$lL(T)Dm8cFR*F!ib(LUv>XcGzVyr(&up7H2~p4Oo=vu^iw)8fxD5?#oi6 z3zMJ`CW@RkM>anks+BjezF zWaX!@icuZe4wp~Pu?oMhpcPKvE;>Pb74dvqSW#X@iN7+?E%T1qJ<#_q6UQ5DT5*nUFwym)!cDOPv_ zQqHu(7nZW~>`*tVfKu=j*AH>k_gEatcjI+E1V#nG;t=&O`of6}TQ`Wtwu0Z;Bt-iw zNd_E^EJWRmwn*vDmW5J4AEz1!x!Y13VNc1{G0Tom2RZtbJiAAduvRJ1`-jS$;Av(R zbH`b%Bpi%y3Fq48h5Kbsn=@$>Alq?Y<0FkUHrMNHxGFxmayX7Wel4{0CgRVUq^e${L4yWDB8P=qPVM@Cl-ipM~~@$ zjZWPbk=J-@&&HJ>YoVjt4ml=`#-KMf681>%WICCy6m*(kung(cy-nWab!rnG_maAm zw!~%Pb$c_GIIZ0bJsyx4sYRbf0AW9bELihqm4dk5;TNL>E#>jFS%iQnCRrH6#e8AF6^Vx`{OLK&sI~RA53S(yg zkW=1HR{Mn;`PpKAdsPIkUEEOvDyi%2XoVp(>&9D-9nWcTPx$uRi>f6&&*Yc8=BX+l z7A>Ya(%$2$9HUVU7|SozRnFjdOE-w({%P_2HzbV*pJ0Ud;kU|P4et57JH5E-HGfZ< z+BEt|W>Fp&HK|`5DcnFgsf$#%7a5*ArR9OE>ar?R0(Kqb^MUz{xHE`toD+f^Oyz*z zdS>@=8vetmnL#7bf&iwUE3UV-Hm=9gVNXKsD!Fq6dvJ4a`;REXU2(*e{`PJmh@&WT zZm257)l1^0Yb#)XiCK%21CS3b;mCF!TD2xBDLEAe=m}|eCq)WRs~Q8n9Z>y9iY-(b z@T=^Dz7_^3{rGz=g-_;B&;<(z<1uk_$l+UW#<5w&tkA#~i!RU}sUg{wU}@pkFuR+L zDUr z@ZTYARhJXr#VB`}B1xj+*PxU@9|af^aSA?!)gIEiZ#G5; z8kwdP?8W^iZj{QSHD@ki%t4Dk+SNPI&#fs4L;jkS{p8Rc^??dXD0)qi{Wp|5)OonH;Zltmd86IoA_@bkkFPzkXDGB>*AJWuKDd-&GtXgnVR2oMyTv?6Zcvk zTPo?t^k(kx-NAXM7Hv8H#p2_H9G6m=ElI8-v7X3X4(F}tSdA!iN@a=@-dp2-?%Id- zZamdunOLO_hRw|@csx3R$ATEjaw-c(zLMtPiDqIs17ypfNx9~m;(w!tb#D=A?Y~i) zne5c;e9gzrSo!3l1xI|ci)2I>U}I0p%ndGrgm@zZh-66X$|%O!Q?|EIji+Nk|( zVpKzcLQgzY7xsP9tihAoZKo{*0Vq>W=MVYeJ zfCfqvdwH@y+8-L#*p)$80nXZsVTj{+A|-QiOu>b!>L{}U0@DMazE;pEZZPyPa)s!d zc{S*OwSS?<BT?*kK*j}RU*4!O*sKZkJrVfPJS{r^yOu)VuneI~aDM#>PCxwT z^GJ;->3Ww-T@N9H+z8atdzn^$fYg3VQ`KNX`neIgBW;#tZT9*7P7;(Yi9z=zEn*N7 zhu87G%gGqN`X;>2Qg~BTqp4NJxU8U%>M(QdA1(U%QRQ-8`EP9Xq|Y&o;ZvXE+=F>nGgM<4aNLT?#iQeE0>vCBVh1q zo)ri)GzOqY1M@#}qOZK4zWXC-1<3b^j5wyty<>`u4Mh zD4b$JWU({0^(|%0;%myOcB8E_2bUpbTw0$uYEXHD+!w-)eii)^LHJL|tNAC`tyQ($ zjmM6s8}n+z?FTo6zA2G2<_LJUWo^R_kYPcm$K0ll%>CEs`eeGMtiBm5i-g`3|E`aR|RVA%142kKDD6fQB+} z94#Qe>82lcx{(+KTr5`_zFxwq0AG-kxpQhf{n>SeN~ziu%nVCxw$ny0(5DEfxtsnN z;L6jayYou@oB|1A4NXg>XVJ)TN_<{)9h+0ysNVTvC9f?0!&wKf9VJbvjOxJ2Zo}CrelYa^pug93t zF4k8Qp6PVibBs!VGt?PWelM3{!8PG=Geaf`i=+1CJc~Naq+#_ zZZbcD?FSOj5W8>}<|C{+rKV2LT(ZxTA_O&-t>@|jA4LWx%Lbj4AgXTmuz42<< z%gQ~{Z1d&!?Y5X1!%tKCwJzBJ|`LwJD?gDVL?qwkw~x>jJv`<1V8ng(q1YizOJj>KATvevvK-ZqD^j%u-EPRmoM~jdseIhB%EowXvX(gJy?`Xp( zq}Qu<_n#X8)F2=td5&X{V=rj$?`ed|j|6`t{_%H)j($Nwj%UFb32qg!c;wiSr`tSd}!Z z50viG^)Dd<%Mbaiyw^pZCij~+ToBdr$BB!8ry9-n?m}yf2Q`!Ur8MW!#bT;MiS{?y z8OuJXKC{~l5>kh|BH02V*D6T{v~R*JsHX0RM=?CP*DOogQHDu-MRfzsF*>fDSrmIr z3Cqgx#p1l%{0n{K+?izL)yW+8d&EpLlBeu^0q4`B&}s{1R3pt@u$;H-OM)lV zUnu4`N!J}X;wg~si6PO_1 z+CS8 z)R1Fc_vJWZ6!2&H-(0|L^a4Iz&)m1f-J6{GXMQ9LskW)cp+6x8vWdcRPmNvfaU5e? zSPYqc6`7Lx;VOyDmbHvE*%_Thm(ooSv0>ha#GS8K@Gw@+>q{5`YILaAx~?Ng>mb~K zNyzc^qVVI!>H|D?=95HjE+H|2JQVN^T$Igt<~OQ6fkJnutg}4L?;fwh?e$@pcF~?t z-7!&-B`Pl2?msYupjDW>E=X6Co-8E5RQkXdq+(*UXqkvCoC|teAySE}l<$&)=E+M3 zx;EYBf>yk&<&HerHbCE_sO1ruPvkiq(qpKzr-S}tnRW<=iR`^ZYm^Xz+X;P{i`p_s z#F5ciEJr}@sK2mMtF(AWrsRS?@Ov_Psv}PgR?wxDMtZ?Bk!u&JRO}tt3io~$y^`A+ z8DhIr8LtVQLNWAi#E}>wz}QT1_HNe-GPlj6GaA+lCfcpRus1HY@rSbI8<**0 zyO)2+_33D7&v*b~+In`<=6V=SvP#(Rg`5IG59h6cpdxi^&+C z(00fDW{_vTxh;*S<4VjVh(`zfh~^TIQ4-(jk9#F@enCVCrjt-bp|fr0ap?2~V^%f3uOV~L*9h+>HLg!nKNcMCze zBCvh_G;`&-*ar=4?%yuaJrrv>} z`6v(+w#`C(TT>oPp2$J@p4(7foV+w#LI@nOan*Lg*HiB_jZw#3X4gAgy*G!V!&rh~ z=_!0x!XB(QAE>{X?a4dCu5d*pGa>Wo0|_8{&G=4LomORC;OSk`zmLNoc$}u4skO%W zqjffKyq3u_ScJjorUanw;MrlzfB)|6R@HVjq~k{O&B_!TmsxlSn;P{?%FyMeW@W*e zsO%Hb_>d|XREJkRZ;0rlgGzg&cfsk4t%-ov4n4MBX5WFkMY_wXvaDy;Q8&eSo@V?2 zXGLp?&Q9swiR~Z*x5(n%$5Xvatn*zcphyFj&svO-H5NW#&~r>@^V2K}ux{M1{5uFN zJ`{W;x>j_KMKq7-S9y|%yA8B7TRv7Uvhfa;A zRij#oqxFpmyW4GsV-w|wiMTPNMa=s76ImF$4Q~iUau8q~4icDP=I{Y#O1J`6AjfEa zI{_sD4@sb;gx3wC74f z0bbQx>~8A!OH~|;jfUO9a~RH5DC>x()MP<^hwb&8E&jJ{Kx|)adPvVCQr~PZU-5H{ z;w76Uibzg1${3dV1BMQfYmqfMVet#04V^~AGF`VDlsIc>-j9(KQ$*Wjyq(X+} z@lORBX%UED%Z0Vjc{PU3tAfjBVH-+<<@3RuBHJwQxIO1MDZz$8i zH5w}k@XM8gr+yE}4|xH!Z!cy39PTJ{?VwHNK1o!~+kdeU4vI~_xjeHYGM3s&F0+(B!&!U1(ccOTO%c6of&K7TLY5s z|8AAW4#1L9kl4%jb4@R!m{7nNiW^$*H2*z$rV@jW%TTSeQKv!0*W?V%I@&dV8umn_ z;AV_eQ%IqnTvry9r^ouEqL$$e_Dkf|^qL>B{z4qZ`#GG?5`??0<$~Ot=Umgx|0yq! zy(}CnE8ChL^YO(#2GiO6tEeF)ukt7{7Vf%EHOPs!`a1I{EVDd0u`2EPAteA2&yN;n z%E$qXYWyvpI?M_Q7p*~Csy(19&HR`8icOxdz!S7SE_b`#8u&$}*4eJoXb)*vXDm#V zNS{H|PIt>r`C@%~Q6{;!IEi?=hu28aoxo`gFc|NM5||=5J1DIKd8<5Y%wJKm6qRh2 zmsVN6C3DQQN{8?!G{Tf3+|V;tnWvFk&elya*qu}%;GI%O@gBqZS%mBr(33G#GCWH+ z=^6GO5HYhDqD|qkP%i^F;^Ke0jhE6$SF#W((F_3GtHUB`Q)}K38bV~2Y(~MZgDG*I z@=1~1O0Eiwhfkm+>XGx0_J{5;1x1VLe1bpF4!N2kLa^m4nx~%p~8}xX3X6yzvrN zD=-Uqlm?ztB5>6JgseS%q|Xagf(U({T^#$dJ>6~A z8Hp{JTZ?zXCpA!>LOAUt9Gv{}Y!;`>!iE(_KT(&Fj%sECfX30jy6rWtr*r@p$57hb zJ|n3|EmLPhEOgo&KZllaCC+X-fgd0Vq^Cfp;~L#|>ZaM5VUqK%DET_%6yZU14VtXz zzq9HB1vRHC_=n49X}lB_GAmF_cX$0lA^Qk{SFE#AlO48^015)h7r`bf$tn7U+j}N6 z*5y#xfLezGzp21xyGV7j7Qi%HmWz%ol%rSWhmnBXg~@*_tP_u|c=gEv$~}C@_D@{$ zRN!rjjPHdB!I~tej-tAlI9gud+5v|0xI({O80Go!$>J*y0CM{RgHdYbXj zSD5H~ghRbEf9^{6fx#VUjSUyUuMM^RwxJoOl6h|msg#QE#zV>_7tBgZ#`B?Cky|ie zBI8_I4m%XX69=rNSS;8L&0D;C{fK8$*cp7@B*LW4rENJ=C55!prIwGt_4#Cnznw@E zWIh_|D!Xzavz(`%yBUv&N8@z|-!~CZ>!eD^AtJ z^1SB9qYTgPP7DA+uAo3Ip%I1K3QqTl=tXYwV@gg*>0I-u^Ag1_#;2G_4aYnVR5%=OT%uiRG$tMq|x+k7|4BZA) zA$u!4SlgEg5@I%|8A$$R6Tpmq3Jn;6C;t{GZ>SgPZmg;}fN&Eq?;aP$K=3ZG?WT{zm?^q$)BE2o@}lN@gy1-=%t0l;{}wxjCXIER5m z0S#!87D?;9C@XIicPd8-m!xL3jA4;H-kxS#DDP|@gmUxr8^E5;z4brxe*hZu9E)7BuWbO zA;G(|ERmeZhQy&oBnunZh{;TEWc=?~WBDL`q11!F$hYN=nn?ZM^lAMb`$J9_MXpv} znX|Z`5P!iG3-$XK8?u8u*~A`$4KrOrSY{oIsvh0C?@6Qh5^QumGl}FB0R`agBtDJ4$nYLrp{p05 z>OLnQJ>{Zgpx&Mn7BrORS%?7X-I2cV6W!Pj;)vBuMp%qmTRA>SK_x@?XM`k72>=^1 zw(Ox3y{N}Thu1H=`E=vJKGVgO>pYdU3YFBhhYoMYSGC4zvQ)nWde`w)YN)dwL~K0s z<%RFKdk!9YphPxJR^fK&47kmcD1Gx2d)W;@O~3%10_`W80xi6bTQ@2mZL@iR1i&!HWn2>Om19 zSAB3XBOV&2>af%~5TPuLM@J8L=qQhEsQ_KIL!Btk%HyTyPbk)`*x2knuhGKn`&Lny z{do=jEqz_O`a}6HcoOel_=_cH5JGN6%6UG;frk(mz48hdo5FSm*b7`n?y7Ue7P_8az88hsnsi;>E zy3_T=gZ12&$A2N`+Q4}`!uUkR7SY{h0h)e&f--7CF9rB_Vi)#jm2M1r^Mh(Ry$p-SMgz$Q>G{PQ ze%;w!F$2J=(bW-nIv}4Ad)`|&zj4U`GGv(t5sjKoakGyxTNN%V#gmQ~F?~8fkM!q* zJxPD1J7U__k8u!G5apDnL1&!1Ph+$q^L3-d%>~qImn6sUecYjJfFAG5Tj}4a2FG}N zPxeI2E|v zW_1fUE8afE!wzM%w^6WXQd8FhcRUFJ*0yKku`{)sxdF~hd`|kE)Xn!ftYpEZw7OgY zcq>O;=keIZ$BotbiDaxXDH}%vTtO@#AO?iERn?0)fEl<8)Dc*adJ3#}?Dx7r;bG|R zrsVawmKQ0=Ph!!t(YqeDO6G?ELL+}@Mn9zQq1Je`eS@h!+`Z!u2{|9Hut%PB!mkWu{5#$Z{&)(WjQ4ksybC)L5FTw8*nUY z@M@#KKYiYXS(0AHlk0YhQ}W6&H3!APeFv|rFx>3f}cYKz_L+r$g3q;#J?sXN`ka)2Fh)~j&@mopmgDKFCKMQt( zy{&c>cuuv3JH_<9@cRw;!&oU4O6H_#tDey2Gv3ndaA)zd3oA67X$#F0anY@BY0C{_ zgr0p$FviREy{J&ds|=6CTMD4U7#T?Cd!6BoRjwoZhw(bJ*kUwktUG7w_?w{#6+dui zmOAf{*Pj9!Hpyka{_HHuQGiRISRaY3MqBIkWbrif8%Zoi^u);9Tb}Kv8D^-U5+`;O zp@Jl!SDw%#qx7*4r~%e9U%{_;gk636Z<-2d>CC*s4NpOsX)oaO7o0Tnq5`b`MdwPU zSf%GzV6Er z*h|Zh9V7RoGl<)QtIdXC>a2rxe@`&aQy848X}xY)qi8wJ@t6*h^l>NU5CldSnI zs&}#UVip#?Yt7O+Sf7>u#XG*ZIl059Aa?xiTVtEpX23R|R4d^+DXPF_pTkm4$mfn^ zX+e-;0EonWS&3)Q>gNDwVa^fvCG!QU~kfa1&_Ei1bS3K}o zkyQ#nI0~kJNAoW?Y6)bX@fdW1UjyWuMR6K9W^^ zU&b$I{$t0xTK&a@4loYZyI_nVwxFpPK&sGiZwrZv$`aN!i*|{Z)=n#ZZacLT&l)h4 zZ(`noGPa{ZG*vQ6}d{ITu z452Vl0xJLY9q&9)qt|pflHub}Ww&!6TT~E1pqHU<;^1P*K~;d>U)p1c~Rd z62`=n-9}Wo6SqzJ-Ef#Cd`81Jcz1&X+sP_P3dRN+T;JUCoym&*@^TS4I^}6pNa|7g63hWXiaC;bQ#)okT zkxzp})?9>Bu%^2rlv>Mjc*Rj%6 z00@FyolI{2mhBqW%82Ee=@h7V9636+^qNpaf0NntiC}gH2hxt8;Wv@X!Q>y{M+279 zAKkex%{&kV2cMsLS4$uWZ(Wod-ou!Mf0_{zPP!};zq58yGll8J3L9dM_Z=QHTN0=E zDpMJo2e1}6bSR9=5+dHlv<5)Ykp70SMnBJI=$@^?px+Y<**$in)@d-~kb#aDjHqqd zNP3d*qNbkBpixBXU@F=@JXpXda4OAzV8dJ5@$e2@Iwi`AWO%B?i>{tN{XsO@3Di&N zobjc7;%5EW362JzN~*P@?j?^z^Jly{r|&<66QiZrtUCg`o#)YfH?$D|wSU$PKf+}z z%f6jS+lWzEbRXO$LTbnfYIo@bmch1LrdtH_`%41`WB0lS9IO~~Jq!;%)qeQ# zr<%L7zw>%7sM=8PPc<4$N)6KZ`TNkaWP<@kSBnU0DU{m8|-tT6SC=ifcyA{7D&cZjW=*4~UB0Gu^{6SnKRB zzzYY`l+FEr$ewmRuOrbu=T|8S_tkZUu2a35x{x~~0nMJq+?eDJ6EL_FHqWUhPvm2= zD;xa*f8AVX!evh!R%I|g@Fuc|E^3G20+fbMhO8}<`ECGTw#Pir03lEy>!E;CAwF*x z={uk`lRH!=fdiS`khd*>`q~OWI95 z2bZPG538oJx;96=7?EY@Fp0JSrLz;@NFAEQA;eH(r?IRew9hodrS}0Cek;@%hR>f^ zgq5e%0PjqNFx9JW!CVj7$t`(qs-b~SRN7by=TPudDaP7HFvn!D?xLLJ`K;GfFq~J^ zcd*-n2Ik_)P2m)xwf&Lt`dU=-0we!Xm1B{^`(P9S%8?Y&_poQLnDzD7H}yBGo|25Tv2`sLKvl;Gmn z$SEsNK-=4=9D_sFcio~*y>m6;EvgXoUS2Dx{($dPzO^x507pQ$zq4Y;gSwmRX} z#+DGJrFzlz(>P|DTDgBmM>)!QrblfWutPvm@Z>6Ne0z3n{0aVb76qift^ShrIpu`j zVt6LPi6Y;Y|=d_it|-eqr^TaVy_a+69O zh%Q>md{16ZMa>q*;-W4y6r{QJcY|0h`>X)!Va~e~QGKp!dlAIH&sRP3|K1p!dB6%A z+rLu$f^6ERB+@ILyiLwULAfr>+~2CCoX)xJJ-qDV;k`X>+8%g`&+Wk-CbJYp_K-oj zQgoG;IXYF^*+bZ=Q-Q;L5C(!r4pb^h1Y0zxE}CeLPlS~5XJ&|y8Zrd^rweL9NcqDq z8TjJo;^FH1K|dUn83A0RFDkvuqAfWCaZJQc^ksF8gN3tq-RA0Y@|?R&AAg!jJ|BzO)A`;9%jOQP5p?(B>5M%Doao=+ zcn{or>hDdy6DtONdPB1&W>G2ke^R1|7isJG z5 z-~JH3&ktft*E_`#7!YR+P{O^)hwnls%IRRxxHXd}!(FocEMF>>G#Dv0OdKIeyaN{@ zx@gY}jX9L-Fx#-~C1^XF@I~27>i%A0_e!jpe0~?SCNWg z>&m1P_pBml5LX(LfzI1!{fABjdTh=)LcDd}_vNwlRm3s>2UKe-Vys9Bhog6)2xKZh zy;D12G@JBz(hW9e;?1E8Gpoi|bL`n6npURuZqui|L&Kh^)cmX3ivgeJ^n!~snBjbq z=VgTmuP9I>-tLJqnGNb|pYvGI3;r<(@M;NpGg)fMdd8bjAYELU*-4rsWV;gS6|IgZ z$Rqab+aJ8yB8t*O1M@~BU1wsf-s-0IV&K<59Fw=;_GXZG$?4>Dzb0||6^o_Rd|?vw zECHEmUnF8(+6Wb0p%Q}gln7{}S(tWXK4Fx1$088g;yr+$YGKBy%B=`x)aT&c#rH6o zEut5z5538B7$hUexSEpNWnoViduvJGZBH^QAXQUU&2{|JV$b%}&6*w}Lq!C_LEPzE zaqk!YBMo!%hTdTwX1hmqL;j(8dtO0SKOL46*DW;1`@^mB_Yx^IP8KUYElfmyxi2Kp zfZumRMFMl`?IPTqGgP^%XPu6`a0RZLZRbQobQ`GWrdkfPY)VT0w>}*K7&WY+HHUHx zbR=SYi(S4Nt0fyLSMD&^tswuHRZQ}G40&Opl$m>3H~K-JxDpZ? zsawnEs^S>|IITMfDI@T&#<#BKSlaWISabmu z9qOpK>{nyQbAyFJIQDj0#sr>RqzZYVK8WiZA_K2r9HsNEr;G zT@X;Fo`dW(Srdm?q~iZ98$&@ORtcEyy_XLNSDwS0`F?>0=$L6eB>yrCx0 zi{zN)9$H0t{*c=-(|J-ro4}1{s`rMuR+Sac)N@f9>gyB!rO;q>^bv+c-`Oj+R9eHk zMoE6X;9s$6>Aa0fhC0%fv>xy`)1Tz_MPwFX_jcPjpwfFAgsLHJTKo(=Q|(2Ut&rIJ zuMmu~^p_@gKh(7InWx2f4rN?(xh`?AZ|Fb_2VsNgXw3=bKvS;FYFt!0uQ7{YFy=Y^ei{qT2nC%dipf^NZlV8Tf-cX8eYRO){8jYajeaJ`gCeE zqbPeF3{07gFH1@i;)U?RQ#KLPxK2cFGBgB57}BqrQ6wpfm`8JgFazldILXrDt-QxI zA`Ov6Oe}df%U?Sj=cGIF#U}e?$?xib)At3b_(8@5*}`}Px5W;o8w_YW8<+mziO$tL zoi2v`g)~Cym@#m&ec;1Pwe3d9H)e+cB_7scsbp_HfuVpnsJ|Ll`NQ?iP##3}YHlT# z(t@djW#JIZ_t}|BxLZO(38}p5EmEyM7A1t|>#s>V14*OI1=K1dP5*;0`=N<*L`4C9 zi%u8=R_RWqD}WC5Dt<*DJe94ALAnr5&o3Rn4C~gFl`0N(d7B++*k$vXg4ZxD#Zl9#&VY1&5$)=F!_rgnq z!4o5+-gHacdtFQevhJ`fpRtgdrdQW$5u4gg=y9<{Xs)&B7E9Jfe8r?;U z*%b!F@$RTL2*@{dKXwhxnooQ!*wcMhVgwgsmf2=4h2J2;OMqNP$;qz}RLRD#Ho5MaID9vd1X9dRF)Gys|qrZE=KgaexKJE&BCk>Y+s_ih7b( z>yM)TH8;B+D*QJ-jX+&!IYwT)LZOdfW= zu3`05@XlExULb)qg8$df3-5jEorG?c+Q}L)QU8agz?0{p*JZf5jKe&}zvH{waWsA6 zWwa3JNr;1FqQf@tM_ixj=W?KhT&=a2`S<)@h|OhTIrZx*`p0k1>;fWoFTa|^%eG;J zTDm#wTu}&>@#e4qiLX+@T=MC!p_Mdpbq&NIG2)%i(^gOjPczuP0GIFh8FJP3pDh!<7?f0u9# zn7I+nh8drp#UFIfCawNavbOySV>|2M`noD?yHYVqu}7M!2F9F-X8Z2G$NkZ3)!VFD zUXb=i;2%bG%wMv!hibm?zZ*4r$GU0J#iR8LAue*F;zr~*!I(qTSOoS&IYIn7Ps!jmeLma3XT=Q?bEOtNcCiexYEU%boxhh#bAY70zEq36$A7=?w1Xmf36sIy`L1|!iE^VF|sGW)ucOjD6Bd~2B zLmu7JPC(wgrJ~JZ1#Q?EVgaR^OPzw0Qz{@~pM6psCE8ezG=Xg`i-`b_=#74W5?Mcf zT)t4CL9BzE@5MENe`#KTW5c5MZ!6m!@PTThlR5+M(LO&A1DfMObXsmvW;d#mxu$e^ zUE_l*l1#JH&N3mq&0*swAV07@a=ZqMj@6!(wrpY4uf6Lwq)*&whF8)d&6OSh=zxh)^}wob*)6+|8jj$^oC++Tee(+cyQX+^ zj$a27?a2Wlmh+8`*=3*tW7r|&l~9-R9~lSTAM{j^W5 zn!rVO4bq+pA66WubRr#{=Zl+M2;})~2Os>~ z%27R@E7+a{B-6{WrU3NPHYskG@Vkrld;vyKcF)BrB)ze{l32TM`{T{5EJ#peJ8P}H zdR`YRV>4^*stQx!NTP0xL|uKTHy1m4nRjCxu*e!x<80sVly@~wY=v2%H8h4xYE_h= z4}O$(rV?DKEVMWKu!hJS%Nr0u&b^y^xt_4xN05N;(RAKb29}T;Wk+a^7i;7+$Xn8s zDK7?6BGSw@2fHu2d{`6bU2dB?BpPpv4HMuyAMFgs^hy%eI`nSHF60Tk(|uF7{>?KF zcev<4l5C+_NPbXLEu&bdK|oog*q(4-OP?QvgUgqgDOls)t+@z=x4*~b$1s}(!caG8 zU&n`8g!Xy2$W9y0{nNOc()^$3VX;x>zPpwQKitW5$i#io2n^M1F!Pwd;v_ZC^(P$$ z7`7$Xz|>P$W^D6fLPD7r)r*PmTi&4E8VJoi3e=Pt53_-0FCwROE$^-9inntquv2=L z7V=TzD>BN^HNID4ZpInLTr*=x z^8u-2%Mr1@>;UlJGmnP*eoDLgK|ViO^azmv)|N{B+VdV?gs$h^k2k6)2MYhvZxNA+ zS7?gvYm5vu)uGhI-y~E_MSI>St|1A%Gx3m8Xf0}&PO9e%RRcQuY+`JEeFYyXK*(`~ z4c@}s8l0LUa7<&nZz6cOBS$6eq9{N#E9BUY&R%B0(~uUYJA_Ku*Uw9O{-D2__7Ast z-T~8Lg-%fxiF;yzE>-rlBSuZj{B9L7q-kPJsI}g;8%9^}SdXR~qgCMjYXD7fcP@Ex zN)pJa2E;w`j<|=rgrh_KOVJt2qx5a#@zl?JuEyxxva;u9Pk>eC-GBBeA`4^+zJi)0&pJa%2m@U{8_HV3F==MU2-<%(jGwUYWBooPnzuuYlt*B6q|Ox#=I@ ziBINYV9hBJMxDr6)fa8_m7UO&X7f}SJ{~t$?{Ig)M=h?14F5E?TJ|Ms6LR@5dBUi^G3O~_23<3w?tW(aa zMZCmP5WLg^;HKInuLeen?ipfTZ>QKb7$k2sk=qJOwjdo;E+r_efIJ;yj&;?s{6o$s zz;tFfs;?fr7(Sz~DCFR(tvr1bHoAzoD8aQcf7A@tq|9tpyYX??_F7-Ag`od-x~+#bQU$bn4)o%JNyKghEIIE?u%QtwyXnba-)p3BQF|ta?FYSBw1N3rYUCo&!DVNx@_3| z^KdvV6h-~|?%qn-9tIt0i7*NZRIXA@X|3f)lH3c8d_y^lv-11F_(uIvj#fSXX3i2o zV-s)Bdt0o7z?Fc$wg0NhqNF&{6OOe~j1)A}U$g`V5|M$SJ2aM}yHC5;Q8zF65cj5b zynU5?@0kSm?Q#K`4jj%pM%2x|s0;wnakcQD>$?==k0jNDT&flmbkm`b45H-eb6U!;L!6;^#=;DUI+*`&$)jFQ6!3K@ROV3HcV+= z`BRm+jGi%-P;H8G(cIKmIEkcVQ+|^x{`B4a6)+fXZU#Fo1!tmhr|eL}(YV{1Httl! zD~?S?6FHMx^5j2w?nkJ~?VX)G_?B5b7#Gew{*y+|a^Q4PdN!93!9pk)=A2!+ZR469c?|r5X?!m@(#8=mS&XRg|o{JSI{%j%?3(F8} zW@eVkbUp2_XaY;tWdZ{@b9xkPJ;Wc>#fy)U6cfu_o@G&54YUX)1Z}mAAwM8bziCGZ zSs@x`&zp(D#9CMu2CJ8@QletrN&T!MNWUL{&%ClB#H4OcyVAtl3JOah^%S_+ryAuM zzM;;4r?a@W0s10sD9ELC(6Swv^b~ayv2&u|yu96J=hZiXUk_$z7=!=WnX(0Y=;~i{ zl3n~-U%wwLnf@A{gL%T}Z0a1S=_gXFf<>)Y=?O7eisffglPA7F(xmvZZgOhpfSmhc z46whS;0yQ3T=5pYY-8{*qJT}PaWjDdlWl8-Pn2DEjY;SLLr22ebo0@sC93H zekGQ2tr-~Cv)BA29f?!4(LyiakmR=})mB^9JK5$~bA5%`1Dgjjz=V5i9k;=omEgGY z+jZoPR(~>I$8Bz!PR@yXr$d&V95<+9QG%<|6C3@Jh2;2j8{qbw1`Z@lh4qrl=uRg)3ChVIt(C5UPM z@wmz5-VqBY8m*z-%64_*9^pqDA!X*QE*NqHkh$S!?!yi_1>(Z|8X2h7L6wyynO@Y> zy6{(vP?)k0KL~TH^J5SF4V_}j<`$g%Z1h5j#zYdbTQeOrCTc76VUz(OO{ZXx4Kaq- zZs2O};MKlpGw|1uz(2sj_?O#!k6A{?ag8kw==V7=`Byppk2R|S*m(pp4aW#~c;C89 z_7%Ek^IY<=10=F!zkNw3O(a4qZ}k}W5s2TV?_g_hPbdLPwW=hc^o)CADUG?;zM@YM zeyb-9DI;29w7mq&?7o*&b_)GWaR^x0WLo3#2<+|wIG^&`DweIZDLDweM#^HTutqk_uwNrek(_c*FMkHd z1yy$r(O^i!)hPkkK2~go+@bi!-hZ#h`Kb!IC28LoE;*v1V76Qi~E??!sX+`X!k!|I6bE zExFVCs~%(Rv;S5pHwH+PhUSEO%WsD2G()TZunl=eYbBas1^gd33I>|LHJytigeiXFFf4-4y>AcM}7Yh9M( z+i zG^hgl6|9~wKLIPQcHjkK4twHc=6UcM5J$Td-UcI{38&~2rUvtjc52S;*gN|&wdu=? z6!IRM%FTlI&|B8_^yC@em9OKY8CeiTup==_VqYskWKYIhXSO9C&WTk5mg-UWA8{iX zy&Y5y!OMy#d;|~&stkS$s}#F6t1@cN?hSPRi}$;6|66>i<#R@^Nt%Zmi8X z`F#vJ!Ety&2V~2oZPV%SXorFy36mNNn$bO#${&M5_L_J#2eOD1pwe?`H4W?>8KXBq zPA&y|_jF6yiU#XDp&LqU#iaVo@2j)3^QwrB>swWO%9fDtHamQmCqlCZVfoS(Cfow$ zZSGxb$W7utJj6~B0|ib&7Tv{&+KDoPYlxb%k&@Y~;zc=I7Yw_{)nDR7S_GQ#6>kig zhDRgSxR{BmeO4{US)VcKt-!M9-7-4?qIR4T8A&Jy4K`q2o#LrvnVGdNEW?bxrSmQ3_DBvVe6ZVp_sgLZq#zv)9@mXl?qth2fk?8WJY{UM} ze>j4q4QDjGm%k`#DOrRbatrS9@83*&bEb>1Fb0DaQAPM{NKUqXdb?|^;Vnp#OCZgu zU^4xo!bT#~|;tb0BrU%9$@Kh}JvgRVR_45~k}bXPHhLIz_&k_2p_=Xa*hX|cfl2njn8 zRlA1eth75zFh}F1&2+unf2Y`}+S~}B%KQdupRl@y8{Fu!C@7KaHH5`Wmsd-=KkQG4h zylA5ed*%3+QEN|jsY7DD{bf;}V#ITfZ;MWDt%D9phkZma116T8i^lS7g(-+!0N8cX?(|4q$8k zGr?;Bxh>iU7=@PU5ZS`NrA>;xP#~e?JRU0u+i+mvMVDw~Hywr$eRy{c@_n{I^CS-2 zG1vbmS;bRo~(T%c%WD+l`*$#NYtLDSo|QJC1uT}P@{H%LqK_> z5)qOcK1W>OHFToDZswt16WYXD?*`;Z!|*JmaxX z3?~QpJCL7dZ-cye%XA`vHG7$2vhNXS?3@@;4nkRPR(me}ufsP`f;_gyyY8F|p# z08CYyVL2yGN;+k3fbao3hYijTKi+uD8w#l@{%;F#=56{J&q8{HTwVMY26L83fxfpG zcoi5Ne7Daws{k;(XvSwBZv$hjfuJcAH&DCsrsAa4)UdkK)PE+v8M{hmwfl0HJsNvA|;6dF4boL^)A{7AqzGe~RDAutvdFEe8G z3+%A3arNM`TMcLB*Pgz?)q|Bkos}0#61y(2366A~rK+j_KGqpW+GAvkRcEMWTNoU* z!imEWNtHNL+V-YqmE)IZ?F4^j71J03%=kCFN-`RM$7B07b+@J@z^6u6Tz1>f;%ZTE&uhX)hkp9yo{wYo zAp`Grv2qJU>cn#sRH>g8Hw$p6ZIhXf8f8KlG*^($sa@7w{3pW~@^K{@j(s&wP;Ehs zGH-6^>)l@j2LtDa8yzs>jl)cel0tb}^_v8?I(!pNwgJ2ao(Vs}KT0zWb_+E}SxR%C zMO^r=SNQMVF*Eu3Tl_t=HpA6vpKljWzhnemrMB45=PI&yD#c& z&dkllj6gm&i2+(4!s-0bgDsqg@T4#wPD!D-sJ@AbVzqeFm)u&p!_PugD-s?i%e>(c z71!8`v5oFqi!@{?17A?HMuz?E(f6*mJ{9|ds~7*kjwttwe#+*lu85=+Cp!o%YrlzO ztv>|Gb|KbyeLJ!bAMHs?JoiHutRflZAjAdoWIG@XQ*nR8;R!&I*Mm(Jq_(cZ#IY3r zvpS*!bxI^$HlN4lzdtzk`Xbl|Yk3IaR4XR5y~3(S)^P%BJU4Hl*C%K*3*98a6@;#r8#ND$Zq}41 zEeU$+TKi!k=GQUDs+nD8V=$S!b%Frd*)(%`pWH!+0gGq0ci|%*NiaaUpSZOl)i5S# z;4jTg>*izBR1jPV6&vpO(iOwJ9p@1#*JEY5>Y01N@i&}NuoC`(>{wBo9sXvL$sDB4 z0sCe=MBVcbjfXZxygIW)xJe2dwq%r+Tq1GnM6iMMwS@!Kw6Zx)ju0W`H_C>T2M2Mk zYfVO<>3*e*p3@`IOK?8(+%g^sZ;yPxI>)BV@kdQ!|2s&_s`#auI-r+wZlMqvxy3}Q zr)WGK;(^k(Gf7R2`<~JN3IHhu(I$0@(QcAQ;Ip-_;oA3a`mZKL>wQilpln?jlWC5P zy~^l)&a+j_c!n$06%|@|e?zPi;<7zjOPw$#tg(EhvC_F1E*a+oyLPwv0+FY@Y5wue zq|QtE6B3QCr_sb-0auGhy!J}{rP2%|P3h*!n)1G*3`OeN{BmebQiB^Z%|r!Pv!66Y zs*IJfsB6KO^%*}EIM;x)vn3U7MKyM-{$G>`hOI4GWz`~Iue0bSFS1Z?O{^@_@{=Fp zy8+TP;bv1IS$%&~m!XIsoOXr@BK+)c+_rWm&z=tC%i=k)RuSo`O~IS*hH0!DnA{c? z)k2Y9fhbPEjzN$&Fn?SeNsCWc6xmL!X8f%DKyjD-rD*cgIFvC5o#Hd}B0flY<^;8q zN8W4v_kq9wy=)@Gj3CyI;)fMG>KBBsGf`SD;mg~{hL>G+mAWfmD`uytpAcd()6K2i*j_WPstZj$hMI*OVC>X!J(f7ZlEI0^DtR*8bI7S+)>AIoDsVC;r#8;!*JwAMgjJoIE*SUxI@4M!;y)znqBB2a&8r3 z4A^2E!XbK&GQj?pL60X&(1R2LT zQ+?7?JJ@#Ts3N!nDauxbO+6O!)*CieM8zU0d>8& z!kn6mwoRy*7{^?OwHRCyrDw_3prT_v4)VE^3aI782Csi6J%>=QhOrwW+&D@4#t-kB z?$r);J?=tDBlJ^w(v}~LX=PkZ3$0p*bWL{Ya}I)*(aixVS(aa7tLG*f`~1L|GFs6V zH@I@GP)1VZ=OSUMxDC-gFot6k{2YO0>KjV(_|!4&=({C$q3Pg51QuDFkHtfn4k5Xj zey!2XSkL(P&tUMA;RN+iuC+?s`{G@-_6m&}q$K88m!qVb@9OWkqdMtyMr9lZ8aDop zeE2tiMz%c94gV_7!%9QA5Nz`nEA4W=VSF)R#QUl65T!|8dzW3`|BEv6(zmfqvZorh zaEFv_(ANHN`TqQz=oZMK<=`7gf5)VQKi@hI9>eG_O}lC;!_|f z0oI(ua=hY+lg^Ts#pq?Wb_n5#L#m4%hU14u8Zi=$Jd}t{6-LK@`qXOQ^Lx~ zt)Qhcm^1bVsE_rFoq4HqZAW=?k5y#S$}bjkmD03`gt6f*XW22#O7x_nQd*0`2&y^d z>2g_j9kBj_E`WpS>XAW$+jB}R1tA!(RlvSGR*UpLt8cTmXYO)lrZgP*1yL4T)j4(G zIuTPWt>33)s}lcCY*!xK62YfgzLIwTICwYdYy?gC#Xvqz?sGlzFYE0$!j6@t-wb*bKuK< z=uJ|~SBz-hX@s+syUl~=C*|Gb;@bY^Tz zb*@+wuq#e{_!`mDEdF7j1%Of-9IVV2Ry>j7>xoeqHjhrg$jCiPp+(l;!n#G=HYI9c zI<~P}&3b-ig7)U+%*WJJj{=XnU%FXrxK_O@SOmdl#4b3=8;UJSqa{y+hhu)=q(Kii zc~UhW4U@Ar4H2LQw2PK;tuF4ED$rZkiiFJc0MVQ1B=|SMT(Z1WAOH^mGU7XQ41<(( z2?Inu=Cg!pU)IrsHz%QV)r0G2b4uB?Bc`9mmp_La*8!QL9v?{!&}JJeW(ZSPChuoli; z(x0z9eMcaqq4rm!UjQ0r%eJUg;i98f?-`sl8Q|izq({<7-h6Ni-8?zU8MbS#Qzp0! zgsJ=OWNPiQf2Poc6&3?%2Zu=wqkAhNQ8qYQ?AU8GWpLUqj8i61ef==n?4e~wqjKRy zjdrM2z*zI3m!FXsz%xv}94N>LIp-d#V-@lA-#XNHPgJ$;N_`G`m^+w~Q0(5A3aCor zr08w)daR?OQ}HfeR;R$L3W^NvHyxr`0WMQ(@tO2U(RT;Tgvv7L*c$woCu?kbKaW%3 zUKAH9YQ-2g4c~ac&xz~BBC&sTn*Yy&DM;xqo*laLS^Pmw4h@Xu6xeH7)SK}wS(mN9 z{O>#$hWy2rLCGI}gs-0WPcZgI5?tsP(0z=raX-&Ct17lyQo;dziZY-`LS-I&-ohFk zILch2blK&h#RAsF~*!)VwB@)5?yV62F0 z)VUu^gvwYVQn11I2~cdM@1fRQu+1sxbcHpxzkGj|-KC}R0B2onv1UaIChNSFXg*iV z`_*Z;z&bO94egxuD|g(&6SfDuhjfqTOLSh9HqNQ`Z*GNT3}#OJ_%7pSCh=^KG#OL& z$3f(0DM@a%?cm7W{SZJ8wbHe#S-TbyX&2;G!VOB^gD3-e28>jQkUYDISojj3j6Z~D zvdO=Gn;cV-qQ!r>5EI-?KQi6E`7smOlH|z)XuJ7P_o0d4NNk}GpS*Ac-&tCv$L%SO z53;LQP`5<^=g<^-%M&^Y2U;it)(sSFvs^)3cb#KZ^EnsLLD>~0xU^j!nG$QIwt8|H ztFt;-ImAml^{FqZKwYjE)q7}BRpoPX$5raSXhISF-Pm4nlpbH5EtX(ta*{i@y*u~$(+vHwt&Jgf5O~wc(Xs4jidCt454OM#1FB@P#?!DijPdrRZ|mA07VX-mE-w#-}l!<%kH$ZO(09HQi1j$T~UhDwOSR^o2FG zaGVWO_Quf6I>WQe8UxSL(9_x4m+62rZa*Z4gYV?3c&%Fe-i7|<01Yc|=*^bz-yVnz zB_L2+i%(hacM{A?sr%GK_o|!G$Agn0yX4HC`?`pHxf0SIQOy-;p(3^i;r)>0^;FbY z)T44BTGQ&#h_F!=b7oL3)-F9cing5y-#3gZs1-3>mWVM_DCIjdSv4$1NV_oNHaAX0 z{FX7<_|#o1_j~ibc|7t8)P-l;z&YIap@!r9!kjCVsGNQ$f9@0U#|IGwdh+Or`?mtZzVuOei6TM7Zw0eIn!*9ws6C>z>_o00vseo z-p;-dXv!R=` z{W^m{-m+bfP1oJ>G7>YipdIQ%oMEU7+Pg|KNbb^QauP-993f4>m6b6yaUF0L9YP*HCqJePyvg|HtLQjB*>l#u9cq&bty@;>^gm`Cg{KZvuaZ!>x|2FS8j1cL3N z&yKds2+aEA?LQcQUt1m|v8H5+hFC_XY0lkkceQ91;#SbrBdk)PL#WVd)KD(*oVFh|z(xziX@3c=|tg&{eehP~CcZsv3W<}ps&2#d{D_E+V z({ANyYtKkdcCJZ#&vj;w+`PDaQX;Kzj7H-;xnCu4ywma2(NJS(Q8R_3mo*ntb&lLD zKaAuPoW?1K{XHhfNT2kXXxP3+R!n*t*54FXP|vv9zyY6!mk$#kr* zKg8TZ+*R@2N|rzPCKiT4xV@J&6++zOcWuhab28=38M{A$Q6`A(%|bRlA0(rA%`qvo zstfO@T0cCF-6JB@mi|MB^DW8jNKHZQ`3wt%?B8F-WQD>>JgXc+74I&EefC7%pD1j; z-;~I`)kS06`*Du9~;gI^W(`(xTg&ARnW-`$} z_p&;xZS9Qiq^Ie}f1FyrK+E#?-q+^Tuqq8QG@6-zN5}2b989!qbO8EF8b^oXdv!8| z!l+y-Av8MI`Mg)Lu^(+n%efO`N1W?geDw-wE|cBwQbj=bxWnSJAc;lfi4NR|DwsnM zORFp6*}Dl?4qVSbNCTfMxvJ!8w9_V znkGMyVcYh$ttpZPI{FVY)#v+RiC;Z$nCZ-Z$cW90tqk||g{MBEUO6$|gr46@TsKzp z@I8)mTUZNRfc*d`f7aBEy2_qw!-~GvYB2fqO=8k&A;sT5}Zg^27{9=`7tYL0D zM=p2PDbWwg+Ksd@8L>89U5u1RhRB*8G8}+VPkCb`JXvu>m?i@V6w&}xcHK^}-GEs? zni6LV#gJtecYrvC8gfVbuM7w=%i)|T+_{^fkIv$Q64GAJRgpFo*-Hr&_zI@AVqNC+ zf<~V)JIZP{>_Q1?Oh0XWUJTAzCOuZO9{uJ+*6({Vt_$nkz?eE_j`A+?qyg0jQW5s> zow|}fuDQIKBS3b%_bnAfFSj$GRv)X{uC% zy&{=?N!S!TqT;>TCu+*GPAQ*Fl6ln>0d0~?3GKylc^Tw^?g-QwvVCMa%_6K-LWxW{ zv#1=Aoky*GgJ!^Bm33s%XSb5L^nRE1&19_~g7qHzsky}M@pFyx2km-MkS|M2)6i8i z1Q;=I8R;UU%kYShBgus+3(8H|Sd2%EY7!D`{c@$)c}Luk%l6GTJ&&XC2LTT)fq?c#q2+AsB~zE7k=9_`{e{kQQ#raW<(tLO)v(!_ z!nHa0_Po1Eetw43BWZ@Rqu)L&G@jq>I-jMkuNo=;6)SxFiN1PnzpN0|$Cl|>t8!sl=st%0X}MRp!`ZM}71Fmjza!*fvaJWmmdC=* zoINO}j2HFj&`;R;@dWqF<`2}T51Z9H*hh?)kQ48KjNzwPSHzwK1SzX&-DNJ?Nw1@^ z9ZVB7CI5hOUc|4|6Tp#yGvy@JG#!2Y8A|9(&t5;Sz5@I#{QgQXJkhK2(`<9t(ZwXx zMm@kWu(8Dp2*HzUOxt0VZ4F2GU=Qx-#Z#h}Bdr{zwl>bu??;akkDXo}T0ZNxTK=#TSaA;s8CSAxlp5;hgaG=|1Ka?{Ffu@!;0ER-JSI?DFBlrSg?F8|I| zA5SS>iwEBFh=U_{-m_*wG&=@5X@u1P5s<48KZ67deUh=w7WE+yy$(8z%%(qkz;T{e z5uZ89P4qOKQ~x3gt_^JZn>-rwd|Io`P;5HqV@D?$f+VeeLYJO<)vz3AfD_@&=O2yL z&QrTnn+icrl{5(`X@-@J%H0ZX_`eEK z!xxX#%U1JXKNbpw%m^rQxqZ2m@6Pr>lVM3hsJH=IEmG4x45ljNJ3GP29)C|}pf->Z z$wPs1SauEm{5C5zCb>E(%g*4A{YjRU41pY7{lEWQ*1E)5e8c&d^WNtS55JG)*Qaz`piU zM&C%JXzvG^o)#lLE!AJA8wGX9nXMG?=scO*aY7@Baw?}(RWEUn6WSabbY#wK?pI!E zw&3XaZWDuk=Z2QYQNFkUTptJe(rIt(UX!sIDJ;AQJQDim|7gidI%XXhF=7VDhg%DS zqoZuo-l5#U3x0YR7etP>6y3weYV_$^x~)rmCCr$44Edjh5>hcUG-DI`P@L)oP~G`P^M`_J~fqhZ0aW=D$U z5Zfh$bc70B<-VI!2@M?p@C*t_G(y=HRQBQ4YX>8Jkx+G5u6TzsJDJRjt3YA5k3D- z%^!2?q8(o*D`&wKG)Yz`Fe2Gcjjvl=?L(*7jKqb$yi+>#10Jha_D8d9@{QJ;qk)$4 zAvyYi3s)^nIX@u^8zy{NQKLKRqGNB@T~O@MmWqa!Hd)kz zQ81j7*U5BAvNll)X&_EB!(wcA;ML0`uG|03Xj+`UB|blRWzgD!?M|Bb{>D9T&Yuz^ zlahzeTSOGt=HW9qft#l1_h|`c5?PP zo37Xd!T7~aI9)|v4n5g$Xa+6{3V%VrmMX>Veb&J<83_x5hC8WtDYl;` z{@Phw>TcY2t@>wpup4-Q;i`o3Vw22iQ|b znW0$bQJs&4m?nT6CkAwr^(`Bypqw8QWr5cv|rx=N#K2=9$4)kQs8<@%-TRAGh+Pyv-aV3 z;aC9%PGQfsLZ552%2dMt4sh-ca=>EhJJjg#~njnV!IW|RE6|x z8|A+wQ>@I*PkMUsy>t>KqlzrFBFn!BDnBrZFD-x)!>&-q7ISaEHum?O&Q#QTPB4N( z|7QKk8Zd+#q~LF4x8+ceVqCX@miI#14XPcx>K8JEFJEkp8b5&KN$1fErm@zbvuG|8 zt`0W;Ok;`t5kTPSmKKzz;=PK52JKdSu5%dOnTcga$l21TVn83qX1$&&S@rEeRuT@F zvhnHNYPgj6$&?D0@qS?Yg@gn)2xXG-J6xP!ykx}$gg5M#YQ!0N{1VB|ew27`v4XV- z@T`CQFht@bG}cd8S&9*@^v#x&R=dlqPEdIlg(Ubpo@GIldUfy-V(#1oq zPpp0NSJkF*Q3gkQjg()~EECr2Vm7EP7g%qk0qg@a$d9DAbiID6%iJRs3VLmftwl5g zm%F}Q1ygIYp=QRH{iDZ?KdCFbtt}BXQD4c1-cusss5W1Kg4e!8SXj&SwCpQmWV>AclWB2xR{O$F2cq; zWukt2OtH?JtsWnyJd}p$ytu3E#bgWgG*S01B`1)kzbKXOAm#J4&!`K$1Zx~dj1exK zQx?o6s4p$-v`HXzE86}R4^1`yS31bzh+jxsvWA!<+_&8>CMe@~%cZV@S8W|5pLT04 zdiM`|3U>ZM0UqhD5$7-Ld{|X1A~cw~5RtDh;&ggJ_Oz8w;xPKQr!{@%qfkycwjVg= z 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

OBFqTj=TCxp<>px>sojZ}Zm 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 -#include -#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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(tws)) - { - result += tw->getSelectedFilterEntries(); - } - - return result; -} - -void ScriptTimeline::slotDeleteSelectedTopics() -{ - int topicCount = 0; - - const QList tws = getTrackWidgets(); - for (const TrackWidget* tw : std::as_const(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 : std::as_const(tws)) - { - - const QVector& ftws = tw->filterLines(); - - for (const FilterTrackWidget* ftw : std::as_const(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 : std::as_const(tws)) - { - TrackContentWidget* tcw = tw->getTrackContentWidget(); - tcw->deleteSelectedTopics(); - } -} - -void ScriptTimeline::slotCopyTopics() -{ - const QList tws = getTrackWidgets(); - int firstTeTime = -1; - - for (const TrackWidget* tw : std::as_const(tws)) - { - TrackContentWidget* tcw = tw->getTrackContentWidget(); - - const QVector& selected = tcw->getSelectedEntries(); - - if (tcw->getSelectedEntries().isEmpty()) - continue; - - for (const TrackEntry* te : std::as_const(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 : std::as_const(tws)) - { - TrackContentWidget* tcw = tw->getTrackContentWidget(); - if (!tcw->canMoveAll(direction * factor)) - { - QApplication::beep(); - qWarning() << "cols and/or parls @" << tcw; - return; - } - } - - for (TrackWidget* tw : std::as_const(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 : std::as_const(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(); -} -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/script_timeline.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/script_timeline.h deleted file mode 100644 index 5cf389968..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/script_timeline.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -#include "ltsscripttimeline_export.h" -#include "track_entry_painters.h" - -#include -#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(); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h deleted file mode 100644 index b65c76375..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/temporary_pointer_vector.h +++ /dev/null @@ -1,43 +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(); - } -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.cpp deleted file mode 100644 index 3edbb0372..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.cpp +++ /dev/null @@ -1,75 +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); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.h deleted file mode 100644 index a7ab4f8c1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/tools.h +++ /dev/null @@ -1,29 +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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.cpp deleted file mode 100644 index 8ba470bbd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.cpp +++ /dev/null @@ -1,1430 +0,0 @@ -#include "track_content_widget.h" - -#include "filter_background_widget.h" -#include "script_timeline.h" -#include "temporary_pointer_vector.h" -#include "tools.h" -#include "track_drag_handler.h" -#include "track_entry.h" -#include "track_entry_painters.h" -#include "track_resize_handler.h" -#include "track_select_handler.h" -#include "track_widget.h" - -#include -#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).toJson(QJsonDocument::Compact); - 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::fromJson(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 : std::as_const(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 : std::as_const(_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 : std::as_const(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 : std::as_const(previewCopy)) - { - - int startOffset_s = previewEntry->m_startTime; - - if (!hack) - startOffset_s -= startTimeMostLeft_s; - - for (TrackEntry* pTrackEntry : std::as_const(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() << ">>" << step << ".1 type" << (int)pTE->m_type - << "in type list - not removing"; - return x; - }); - - if (x == differentParallelForbidden.constEnd()) - qDebug() << ">>" << step << ".2 type" << (int)pTE->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 : std::as_const(previewCopy)) - { - correspondingDifferentTopicTypes.clear(); - - int startOffset_s = previewEntry->m_startTime; - - if (!hack) - startOffset_s -= startTimeMostLeft_s; - - for (const QPair& pair : std::as_const(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 : std::as_const(correspondingDifferentTopicTypes)) - { - - for (TrackEntry* pTrackEntry : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(tes)) - { - bool found = false; - - for (TrackEntry* thisTe : std::as_const(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 : std::as_const(m_entryList)) - { - if (pPreview->m_topicId == pTE->m_topicId) - { - hasJustBeenAddedByUser = false; - break; - } - } - - for (const auto& tcw : std::as_const(m_tcwCorresponding)) - { - for (auto pTE : std::as_const(tcw->m_entryList)) - { - if (pPreview->m_topicId == pTE->m_topicId) - { - hasJustBeenAddedByUser = false; - break; - } - } - } - - if (hasJustBeenAddedByUser) - { - - m_selected.clear(); - - for (const auto& tcw : std::as_const(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 : std::as_const(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 : std::as_const(m_tcwCorresponding)) - tcw->removeTrackEntryByTopicId(prevTe); - - m_unselectAllImmune = false; - } - - dragEnd(); - - if (m_isMultiLineMultiSelectionMode) - { - - for (TrackEntry* oldSel : std::as_const(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 : std::as_const(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(); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.h deleted file mode 100644 index d32766951..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_content_widget.h +++ /dev/null @@ -1,243 +0,0 @@ -#pragma once -#include "ltsscripttimeline_export.h" - -#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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp deleted file mode 100644 index d7ece7fb5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "track_drag_handler.h" - -#include "script_timeline.h" -#include "tools.h" -#include "track_content_widget.h" -#include "track_entry.h" -#include "track_widget.h" - -#include -#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 : std::as_const(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 : std::as_const(m_tcwCorresponding)) - tcw->dragEnd(); - } - - m_tcw->dragEnd(); -} - -void DragHandler::dropEvent(QDropEvent* event) -{ - int dropPos = event->position().toPoint().x(); - int dropTime = m_tcw->m_tools->getTime(dropPos, m_tcw->m_isFilter) - m_tcw->m_dragOffsetTime; - - for (const auto& tcw : std::as_const(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->position().toPoint().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->globalPosition().toPoint()); - - 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; - } - } -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.h deleted file mode 100644 index 6501784e9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_drag_handler.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "ltsscripttimeline_export.h" - -#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; -}; -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry.h deleted file mode 100644 index e2c783931..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include "ltsscripttimeline_export.h" -#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(); - } -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp deleted file mode 100644 index 470422669..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.cpp +++ /dev/null @@ -1,328 +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); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.h deleted file mode 100644 index 2abc1bef4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_painters.h +++ /dev/null @@ -1,89 +0,0 @@ - - -#pragma once - -#include "ltsscripttimeline_export.h" - -#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); - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp deleted file mode 100644 index 0e8257fd0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "track_entry_preview_widget.h" - -#include "track_entry.h" -#include "ui_track_entry_preview_widget.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); -} -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h deleted file mode 100644 index ab19cc62e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_preview_widget.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "ltsscripttimeline_export.h" - -#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(); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp deleted file mode 100644 index 40413984c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "track_entry_previewer.h" - -#include "track_content_widget.h" -#include "track_entry.h" -#include "track_entry_painters.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()); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.h deleted file mode 100644 index c97e5f815..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_entry_previewer.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include -#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(); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.cpp deleted file mode 100644 index 1bb250977..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.cpp +++ /dev/null @@ -1,158 +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->position().toPoint().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(); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.h deleted file mode 100644 index 3aaf02d6a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_header.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include -#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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp deleted file mode 100644 index dcaf4218f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "track_resize_handler.h" - -#include "filter_background_widget.h" -#include "filter_track_widget.h" -#include "tools.h" -#include "track_content_widget.h" -#include "track_entry.h" -#include "track_widget.h" - -#include -#include -#include -#include - -namespace LtsScriptTimeline -{ -void ResizeHandler::mousePressEvent(QMouseEvent* event) -{ - if (event->buttons() != Qt::LeftButton || !m_possible) - return; - - TrackEntry* te = m_trackContentWidget->getTrackEntryOnPos(event->globalPosition().toPoint()); - - 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 : std::as_const(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 : std::as_const(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->globalPosition().toPoint()); - 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->globalPosition().toPoint()); - - 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(); - } -} -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.h deleted file mode 100644 index b5aa15f7d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_resize_handler.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "ltsscripttimeline_export.h" - -#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; -}; -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.cpp deleted file mode 100644 index 8a5008eac..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include "track_select_handler.h" - -#include "tools.h" -#include "track_content_widget.h" -#include "track_entry.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->globalPosition().toPoint()); - 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->globalPosition().toPoint()); - } -} - -void SelectHandler::mouseReleaseEvent(QMouseEvent* event) -{ - QPoint clickedPos = tcw->mapFromGlobal(event->globalPosition().toPoint()); - 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); - } -} -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.h deleted file mode 100644 index 6249d5aab..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_select_handler.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "ltsscripttimeline_export.h" - -#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(); -}; -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.cpp deleted file mode 100644 index ccd47d77d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.cpp +++ /dev/null @@ -1,476 +0,0 @@ -#include "track_widget.h" - -#include "filter_background_widget.h" -#include "filter_track_widget.h" -#include "tools.h" -#include "track_content_widget.h" -#include "track_entry.h" -#include "ui_track_widget.h" - -#include -#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::checkStateChanged, 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 : std::as_const(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 : std::as_const(m_filterLines)) - ftw->fbw()->trackEntryAdded(te); - - emit signalTrackEntryAdded(te); -} - -void TrackWidget::slotTrackEntryDeleted(const int topicId) -{ - - for (FilterTrackWidget* ftw : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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(); -} - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.h deleted file mode 100644 index afbb795ce..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsScriptTimeline/track_widget.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "ltsscripttimeline_export.h" -#include "track_entry_painters.h" - -#include -#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); -}; - -} // namespace LtsScriptTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/CMakeLists.txt deleted file mode 100755 index 8de54826d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/CMakeLists.txt +++ /dev/null @@ -1,156 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# Created by Mike Wille, SLSE, 2016-05-15 -# - -# RTI Connext DDS - uses IMPORTED targets from parent CMakeLists.txt -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# BUILD_RTI is set by parent CMakeLists.txt - do not override here -# RTI paths are also set by parent and imported targets are available - -# LtsSessionDataDds (always built, with stub implementation for non-RTI builds) -SET(LtsSessionDataDds_SRCS - lts_session_data_dds.cpp - lts_session_data_dds.h -) -NGL_ADD_LIBRARY2(WS LtsSessionDataDds SHARED ${LtsSessionDataDds_SRCS}) -set_target_properties(LtsSessionDataDds PROPERTIES AUTOMOC ON) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSessionDataDds - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} -) -NGL_TARGET_LINK_LIBRARIES(LtsSessionDataDds - PRIVATE - $<$:$,ddsconnector,>> - PUBLIC - $<$:RTI::nddscore> - $<$:ddsQoS> - # Generated Core type support (topic_type_support, ...) - $<$:$,ddscore,>> - NGLLogger - Qt6::Core -) -IF(BUILD_RTI) - NGL_TARGET_INCLUDE_DIRECTORIES(LtsSessionDataDds - PUBLIC - ${RTI_INCLUDE_DIR} - ${CMAKE_SOURCE_DIR}/Lts - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_BINARY_DIR}/Lts/DDS - ) - # ddscore target generates core_dtc_types.h - ADD_DEPENDENCIES(LtsSessionDataDds ddscore) - NGL_TARGET_LINK_DIRECTORIES(LtsSessionDataDds - PRIVATE - ${RTI_LIB_DIR} - ) -ENDIF() -IF(BUILD_RTI) - TARGET_COMPILE_DEFINITIONS(LtsSessionDataDds PUBLIC BUILD_RTI) -ENDIF() - - - -# LtsSession (RTI dependencies optional) -SET(ltssession_SRCS - lts_session.h - lts_session.cpp - lts_session_lock_handler.h - lts_session_manager.h - lts_session_manager.cpp - lts_session_lock_handler.cpp -) - -NGL_ADD_LIBRARY2(WS LtsSession SHARED ${ltssession_SRCS}) -NGL_TARGET_INCLUDE_DIRECTORIES(LtsSession - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../ - ${CMAKE_CURRENT_SOURCE_DIR}/../LtsUtils/LtsUtilsGui -) - -# Optional external dependencies -# Prefer the in-tree target: a fresh build tree does not have svcCoreClient -# installed in /usr/local/lib, and the Core::Interface::Ftc::create symbols -# referenced at link time live in libsvcCoreClient.so -IF(TARGET svcCoreClient) - SET(SVCCORECLIENT_LIBRARY svcCoreClient) -ELSE() - FIND_LIBRARY(SVCCORECLIENT_LIBRARY NAMES svcCoreClient PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH) -ENDIF() -FIND_LIBRARY(OHPONLINEHELP_LIBRARY NAMES OhpOnlineHelpViewerGuiPluginInterface PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH) - -SET(LtsSession_PRIVATE_LIBS - Qt6::Network - LtsMultiMonitor - LtsSessionData - LtsUtilsGuiDds # ReqHelpr & for Debug Menu ! TODO: rm on release! - LtsNotification - $,Qt6::Svg,> -) -IF(SVCCORECLIENT_LIBRARY) - LIST(APPEND LtsSession_PRIVATE_LIBS ${SVCCORECLIENT_LIBRARY}) -ENDIF() -IF(OHPONLINEHELP_LIBRARY) - LIST(APPEND LtsSession_PRIVATE_LIBS ${OHPONLINEHELP_LIBRARY}) -ENDIF() - -NGL_TARGET_LINK_LIBRARIES(LtsSession - PRIVATE - ${LtsSession_PRIVATE_LIBS} - PUBLIC - LtsWidgets - Qt6::Core - GuiPluginInterface - DataPluginInterface - NGLLogger - ProjectConfig - LtsSessionDataDds # Always link, even in stub mode -) -IF(BUILD_RTI) - NGL_TARGET_LINK_LIBRARIES(LtsSession - PRIVATE - ddsconnector - PUBLIC - RTI::nddscore - ) - TARGET_COMPILE_DEFINITIONS(LtsSession PUBLIC BUILD_RTI) -ENDIF() - - -# 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}/../ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsSessionData - PUBLIC - Qt6::Core - Qt6::Gui # QPixmap as Storage for Profile Picture - ) -endif() -IF(BUILD_RTI) - NGL_TARGET_LINK_LIBRARIES(LtsSessionData - PUBLIC - RTI::nddscore - ) - TARGET_COMPILE_DEFINITIONS(LtsSessionData PUBLIC BUILD_RTI) -ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp deleted file mode 100644 index 10875a013..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp +++ /dev/null @@ -1,1843 +0,0 @@ -/** - * @RDE_HEADER@ - * Keeps the DDS Connector and common data about the session - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-05-15 - */ - -#include "lts_session.h" -#include - -#ifdef BUILD_RTI -#include "lts_session_data_dds.h" -#include "lts_session_lock_handler.h" -#endif -// Stub logging macros when BUILD_RTI=OFF -#ifndef BUILD_RTI -// Logging deaktivieren - qDebug() wird verwendet weil es operator<< für QString unterstützt -#define DEBUG_LOG(logger) if (false) qDebug() -#define WARNING_LOG(logger) if (false) qDebug() -#define ERROR_LOG(logger) if (false) qDebug() -#define INFO_LOG(logger) if (false) qDebug() -#define TRACE_LOG(logger) if (false) qDebug() -#define LOGFUNCTION "" -#endif -#include "lts_session_manager.h" - -#include -#include -#include -#include -#include -#include -#include // app icons -#include // TODO: rm on release ! -#include -#include -#include -#include -#include -#include -// Complete type needed for dynamic_pointer_cast of m_ohvIfc (BUILD_RTI code below); -// the header is self-contained (Qt only), the FilterManagement plugin is only the implementor. -#ifdef BUILD_RTI -#include // XXX: grrr.. -#endif -#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; - -#ifdef BUILD_RTI -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++) -#else -LtsSession::LtsSession() - : 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(nullptr), m_hidden(false), m_loadedFromSavegame(false), m_foreverInvisible(false), - m_id(s_nextSessionId++) -#endif -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); - - connect(this, &LtsSession::signalDoneInitializingSession, this, &LtsSession::slotStopWaitDialog); - connect(this, &LtsSession::signalInitializationAborted, this, &LtsSession::slotStopWaitDialog); - - // preparations for stack connect - connect(this, &LtsSession::_signalDoneInitializingSession, this, [this]() { - // if (m_initDone) - // qWarning() << "blub"; - disconnect(this, &LtsSession::_signalDoneInitializingSession, this, nullptr); - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - 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); - // [Qatar MNQ-474] Crash open Script Editor application Hack - if (strcmp(PROJECTBUILD, "qatarmout") == 0 || strcmp(PROJECTBUILD, "qatar") == 0) - { -#ifdef BUILD_RTI - DEBUG_LOG(m_logger) << "qatarmout Timer = 4000"; -#endif - m_scriptEditorTimer->setInterval(4000); - } - else - { -#ifdef BUILD_RTI - DEBUG_LOG(m_logger) << "qatarmout Timer = 0"; -#endif - m_scriptEditorTimer->setInterval(0); - } - - m_scriptEditorTimer->setSingleShot(true); -#ifdef BUILD_RTI - connect(m_scriptEditorTimer, &QTimer::timeout, this, &LtsSession::delayScriptEditorStart); -#endif -} - -LtsSession::~LtsSession() -{ - // qCritical() << "~LtsSession"; - - // delete m_mainBackground; parented on hiddenWidget! -#ifdef BUILD_RTI - delete m_baseLayout; // TODO: deleteLater? problem: clear interfaces! - // qWarning() << this << "deleted" << m_baseLayout; -#endif - - // Don't: qDeleteAll(m_notiCtlList); - - // make sure to unref the data plugins, as they carry this LtsSession, and the erase method would not delete the - // Session (avoid circular dependencies) -#ifdef BUILD_RTI - clearGuiPluginInterfaces(); - clearDataPluginInterfaces(); -#endif - -#ifdef BUILD_RTI - delete m_exerciseData; -#endif -} - -QString LtsSession::toString() const -{ - QString str; - str.append(QStringLiteral("LtsSession: this=%1").arg(QString::number(reinterpret_cast(this)))); -#ifdef BUILD_RTI - str.append(QStringLiteral(", domainId=%1, partitionId=%2").arg(domainId()).arg(partitionId())); -#endif - str.append(QStringLiteral(", workspaceId='%1'").arg(workspaceId())); -#ifdef BUILD_RTI - str.append(QStringLiteral(", appToString='%1'").arg(appToString(app()))); - str.append(QStringLiteral(", partitionType=%1, exerciseId='%2'").arg(static_cast(partitionType())).arg(exerciseId())); - str.append(QStringLiteral(", LtsSessionDataDds=%1").arg(LtsSessionDataDds::toString())); -#else - str.append(QStringLiteral(", app=Login (stub)")); - str.append(QStringLiteral(", partitionType=N/A, exerciseId=N/A")); -#endif - return str; -} - -#ifdef BUILD_RTI -QWidget* LtsSession::baseLayoutWidget() -{ - // qWarning() << "baseLayoutWidget" << m_baseLayout; - // qWarning() << "baseLayoutWidget (widget)" << static_cast(m_baseLayout); - return static_cast(m_baseLayout); -} -#else -QWidget* LtsSession::baseLayoutWidget() -{ - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::aboutToCloseTab(LayoutArea area) -{ - m_baseLayout->getTabFromLayoutArea(area)->aboutToClose(); -} -#else -void LtsSession::aboutToCloseTab(LayoutArea /*area*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::aboutToClose() -{ - emit sessionAboutToBeDestroyed(this); - - if (m_baseLayout == nullptr) - return; - - aboutToCloseTab(LayoutArea::eDialogTab); - aboutToCloseTab(LayoutArea::eInfoTab); - aboutToCloseTab(LayoutArea::eSpecialTab); - aboutToCloseTab(LayoutArea::eMainTab); -} -#else -void LtsSession::aboutToClose() -{ - emit sessionAboutToBeDestroyed(this); - // no-op in non-RTI build -} -#endif // BUILD_RTI - -void LtsSession::slotStopWaitDialog() -{ -#ifdef BUILD_RTI - disconnect(this, &LtsSession::signalDoneInitializingSession, this, &LtsSession::slotStopWaitDialog); - disconnect(this, &LtsSession::signalInitializationAborted, this, &LtsSession::slotStopWaitDialog); -#endif - - 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)); -} - -#ifdef BUILD_RTI -bool LtsSession::removeMenuAction(LtsWidgets::LtsDragIcon* icon) -{ - if (!m_baseLayout) - return false; - return m_baseLayout->removeMenuAction(icon); -} -#endif - -#ifdef BUILD_RTI -void LtsSession::setMenuVisible(bool visible) -{ - m_menuVisible = visible; - - if (m_baseLayout == nullptr) - return; - - m_baseLayout->getMenu()->setVisible(visible); -} -#else -void LtsSession::setMenuVisible(bool /*visible*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); - // Wenn die notification getötet wird, auch wieder austragen... - connect(c, &LtsNotification::LtsNotificationController::destroyed, this, - [this, c]() { m_notiCtlList.removeAll(c); }); - - return n; -} -#else -LtsDragIcon* LtsSession::addAction(const LtsWidgets::PluginData& /*plData*/, bool /*isFirstRun*/) -{ - // no-op in non-RTI build - return nullptr; -} - -LtsNotification::LtsNotification* LtsSession::createNotification(const QString& /*identifier*/) -{ - // no-op in non-RTI build - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::initializePlugins(const QVector& dataPluginList, - const QVector& guiPluginList) -{ - initializeDataPlugins(dataPluginList); - initializeGuiPlugins(guiPluginList); -} - -void LtsSession::initializeDataPlugins(const QVector& - dataPluginList) // TODO: DOC: no gui ifcs in data ifcs ! (as - // fetchInterfaces() is done before gui ifcs are loaded) -{ - 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; - } - - // super heavy hardcore hack to speed up login -> we need to check what plugins are needed for an app! - if (app() == LtsAppDescriptor::App::Login && - (!plugin->pluginName().contains(QStringLiteral("AssetManagement")) && - !plugin->pluginName().contains(QStringLiteral("OperatorManagement")) && - !plugin->pluginName().contains(QStringLiteral("ExerciseManagement")))) - continue; - - // check if plugin has to be loaded in this session type - if (!plugin->availableInPartition(partitionType())) - { - DEBUG_LOG(m_logger) << "Data plugin " << api.pluginInstance.pluginName - << " is not available in this partition type " << partitionType(); - continue; - } - - // super heavy hardcore hack to speed up login - 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); -} -#endif - -#ifdef BUILD_RTI -void LtsSession::initializeGuiPlugins(const QVector& guiPluginList) -{ - m_failedFetches.clear(); - - /* important to create gui interfaces even if they are not available in a session, - * but foreign plugins can still use their ForeignGuiInterface to use - * their windows */ - - 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; - } - - // check if plugin has to be loaded in this session type - 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; // copy flags - - if (api.pluginInstance.pluginName == QStringLiteral("dev")) - flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS; - - ifc->setFlags(flags); // durchreichen.. raw plugin => plugin instance - ifc->setOptions(api.m_options); // s.a.a. - ifc->setAppId(api.m_appId); // s.a.a. - m_guiPluginInterfaces[plugin->pluginName()] = ifc; - DEBUG_LOG(m_logger) << "Adding gui plugin " << plugin->pluginName() << " for " << partitionType() << " - " - << this; - } - - /*/ - * there are all plugin instances in this list - * if fetchInterface of a single plugin failes, that plugin instance will be removed (does not find his obligatory - plugin(s) in this list) - * a refetch has to be done, in case some other plugin(s) used that plugin which is now deleted - /*/ - QMutableMapIterator> it( - m_guiPluginInterfaces); // modify a list while iterating through it - 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(); // rewind! - - // remove from payloads list if failed, because payload might already be added before refetching, but we - // don't want payloads of plugins that failed fetching interfaces.. - 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 - { - // ERROR_LOG(m_logger) << "GUI FETCH INTERFACE OKAY!"; - - 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); // addPayload(executionPriority, guiPlugin); - // DEBUG_LOG(m_logger) << " > has payloads, adding payload request to session"; - } - } - - // DEBUG_LOG(m_logger) << " > done"; - } - } - - QTimer::singleShot(100, this, &LtsSession::slotStartPluginInit); - // executePayloads(); // does not execute when there are no payloads, but is needed to be called here ! -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; // not found! -} -#else -LtsTabWidget* LtsSession::locateWidget(QWidget* /*widgetToLocate*/, LayoutArea /*area*/) -{ - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); - } -} -#else -void LtsSession::enableBaseLayoutMenu(bool /*on*/) -{ - // no-op in non-RTI build -} - -void LtsSession::enableBaseLayoutArea(LtsWidgets::LayoutArea /*area*/, bool /*on*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -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 -{ -#ifdef BUILD_RTI - if (isReplayPartition()) - return QStringLiteral("replay_%1").arg(QString::number(partitionId())); -#endif - return m_workspaceId; // sessionManager->workspaceId(); -} - -void LtsSession::setWorkspaceId(const QString &newWorkspaceId /* <= MW: that's why I like "get" in getter names (needed to prepend "new" because of already existing method with the name "workspaceId") */) -{ -#ifdef BUILD_RTI - 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()) << "'"; -#else - Q_UNUSED(newWorkspaceId) -#endif - m_workspaceId = newWorkspaceId; // m_sessionManager->setWorkspaceId(workspaceId); - -#ifdef BUILD_RTI - if (m_baseLayout) - m_baseLayout->setWorkspaceId(workspaceId() /* use getter instead of member var, as replay partition strings can only be retrieved with the getter */); -#endif // BUILD_RTI - - emit workspaceIdChanged(newWorkspaceId); -} - -#ifdef BUILD_RTI -ExcState LtsSession::exerciseState() const -{ - return m_exerciseData ? m_exerciseData->exState : ExerciseState::eUnknown; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::connectOnlineHelpButton(LtsWidgets::LtsDialog* widget) -{ - // FIXME: HACK: also connect show online help here !! - 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(/* widget <= see comment below */); - /*/ - * Conceptional: widgets is closing, widget deletes lock handler, lock handler unlocks via ftc, - * lock handler was deleted, ftc answers to already deleted lock handler => crash - * This is a conceptional problem in all the ftc handlers - /*/ - - 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); // sync! - - // widget->setTopicLockHandler(topicLockHandler); - - connect(widget, &LtsDialog::destroyed, topicLockHandler, &TopicLockHandler::slotFinalize, Qt::QueuedConnection); - - // qWarning() << "added tlh to" << widget->windowTitle(); - - connectOnlineHelpButton(widget); -} -#else -void LtsSession::connectOnlineHelpButton(LtsWidgets::LtsDialog* widget) -{ - Q_UNUSED(widget) - // no-op in non-RTI build -} - -void LtsSession::slotStapleTopicLockHandler(LtsWidgets::LtsDialog* widget) -{ - Q_UNUSED(widget) - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); -} -#else -bool LtsSession::focusTab(const QString& menuGroup, const QString& identifier, LtsWidgets::LayoutArea area) -{ - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - return false; -} -#endif - -#ifdef BUILD_RTI -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); -} -#else -LtsWidgets::LtsDialog* LtsSession::getWidget(const QString& menuGroup, const QString& identifier, - LtsWidgets::LayoutArea area) -{ - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - return nullptr; -} -#endif - -#ifdef BUILD_RTI -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; -} -#else -bool LtsSession::widgetExists(const QString& /*menuGroup*/, const QString& /*identifier*/, LayoutArea /*area*/) const -{ - return false; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -bool LtsSession::addWidget(LtsDialog* widget, const QString& menuGroup, const QString& identifier, LayoutArea area, - bool setClosable) -{ - // TODO: operator selection + possibility to permanent select base layout until its dissolving - - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "no base layout to put a widget on! menuGroup: " << menuGroup << "ident: " << identifier; - return false; - } - - // slotStapleTopicLockHandler(widget); => already done in blw's addWidget (below!) - // FIXMe: connect(widget, &LtsDialog::signalShowOnlineHelp, - // this, &LtsSession::slotShowOnlineHelp, - // Qt::UniqueConnection) done in slotStaple.. because using LtsDialog::addDialog does NOT reach this - // code here ! - - return m_baseLayout->addWidget(widget, menuGroup, identifier, area, setClosable); -} -#else -bool LtsSession::addWidget(LtsDialog* widget, const QString& menuGroup, const QString& identifier, LayoutArea area, - bool setClosable) -{ - Q_UNUSED(widget) - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - Q_UNUSED(setClosable) - return false; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::setExerciseData(const LtsWidgets::ExerciseData& exerciseData) -{ - // avoid mutiple calls (and signal to emitted multiple times) - if ((*m_exerciseData) == exerciseData) - return; - - *m_exerciseData = exerciseData; - // partitionType(); ?!? - if (!m_baseLayout) - return; - m_baseLayout->setExerciseData(exerciseData); - emit signalExerciseDataChanged(); -} - -void LtsSession::setProfilePicture(const QPixmap& pixmap) -{ - // partitionType(); ?!? - if (!m_baseLayout) - return; - m_baseLayout->setProfilePicture(pixmap); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::makeVisible() -{ - Q_ASSERT(m_baseLayout == nullptr); - if (m_baseLayout) // onyl create a new base layout when there was not one before ! - { - qCritical() << "base layout already visible"; - return; - } - - createBaseLayout(); -} -#else -void LtsSession::makeVisible() -{ - // BUILD_RTI=OFF: No base layout to create -} -#endif - -#ifdef BUILD_RTI -void LtsSession::refetchActions() -{ - // get actions - 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)) // respect the MAIN flag, even though ACTIONS is not set - // (main, but NOT actions) [but only if this is NOT a - // refetch] - { - 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)) - { - // get gui plugin's actions (only when init was successful) - const QVector plData = ifc->getActions(); - - if (plData.isEmpty()) - DEBUG_LOG(m_logger) << i.key() << " (" << ifc->menuGroup() << ") does not provide actions"; - - // WARNING_LOG(m_logger) << " > " << plData.count() << " Actions"; - - for (const LtsWidgets::PluginData& pd : plData) - { - if (!pd.menuGroup.isEmpty() && !pd.menuText.isEmpty() && - pd.identifier.isEmpty()) // check if identifier is missing - 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 - { - // WARNING_LOG(m_logger) << " > " << plData.count() << " Actions - but ignored by flag"; - } - } - -#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; -} -#else -void LtsSession::refetchActions() -{ - // BUILD_RTI=OFF: No actions to fetch - m_firstActionsFetch = false; -} -#endif - -#ifdef BUILD_RTI -void LtsSession::slotSwap() -{ - LtsMultiMonitor::LtsMultiMonitor::getInstance()->selectAndSwap(m_baseLayout); -} -#else -void LtsSession::slotSwap() -{ - // BUILD_RTI=OFF: No base layout to swap -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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(); -} -#else -int LtsSession::screenId() const -{ - return -1; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; -} -#else -int LtsSession::screenContainerId() const -{ - return -1; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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(); -} -#else -int LtsSession::screenTypeId() const -{ - return -1; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -LtsAppDescriptor::App LtsSession::app() const -{ - return m_sessionManager ? m_sessionManager->appId() : LtsAppDescriptor::App::Login; -} -#else -LtsAppDescriptor::App LtsSession::app() const -{ - return LtsAppDescriptor::App::Login; -} -#endif - -#ifdef BUILD_RTI -bool LtsSession::event(QEvent* e) -{ - if (e->type() == QEvent::LanguageChange) - m_baseLayout->setAppName(appToString(app())); - - return LtsSessionDataDds::event(e); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::addDeveloperActions() -{ - // DEVELOPER / DEBUG MENU - { - 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) - { - // QMessageBox::information(nullptr, QString(), QStringLiteral("no admin session")); - 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); // no payloads !! TODO: is this a real domainId or a dead var ?? - 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); - } - // END DEVELOPER / DEBUG MENU -} -#endif - -#ifdef BUILD_RTI -bool LtsSession::notifyUnsavedChanges() -{ - if (m_baseLayout == nullptr) - return true; - - return m_baseLayout->notifyUnsavedChanges(); -} -#else -bool LtsSession::notifyUnsavedChanges() -{ - return true; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::createBaseLayout() -{ - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - 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; // already exists - } - - m_baseLayout = new LtsBaseLayoutWidget; // main widget - - connect(m_baseLayout, &LtsBaseLayoutWidget::signalSuccessfullyPlaced, this, [this]() { - DEBUG_LOG(m_logger) << "current APP " << appToString(this->app()); - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - if (this->app() == App::Scripts) - { - m_scriptEditorTimer->start(); - } - else - { - if (m_needsToBeSuccessfullyPlaced) - { - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - emit - _signalDoneInitializingSession(); // needs to be emitted here, as if 'm_needsToBeSuccessfullyPlaced' is - // TRUE, it was not emitted before - } - } - }); - - connect(m_baseLayout, &LtsBaseLayoutWidget::destroyed, this, [this]() { - m_baseLayout = nullptr; // sec - // qWarning() << "baseLayout deleted"; - }); - - m_baseLayout->hide(); // hide - m_baseLayout->setSessionInfo(domainId(), partitionType(), partitionId(), - workspaceId()); // this shd ptr has a different counter .. - m_baseLayout->setWindowIcon( - appToIcon(app())); // for the magic corner to have an icon to show for switching to / from secondaries - - // auto *x = new QLabel; - // x->setPixmap(m_baseLayout->windowIcon().pixmap(128, 128)); - // x->show(); - - if (app() == App::ScriptEditorEssentialsView || // NOTE: timeline's replay app and snapshot's edit plan are - // secondaries, too - setSecondaryWidget needs to be called from - // timeline & snapshot app, too - 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 : std::as_const(m_notiCtlList)) - { - if (!m_baseLayout->notificationExists(ctl->identifier())) // add if non-existent only - m_baseLayout->addNotification(ctl->createWidget()); // FIXME: crash when no icons? - } - - m_hasBaseLayout = true; // not needed anymore, but for its correctness..... - - m_baseLayout->getMenu()->setVisible(m_menuVisible); - m_baseLayout->initilizationFinished(); - - // QMessageBox::warning(nullptr, QStringLiteral("attach!"), QString()); - - if (!m_foreverInvisible) - { - // build list of irreplacable widgets by the dependency tree of its dependencies and their dependencies' - // dependencies - QVector irreplacableWidgets{baseLayoutWidget()}; - LtsSession* dependencySession = dependency(); - int runaway = 100; - while (dependencySession && runaway > 0) - { - irreplacableWidgets += dependencySession->baseLayoutWidget(); - dependencySession = dependencySession->dependency(); - runaway--; - } - - // qWarning() << "#2 irreplacableWidgets count" << irreplacableWidgets.count() << "from" << this; - - // for (QWidget *w : irreplacableWidgets) - // qWarning() << "irre" << w; - - 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; // done here if not a tablet - - LtsNotification::LtsNotification* noti = createNotification(QStringLiteral("TabletWindowSwitcher")); - - noti->setIcon(QIcon(QStringLiteral("icon:switch_display.svg"))); - noti->setAction([this]() { emit taskSwitch(this /* tell it that we are the caller, don't hide US! */); }); - - emit hideAllOtherBLWs(this); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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()); - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::delayScriptEditorStart() -{ - DEBUG_LOG(m_logger) << "LtsSession::delayScriptEditorStart "; - if (m_needsToBeSuccessfullyPlaced) - { - // qWarning() << "delayScriptEditorStart mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - emit _signalDoneInitializingSession(); // needs to be emitted here, as if 'm_needsToBeSuccessfullyPlaced' is - // TRUE, it was not emitted before - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -bool LtsSession::isChildOfSession(QWidget* child) -{ - if (!child) - { - return false; - } - return baseLayout()->isAncestorOf(child); -} -#else -bool LtsSession::isChildOfSession(QWidget* /*child*/) -{ - return false; -} -#endif // BUILD_RTI - -void LtsSession::doneInitializingSession() -{ - LtsGlobalWaitDialog::getInstance()->done(); - - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - if (m_markedForDeletion) - { - emit signalInitializationAborted(); // to call done() on global wait dialog - emit signalClose(); // session manager removes us from his list and then deletes us - return; - } - -#ifdef BUILD_RTI - if (!m_sessionManager->isHeadless()) // disallow base layout creation if app is headless - { - int sessionCount = m_sessionManager->sessionCount(); - - if (!m_hidden && - (m_hasBaseLayout || sessionCount < 1)) // show base layout if m_hasBaseLayout is true OR IF there is only - // this session (e.g. Payloads have not started any sessions) - makeVisible(); - - // if (!m_hasBaseLayout) // delete session if it's useless & empty - // { - // emit signalClose(); - // return; - // } - } -#endif - -#ifdef BUILD_RTI - // qWarning() << "emit signalDoneInitializingSession();"; - - 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; - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - // qWarning() << !m_needsToBeSuccessfullyPlaced << m_sessionManager->isHeadless() << !m_hasBaseLayout; - if (!m_needsToBeSuccessfullyPlaced || m_sessionManager->isHeadless() || - !m_hasBaseLayout) // can't be placed if without something to show .. :) - { - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - // qWarning() << "!! headless !"; - emit _signalDoneInitializingSession(); - } - - // qWarning() << "not headless !"; -#else - // Non-RTI build: always emit signal after initialization - emit _signalDoneInitializingSession(); -#endif // BUILD_RTI -} - -void LtsSession::slotShowOnlineHelp(const QString& fileName) -{ -#ifdef BUILD_RTI - if (m_ohvIfc == nullptr) - { - LtsWidgets::LtsMessageDialog::showMessage(baseLayout() /* can be 0ptr */, tr("Online Help not available.")); - return; - } - - m_ohvIfc->showHelp(fileName); -#else - Q_UNUSED(fileName) - // no-op in non-RTI build -#endif -} - -#ifdef BUILD_RTI -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); - // qWarning() << "showing operator settings"; - } - 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 !!"; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::slotStartPluginInit() -{ - m_initializeThesePlugins = m_guiPluginInterfaces; // copy - slotPluginInitDone(); -} - -void LtsSession::slotPluginInitDone() // NEVER call this directly, queue a signal to this slot ! -{ - // qWarning() << "LtsSession::slotPluginInitDone: sender" << sender(); - - if (m_pluginInitDone) - return; - - if (m_slotPluginInitDoneMutex) - { - // qWarning() << "<< never call slotPluginInitDone directly or after all initialization was done! bad sender - // was" << sender(); abort(); - return; - } - - m_slotPluginInitDoneMutex = true; // lock - - 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; // unlock - return; - } - - // qWarning() << "currently initializing ifc"< ifc = i.value(); - - if (ifc->availableInPartition(partitionType())) - { - m_currentInitIfc = ifc.get(); - // qWarning() << "set m_currentInitIfc to" << m_currentInitIfc; - - // qWarning() << "initializing" << i.key() << (void *)ifc.get(); - - ifc->init(); - if (!ifc->m_hasInit) // workaround for ifcs without init method (which needs quitting by signals) - { - // qWarning() << "resetting m_currentInitIfc to nullptr"; - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - } - else // do not initialize plugins that are not available in partition x - { - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - - m_initializeThesePlugins.erase(i); - - m_slotPluginInitDoneMutex = false; // unlock -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::slotExecuteNextPayload() -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); // show wait dialog again - - /* there is currently a problem when executing a secondary event loop, - * which leads to resending already sent signals, as they might not be - * marked as executed by Qt itself. So this is a workaround.. */ - // if (m_ignoreSignalInLoop) - // return; - // m_ignoreSignalInLoop = true; - - // execute in order - auto it1 = m_payloadPriorityMap.begin(); // largest key (see qt doc) - - if (it1 != m_payloadPriorityMap.end()) - { - std::shared_ptr ifc = (*it1); - m_payloadPriorityMap.erase( - it1); // delete first, as executePayload can be stuck here when the connection is not queued ! - - DEBUG_LOG(m_logger) << "Executing priorized Payload in Partition " << partitionId() << ", Type " - << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); // don't interrupt payload uis with the wait dialog - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub"; - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, this, [name]() { - // qWarning() << "prio-payload for" << name << "DESTROYED!!!!"; - }); - connect(m_blub, &QTimer::timeout, this, [this, name]() { - m_blub2++; - // qWarning() << "prio-payload for" << name << "running since" << m_blub2 << "seconds"; - }); - m_blub->start(1000); - - ifc->executePayload(m_mainBackground); - } - else // no priority payloads left, check for unpriorized payloads - { - auto it2 = m_payloadList.begin(); - - if (it2 != m_payloadList.end()) - { - std::shared_ptr ifc = (*it2); - m_payloadList.erase( - it2); // delete first, as executePayload can be stuck here when the connection is not queued ! - - DEBUG_LOG(m_logger) << "Executing unpriorized Payload in Partition " << partitionId() << ", Type " - << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); // don't interrupt payload uis with the wait dialog - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub"; - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, this, [name]() { - // qWarning() << "prio-payload for" << name << "DESTROYED!!!!"; - }); - connect(m_blub, &QTimer::timeout, this, [this, name]() { - m_blub2++; - // qWarning() << "prio-payload for" << name << "running since" << m_blub2 << "seconds"; - }); - m_blub->start(1000); - - ifc->executePayload(m_mainBackground); - } - else // no payloads left, delete background widget(s) and say that all plugins have been loaded - { - Q_ASSERT(m_payloadsDone == false); - m_payloadsDone = true; - - m_mainBackground = nullptr; // as it was parented to m_hiddenWidget - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub - no payloads left"; - m_blub = nullptr; - } - - // qWarning() << "no payloads left"; - - doneInitializingSession(); - } - } - - // m_ignoreSignalInLoop = false; // executing payload is done, clear the ignore-flag -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::executePayloads() -{ - // look for gui plugins (needed to get execution priority and run payloads) - if (!m_payloadPriorityMap.isEmpty() || !m_payloadList.isEmpty()) - { - // DEBUG_LOG(m_logger) << m_payloadPriorityMap.count() << " priorized payload(s), " << m_payloadList.count() << - // " unpriorized payload(s) - " << m_payloadPriorityMap.count() + m_payloadList.count() << " total."; - - // QMetaObject::invokeMethod(this, &LtsSession::slotExecuteNextPayload); - LtsWidgets::LtsGlobalWaitDialog::getInstance() - ->done(); // ugly workaround: as the next method says wait even if we call it the first time (LtsSession - // c_tor called wait already) - - slotExecuteNextPayload(); - } - else - { - // qWarning() << "this session has no payloads assigned!!"; - DEBUG_LOG(m_logger) << "no payloads"; - - if (partitionType() == IDL::PartitionType::Global) - { - m_hasBaseLayout = - true; // admin session has no payloads, so show base layout, otherwise there is nothing to show.. - } - - doneInitializingSession(); - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::clearDataPluginInterfaces() -{ - QMap> weakDataPluginInterfaces; - for (auto i = m_dataPluginInterfaces.cbegin(); i != m_dataPluginInterfaces.cend(); ++i) - { - // i.value()->clearInterfaces(); - weakDataPluginInterfaces.insert(i.key(), i.value()); - } - - DEBUG_LOG(m_logger) << "Clearing data plugin interfaces for " << partitionType() << " - " << this; - m_dataPluginInterfaces.clear(); - -#ifdef LTS_DEBUG - // in global plugins somehow are not unloaded - 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 -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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 - // in global plugins somehow are not unloaded - 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 -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -LtsSessionManager* LtsSession::getSessionManager() -{ - return m_sessionManager; -} -#endif - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp.backup_session_1172 b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp.backup_session_1172 deleted file mode 100644 index d6f833221..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.cpp.backup_session_1172 +++ /dev/null @@ -1,1822 +0,0 @@ -/** - * @RDE_HEADER@ - * Keeps the DDS Connector and common data about the session - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-05-15 - */ - -#include "lts_session.h" - -#ifdef BUILD_RTI -#include "lts_session_data_dds.h" -#include "lts_session_lock_handler.h" -#endif -// Stub logging macros when BUILD_RTI=OFF -#ifndef BUILD_RTI -// Logging deaktivieren - qDebug() wird verwendet weil es operator<< für QString unterstützt -#define DEBUG_LOG(logger) if (false) qDebug() -#define WARNING_LOG(logger) if (false) qDebug() -#define ERROR_LOG(logger) if (false) qDebug() -#define INFO_LOG(logger) if (false) qDebug() -#define TRACE_LOG(logger) if (false) qDebug() -#define LOGFUNCTION "" -#endif -#include "lts_session_manager.h" - -#include -#include -#include -#include -#include -#include -#include // app icons -#include // TODO: rm on release ! -#include -#include -#include -#include -#include -#include -#ifdef BUILD_FILTERMANAGEMENT -#include // XXX: grrr.. -#endif -#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), -#ifdef BUILD_RTI - m_exerciseData(new LtsWidgets::ExerciseData), - m_currentInitIfc(nullptr), m_slotPluginInitDoneMutex(false), m_pluginInitDone(false), -#else - m_exerciseData(nullptr), -#endif - 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); - - // preparations for stack connect - connect(this, &LtsSession::_signalDoneInitializingSession, this, [this]() { - // if (m_initDone) - // qWarning() << "blub"; - disconnect(this, &LtsSession::_signalDoneInitializingSession, this, nullptr); - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - 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); - // [Qatar MNQ-474] Crash open Script Editor application Hack - if (strcmp(PROJECTBUILD, "qatarmout") == 0 || strcmp(PROJECTBUILD, "qatar") == 0) - { -#ifdef BUILD_RTI - DEBUG_LOG(m_logger) << "qatarmout Timer = 4000"; -#endif - m_scriptEditorTimer->setInterval(4000); - } - else - { -#ifdef BUILD_RTI - DEBUG_LOG(m_logger) << "qatarmout Timer = 0"; -#endif - m_scriptEditorTimer->setInterval(0); - } - - m_scriptEditorTimer->setSingleShot(true); -#ifdef BUILD_RTI - connect(m_scriptEditorTimer, &QTimer::timeout, this, &LtsSession::delayScriptEditorStart); -#endif -} - -LtsSession::~LtsSession() -{ - // qCritical() << "~LtsSession"; - - // delete m_mainBackground; parented on hiddenWidget! -#ifdef BUILD_RTI - delete m_baseLayout; // TODO: deleteLater? problem: clear interfaces! - // qWarning() << this << "deleted" << m_baseLayout; -#endif - - // Don't: qDeleteAll(m_notiCtlList); - - // make sure to unref the data plugins, as they carry this LtsSession, and the erase method would not delete the - // Session (avoid circular dependencies) -#ifdef BUILD_RTI - clearGuiPluginInterfaces(); - clearDataPluginInterfaces(); -#endif - -#ifdef BUILD_RTI - delete m_exerciseData; -#endif -} - -QString LtsSession::toString() const -{ - QString str; - str.append(QStringLiteral("LtsSession: this=%1").arg(QString::number(reinterpret_cast(this)))); -#ifdef BUILD_RTI - str.append(QStringLiteral(", domainId=%1, partitionId=%2").arg(domainId()).arg(partitionId())); -#endif - str.append(QStringLiteral(", workspaceId='%1'").arg(workspaceId())); - str.append(QStringLiteral(", appToString='%1'").arg(appToString(app()))); -#ifdef BUILD_RTI - str.append(QStringLiteral(", partitionType=%1, exerciseId='%2'").arg(partitionType()).arg(exerciseId())); - str.append(QStringLiteral(", LtsSessionDataDds=%1").arg(LtsSessionDataDds::toString())); -#endif - return str; -} - -#ifdef BUILD_RTI -QWidget* LtsSession::baseLayoutWidget() -{ - // qWarning() << "baseLayoutWidget" << m_baseLayout; - // qWarning() << "baseLayoutWidget (widget)" << static_cast(m_baseLayout); - return static_cast(m_baseLayout); -} -#else -QWidget* LtsSession::baseLayoutWidget() -{ - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::aboutToCloseTab(LayoutArea area) -{ - m_baseLayout->getTabFromLayoutArea(area)->aboutToClose(); -} -#else -void LtsSession::aboutToCloseTab(LayoutArea /*area*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::aboutToClose() -{ - emit sessionAboutToBeDestroyed(this); - - if (m_baseLayout == nullptr) - return; - - aboutToCloseTab(LayoutArea::eDialogTab); - aboutToCloseTab(LayoutArea::eInfoTab); - aboutToCloseTab(LayoutArea::eSpecialTab); - aboutToCloseTab(LayoutArea::eMainTab); -} -#else -void LtsSession::aboutToClose() -{ - emit sessionAboutToBeDestroyed(this); - // no-op in non-RTI build -} -#endif // BUILD_RTI - -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)); -} - -#ifdef BUILD_RTI -bool LtsSession::removeMenuAction(LtsWidgets::LtsDragIcon* icon) -{ - if (!m_baseLayout) - return false; - return m_baseLayout->removeMenuAction(icon); -} -#endif - -#ifdef BUILD_RTI -void LtsSession::setMenuVisible(bool visible) -{ - m_menuVisible = visible; - - if (m_baseLayout == nullptr) - return; - - m_baseLayout->getMenu()->setVisible(visible); -} -#else -void LtsSession::setMenuVisible(bool /*visible*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); - // Wenn die notification getötet wird, auch wieder austragen... - connect(c, &LtsNotification::LtsNotificationController::destroyed, this, - [this, c]() { m_notiCtlList.removeAll(c); }); - - return n; -} -#else -LtsDragIcon* LtsSession::addAction(const LtsWidgets::PluginData& /*plData*/, bool /*isFirstRun*/) -{ - // no-op in non-RTI build - return nullptr; -} - -LtsNotification::LtsNotification* LtsSession::createNotification(const QString& /*identifier*/) -{ - // no-op in non-RTI build - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::initializePlugins(const QVector& dataPluginList, - const QVector& guiPluginList) -{ - initializeDataPlugins(dataPluginList); - initializeGuiPlugins(guiPluginList); -} - -void LtsSession::initializeDataPlugins(const QVector& - dataPluginList) // TODO: DOC: no gui ifcs in data ifcs ! (as - // fetchInterfaces() is done before gui ifcs are loaded) -{ - 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; - } - - // super heavy hardcore hack to speed up login -> we need to check what plugins are needed for an app! - if (app() == LtsAppDescriptor::App::Login && - (!plugin->pluginName().contains(QStringLiteral("AssetManagement")) && - !plugin->pluginName().contains(QStringLiteral("OperatorManagement")) && - !plugin->pluginName().contains(QStringLiteral("ExerciseManagement")))) - continue; - - // check if plugin has to be loaded in this session type - if (!plugin->availableInPartition(partitionType())) - { - DEBUG_LOG(m_logger) << "Data plugin " << api.pluginInstance.pluginName - << " is not available in this partition type " << partitionType(); - continue; - } - - // super heavy hardcore hack to speed up login - 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); -} -#endif - -#ifdef BUILD_RTI -void LtsSession::initializeGuiPlugins(const QVector& guiPluginList) -{ - m_failedFetches.clear(); - - /* important to create gui interfaces even if they are not available in a session, - * but foreign plugins can still use their ForeignGuiInterface to use - * their windows */ - - 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; - } - - // check if plugin has to be loaded in this session type - 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; // copy flags - - if (api.pluginInstance.pluginName == QStringLiteral("dev")) - flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS; - - ifc->setFlags(flags); // durchreichen.. raw plugin => plugin instance - ifc->setOptions(api.m_options); // s.a.a. - ifc->setAppId(api.m_appId); // s.a.a. - m_guiPluginInterfaces[plugin->pluginName()] = ifc; - DEBUG_LOG(m_logger) << "Adding gui plugin " << plugin->pluginName() << " for " << partitionType() << " - " - << this; - } - - /*/ - * there are all plugin instances in this list - * if fetchInterface of a single plugin failes, that plugin instance will be removed (does not find his obligatory - plugin(s) in this list) - * a refetch has to be done, in case some other plugin(s) used that plugin which is now deleted - /*/ - QMutableMapIterator> it( - m_guiPluginInterfaces); // modify a list while iterating through it - 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(); // rewind! - - // remove from payloads list if failed, because payload might already be added before refetching, but we - // don't want payloads of plugins that failed fetching interfaces.. - 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 - { - // ERROR_LOG(m_logger) << "GUI FETCH INTERFACE OKAY!"; - - 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); // addPayload(executionPriority, guiPlugin); - // DEBUG_LOG(m_logger) << " > has payloads, adding payload request to session"; - } - } - - // DEBUG_LOG(m_logger) << " > done"; - } - } - - QTimer::singleShot(100, this, &LtsSession::slotStartPluginInit); - // executePayloads(); // does not execute when there are no payloads, but is needed to be called here ! -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; // not found! -} -#else -LtsTabWidget* LtsSession::locateWidget(QWidget* /*widgetToLocate*/, LayoutArea /*area*/) -{ - return nullptr; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); - } -} -#else -void LtsSession::enableBaseLayoutMenu(bool /*on*/) -{ - // no-op in non-RTI build -} - -void LtsSession::enableBaseLayoutArea(LtsWidgets::LayoutArea /*area*/, bool /*on*/) -{ - // no-op in non-RTI build -} -#endif // BUILD_RTI - -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 -{ -#ifdef BUILD_RTI - if (isReplayPartition()) - return QStringLiteral("replay_%1").arg(QString::number(partitionId())); -#endif - return m_workspaceId; // sessionManager->workspaceId(); -} - -void LtsSession::setWorkspaceId(const QString &newWorkspaceId /* <= MW: that's why I like "get" in getter names (needed to prepend "new" because of already existing method with the name "workspaceId") */) -{ - 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; // m_sessionManager->setWorkspaceId(workspaceId); - -#ifdef BUILD_RTI - if (m_baseLayout) - m_baseLayout->setWorkspaceId(workspaceId() /* use getter instead of member var, as replay partition strings can only be retrieved with the getter */); -#endif // BUILD_RTI - - emit workspaceIdChanged(newWorkspaceId); -} - -#ifdef BUILD_RTI -ExcState LtsSession::exerciseState() const -{ - return m_exerciseData ? m_exerciseData->exState : ExerciseState::eUnknown; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::connectOnlineHelpButton(LtsWidgets::LtsDialog* widget) -{ - // FIXME: HACK: also connect show online help here !! - 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(/* widget <= see comment below */); - /*/ - * Conceptional: widgets is closing, widget deletes lock handler, lock handler unlocks via ftc, - * lock handler was deleted, ftc answers to already deleted lock handler => crash - * This is a conceptional problem in all the ftc handlers - /*/ - - 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); // sync! - - // widget->setTopicLockHandler(topicLockHandler); - - connect(widget, &LtsDialog::destroyed, topicLockHandler, &TopicLockHandler::slotFinalize, Qt::QueuedConnection); - - // qWarning() << "added tlh to" << widget->windowTitle(); - - connectOnlineHelpButton(widget); -} -#else -void LtsSession::connectOnlineHelpButton(LtsWidgets::LtsDialog* widget) -{ - Q_UNUSED(widget) - // no-op in non-RTI build -} - -void LtsSession::slotStapleTopicLockHandler(LtsWidgets::LtsDialog* widget) -{ - Q_UNUSED(widget) - // no-op in non-RTI build -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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); -} -#else -bool LtsSession::focusTab(const QString& menuGroup, const QString& identifier, LtsWidgets::LayoutArea area) -{ - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - return false; -} -#endif - -#ifdef BUILD_RTI -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); -} -#else -LtsWidgets::LtsDialog* LtsSession::getWidget(const QString& menuGroup, const QString& identifier, - LtsWidgets::LayoutArea area) -{ - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - return nullptr; -} -#endif - -#ifdef BUILD_RTI -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; -} -#else -bool LtsSession::widgetExists(const QString& /*menuGroup*/, const QString& /*identifier*/, LayoutArea /*area*/) const -{ - return false; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -bool LtsSession::addWidget(LtsDialog* widget, const QString& menuGroup, const QString& identifier, LayoutArea area, - bool setClosable) -{ - // TODO: operator selection + possibility to permanent select base layout until its dissolving - - if (!m_baseLayout) - { - ERROR_LOG(m_logger) << "no base layout to put a widget on! menuGroup: " << menuGroup << "ident: " << identifier; - return false; - } - - // slotStapleTopicLockHandler(widget); => already done in blw's addWidget (below!) - // FIXMe: connect(widget, &LtsDialog::signalShowOnlineHelp, - // this, &LtsSession::slotShowOnlineHelp, - // Qt::UniqueConnection) done in slotStaple.. because using LtsDialog::addDialog does NOT reach this - // code here ! - - return m_baseLayout->addWidget(widget, menuGroup, identifier, area, setClosable); -} -#else -bool LtsSession::addWidget(LtsDialog* widget, const QString& menuGroup, const QString& identifier, LayoutArea area, - bool setClosable) -{ - Q_UNUSED(widget) - Q_UNUSED(menuGroup) - Q_UNUSED(identifier) - Q_UNUSED(area) - Q_UNUSED(setClosable) - return false; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::setExerciseData(const LtsWidgets::ExerciseData& exerciseData) -{ - // avoid mutiple calls (and signal to emitted multiple times) - if ((*m_exerciseData) == exerciseData) - return; - - *m_exerciseData = exerciseData; - // partitionType(); ?!? - if (!m_baseLayout) - return; - m_baseLayout->setExerciseData(exerciseData); - emit signalExerciseDataChanged(); -} - -void LtsSession::setProfilePicture(const QPixmap& pixmap) -{ - // partitionType(); ?!? - if (!m_baseLayout) - return; - m_baseLayout->setProfilePicture(pixmap); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::makeVisible() -{ - Q_ASSERT(m_baseLayout == nullptr); - if (m_baseLayout) // onyl create a new base layout when there was not one before ! - { - qCritical() << "base layout already visible"; - return; - } - - createBaseLayout(); -} -#else -void LtsSession::makeVisible() -{ - // BUILD_RTI=OFF: No base layout to create -} -#endif - -#ifdef BUILD_RTI -void LtsSession::refetchActions() -{ - // get actions - 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)) // respect the MAIN flag, even though ACTIONS is not set - // (main, but NOT actions) [but only if this is NOT a - // refetch] - { - 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)) - { - // get gui plugin's actions (only when init was successful) - const QVector plData = ifc->getActions(); - - if (plData.isEmpty()) - DEBUG_LOG(m_logger) << i.key() << " (" << ifc->menuGroup() << ") does not provide actions"; - - // WARNING_LOG(m_logger) << " > " << plData.count() << " Actions"; - - for (const LtsWidgets::PluginData& pd : plData) - { - if (!pd.menuGroup.isEmpty() && !pd.menuText.isEmpty() && - pd.identifier.isEmpty()) // check if identifier is missing - 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 - { - // WARNING_LOG(m_logger) << " > " << plData.count() << " Actions - but ignored by flag"; - } - } - -#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; -} -#else -void LtsSession::refetchActions() -{ - // BUILD_RTI=OFF: No actions to fetch - m_firstActionsFetch = false; -} -#endif - -#ifdef BUILD_RTI -void LtsSession::slotSwap() -{ - LtsMultiMonitor::LtsMultiMonitor::getInstance()->selectAndSwap(m_baseLayout); -} -#else -void LtsSession::slotSwap() -{ - // BUILD_RTI=OFF: No base layout to swap -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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(); -} -#else -int LtsSession::screenId() const -{ - return -1; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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; -} -#else -int LtsSession::screenContainerId() const -{ - return -1; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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(); -} -#else -int LtsSession::screenTypeId() const -{ - return -1; -} -#endif // BUILD_RTI - -LtsAppDescriptor::App LtsSession::app() const -{ - return m_sessionManager ? m_sessionManager->appId() : LtsAppDescriptor::App::Login; -} - -#ifdef BUILD_RTI -bool LtsSession::event(QEvent* e) -{ - if (e->type() == QEvent::LanguageChange) - m_baseLayout->setAppName(appToString(app())); - - return LtsSessionDataDds::event(e); -} -#else -bool LtsSession::event(QEvent* e) -{ - Q_UNUSED(e) - return LtsSessionDataDds::event(e); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::addDeveloperActions() -{ - // DEVELOPER / DEBUG MENU - { - 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) - { - // QMessageBox::information(nullptr, QString(), QStringLiteral("no admin session")); - 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); // no payloads !! TODO: is this a real domainId or a dead var ?? - 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); - } - // END DEVELOPER / DEBUG MENU -} -#endif - -#ifdef BUILD_RTI -bool LtsSession::notifyUnsavedChanges() -{ - if (m_baseLayout == nullptr) - return true; - - return m_baseLayout->notifyUnsavedChanges(); -} -#else -bool LtsSession::notifyUnsavedChanges() -{ - return true; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::createBaseLayout() -{ - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - 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; // already exists - } - - m_baseLayout = new LtsBaseLayoutWidget; // main widget - - connect(m_baseLayout, &LtsBaseLayoutWidget::signalSuccessfullyPlaced, this, [this]() { - DEBUG_LOG(m_logger) << "current APP " << appToString(this->app()); - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - if (this->app() == App::Scripts) - { - m_scriptEditorTimer->start(); - } - else - { - if (m_needsToBeSuccessfullyPlaced) - { - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - emit - _signalDoneInitializingSession(); // needs to be emitted here, as if 'm_needsToBeSuccessfullyPlaced' is - // TRUE, it was not emitted before - } - } - }); - - connect(m_baseLayout, &LtsBaseLayoutWidget::destroyed, this, [this]() { - m_baseLayout = nullptr; // sec - // qWarning() << "baseLayout deleted"; - }); - - m_baseLayout->hide(); // hide - m_baseLayout->setSessionInfo(domainId(), partitionType(), partitionId(), - workspaceId()); // this shd ptr has a different counter .. - m_baseLayout->setWindowIcon( - appToIcon(app())); // for the magic corner to have an icon to show for switching to / from secondaries - - // auto *x = new QLabel; - // x->setPixmap(m_baseLayout->windowIcon().pixmap(128, 128)); - // x->show(); - - if (app() == App::ScriptEditorEssentialsView || // NOTE: timeline's replay app and snapshot's edit plan are - // secondaries, too - setSecondaryWidget needs to be called from - // timeline & snapshot app, too - 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())) // add if non-existent only - m_baseLayout->addNotification(ctl->createWidget()); // FIXME: crash when no icons? - } - - m_hasBaseLayout = true; // not needed anymore, but for its correctness..... - - m_baseLayout->getMenu()->setVisible(m_menuVisible); - m_baseLayout->initilizationFinished(); - - // QMessageBox::warning(nullptr, QStringLiteral("attach!"), QString()); - - if (!m_foreverInvisible) - { - // build list of irreplacable widgets by the dependency tree of its dependencies and their dependencies' - // dependencies - QVector irreplacableWidgets{baseLayoutWidget()}; - LtsSession* dependencySession = dependency(); - int runaway = 100; - while (dependencySession && runaway > 0) - { - irreplacableWidgets += dependencySession->baseLayoutWidget(); - dependencySession = dependencySession->dependency(); - runaway--; - } - - // qWarning() << "#2 irreplacableWidgets count" << irreplacableWidgets.count() << "from" << this; - - // for (QWidget *w : irreplacableWidgets) - // qWarning() << "irre" << w; - - 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; // done here if not a tablet - - LtsNotification::LtsNotification* noti = createNotification(QStringLiteral("TabletWindowSwitcher")); - - noti->setIcon(QIcon(QStringLiteral("icon:switch_display.svg"))); - noti->setAction([this]() { emit taskSwitch(this /* tell it that we are the caller, don't hide US! */); }); - - emit hideAllOtherBLWs(this); -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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()); - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::delayScriptEditorStart() -{ - DEBUG_LOG(m_logger) << "LtsSession::delayScriptEditorStart "; - if (m_needsToBeSuccessfullyPlaced) - { - // qWarning() << "delayScriptEditorStart mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - emit _signalDoneInitializingSession(); // needs to be emitted here, as if 'm_needsToBeSuccessfullyPlaced' is - // TRUE, it was not emitted before - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -bool LtsSession::isChildOfSession(QWidget* child) -{ - if (!child) - { - return false; - } - return baseLayout()->isAncestorOf(child); -} -#else -bool LtsSession::isChildOfSession(QWidget* /*child*/) -{ - return false; -} -#endif // BUILD_RTI - -void LtsSession::doneInitializingSession() -{ - LtsGlobalWaitDialog::getInstance()->done(); - - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - - if (m_markedForDeletion) - { - emit signalInitializationAborted(); // to call done() on global wait dialog - emit signalClose(); // session manager removes us from his list and then deletes us - return; - } - - if (!m_sessionManager->isHeadless()) // disallow base layout creation if app is headless - { - int sessionCount = m_sessionManager->sessionCount(); - - if (!m_hidden && - (m_hasBaseLayout || sessionCount < 1)) // show base layout if m_hasBaseLayout is true OR IF there is only - // this session (e.g. Payloads have not started any sessions) - makeVisible(); - - // if (!m_hasBaseLayout) // delete session if it's useless & empty - // { - // emit signalClose(); - // return; - // } - } - -#ifdef BUILD_RTI - // qWarning() << "emit signalDoneInitializingSession();"; - - 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; - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - // qWarning() << !m_needsToBeSuccessfullyPlaced << m_sessionManager->isHeadless() << !m_hasBaseLayout; - if (!m_needsToBeSuccessfullyPlaced || m_sessionManager->isHeadless() || - !m_hasBaseLayout) // can't be placed if without something to show .. :) - { - // qWarning() << "mep" << - // QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__); - // qWarning() << "!! headless !"; - emit _signalDoneInitializingSession(); - } - - // qWarning() << "not headless !"; -#else - // Non-RTI build: always emit signal after initialization - emit _signalDoneInitializingSession(); -#endif // BUILD_RTI -} - -void LtsSession::slotShowOnlineHelp(const QString& fileName) -{ -#ifdef BUILD_RTI - if (m_ohvIfc == nullptr) - { - LtsWidgets::LtsMessageDialog::showMessage(baseLayout() /* can be 0ptr */, tr("Online Help not available.")); - return; - } - - m_ohvIfc->showHelp(fileName); -#else - Q_UNUSED(fileName) - // no-op in non-RTI build -#endif -} - -#ifdef BUILD_RTI -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); - // qWarning() << "showing operator settings"; - } - 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 !!"; -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::slotStartPluginInit() -{ - m_initializeThesePlugins = m_guiPluginInterfaces; // copy - slotPluginInitDone(); -} - -void LtsSession::slotPluginInitDone() // NEVER call this directly, queue a signal to this slot ! -{ - // qWarning() << "LtsSession::slotPluginInitDone: sender" << sender(); - - if (m_pluginInitDone) - return; - - if (m_slotPluginInitDoneMutex) - { - // qWarning() << "<< never call slotPluginInitDone directly or after all initialization was done! bad sender - // was" << sender(); abort(); - return; - } - - m_slotPluginInitDoneMutex = true; // lock - - 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; // unlock - return; - } - - // qWarning() << "currently initializing ifc"< ifc = i.value(); - - if (ifc->availableInPartition(partitionType())) - { - m_currentInitIfc = ifc.get(); - // qWarning() << "set m_currentInitIfc to" << m_currentInitIfc; - - // qWarning() << "initializing" << i.key() << (void *)ifc.get(); - - ifc->init(); - if (!ifc->m_hasInit) // workaround for ifcs without init method (which needs quitting by signals) - { - // qWarning() << "resetting m_currentInitIfc to nullptr"; - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - } - else // do not initialize plugins that are not available in partition x - { - m_currentInitIfc = nullptr; - QTimer::singleShot(0, this, &LtsSession::slotPluginInitDone); - } - - m_initializeThesePlugins.erase(i); - - m_slotPluginInitDoneMutex = false; // unlock -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::slotExecuteNextPayload() -{ - LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); // show wait dialog again - - /* there is currently a problem when executing a secondary event loop, - * which leads to resending already sent signals, as they might not be - * marked as executed by Qt itself. So this is a workaround.. */ - // if (m_ignoreSignalInLoop) - // return; - // m_ignoreSignalInLoop = true; - - // execute in order - auto it1 = m_payloadPriorityMap.begin(); // largest key (see qt doc) - - if (it1 != m_payloadPriorityMap.end()) - { - std::shared_ptr ifc = (*it1); - m_payloadPriorityMap.erase( - it1); // delete first, as executePayload can be stuck here when the connection is not queued ! - - DEBUG_LOG(m_logger) << "Executing priorized Payload in Partition " << partitionId() << ", Type " - << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); // don't interrupt payload uis with the wait dialog - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub"; - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, this, [name]() { - // qWarning() << "prio-payload for" << name << "DESTROYED!!!!"; - }); - connect(m_blub, &QTimer::timeout, this, [this, name]() { - m_blub2++; - // qWarning() << "prio-payload for" << name << "running since" << m_blub2 << "seconds"; - }); - m_blub->start(1000); - - ifc->executePayload(m_mainBackground); - } - else // no priority payloads left, check for unpriorized payloads - { - auto it2 = m_payloadList.begin(); - - if (it2 != m_payloadList.end()) - { - std::shared_ptr ifc = (*it2); - m_payloadList.erase( - it2); // delete first, as executePayload can be stuck here when the connection is not queued ! - - DEBUG_LOG(m_logger) << "Executing unpriorized Payload in Partition " << partitionId() << ", Type " - << partitionType(); - - LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); // don't interrupt payload uis with the wait dialog - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub"; - } - m_blub = new QTimer(this); - m_blub2 = 0; - QString name(QString::fromUtf8(typeid(ifc.get()).name())); - connect(m_blub, &QObject::destroyed, this, [name]() { - // qWarning() << "prio-payload for" << name << "DESTROYED!!!!"; - }); - connect(m_blub, &QTimer::timeout, this, [this, name]() { - m_blub2++; - // qWarning() << "prio-payload for" << name << "running since" << m_blub2 << "seconds"; - }); - m_blub->start(1000); - - ifc->executePayload(m_mainBackground); - } - else // no payloads left, delete background widget(s) and say that all plugins have been loaded - { - Q_ASSERT(m_payloadsDone == false); - m_payloadsDone = true; - - m_mainBackground = nullptr; // as it was parented to m_hiddenWidget - - if (m_blub) - { - m_blub->stop(); - m_blub->deleteLater(); - // qWarning() << "killing blub - no payloads left"; - m_blub = nullptr; - } - - // qWarning() << "no payloads left"; - - doneInitializingSession(); - } - } - - // m_ignoreSignalInLoop = false; // executing payload is done, clear the ignore-flag -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::executePayloads() -{ - // look for gui plugins (needed to get execution priority and run payloads) - if (!m_payloadPriorityMap.isEmpty() || !m_payloadList.isEmpty()) - { - // DEBUG_LOG(m_logger) << m_payloadPriorityMap.count() << " priorized payload(s), " << m_payloadList.count() << - // " unpriorized payload(s) - " << m_payloadPriorityMap.count() + m_payloadList.count() << " total."; - - // QMetaObject::invokeMethod(this, &LtsSession::slotExecuteNextPayload); - LtsWidgets::LtsGlobalWaitDialog::getInstance() - ->done(); // ugly workaround: as the next method says wait even if we call it the first time (LtsSession - // c_tor called wait already) - - slotExecuteNextPayload(); - } - else - { - // qWarning() << "this session has no payloads assigned!!"; - DEBUG_LOG(m_logger) << "no payloads"; - - if (partitionType() == IDL::PartitionType::Global) - { - m_hasBaseLayout = - true; // admin session has no payloads, so show base layout, otherwise there is nothing to show.. - } - - doneInitializingSession(); - } -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -void LtsSession::clearDataPluginInterfaces() -{ - QMap> weakDataPluginInterfaces; - for (auto i = m_dataPluginInterfaces.cbegin(); i != m_dataPluginInterfaces.cend(); ++i) - { - // i.value()->clearInterfaces(); - weakDataPluginInterfaces.insert(i.key(), i.value()); - } - - DEBUG_LOG(m_logger) << "Clearing data plugin interfaces for " << partitionType() << " - " << this; - m_dataPluginInterfaces.clear(); - -#ifdef LTS_DEBUG - // in global plugins somehow are not unloaded - 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 -} -#endif // BUILD_RTI - -#ifdef BUILD_RTI -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 - // in global plugins somehow are not unloaded - 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 -} -#endif // BUILD_RTI - -LtsSessionManager* LtsSession::getSessionManager() -{ - return m_sessionManager; -} - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.h deleted file mode 100644 index 606218d98..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session.h +++ /dev/null @@ -1,497 +0,0 @@ -/** - * @RDE_HEADER@ - * Keeps the DDS Connector and common data about the session - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-05-15 - */ - -#pragma once - -#include "lts_session_data_dds.h" -#ifdef BUILD_RTI -#include "lts_session_manager.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsWidgets -{ -class LtsBackgroundWidget; -class LtsTabWidget; -class LtsBaseLayoutWidget; -struct ExerciseData; -class LtsDialog; -class LtsDragIcon; -} // namespace LtsWidgets - -namespace LtsNotification -{ -class LtsNotification; -class LtsNotificationController; -} // namespace LtsNotification - -namespace LtsPluginManager -{ -struct PluginInstance; -struct AppPluginInstance; -} // namespace LtsPluginManager - -namespace DeveloperTools -{ -class DevDialog; -class AppWidget; -} // namespace DeveloperTools - -namespace Core -{ -namespace Interface -{ -class Ftc; -} -} // namespace Core - -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, // e.g. fetchInterfaces gone wrong - }; - - static QString appToString(LtsAppDescriptor::App app); - static QString appIconFilename(LtsAppDescriptor::App app); - static QIcon appToIcon(LtsAppDescriptor::App app); - -#ifdef BUILD_RTI - explicit LtsSession(LtsSessionManager* sessionManager); -#else - explicit LtsSession(); -#endif - ~LtsSession(); - - QString toString() const; - -#ifdef BUILD_RTI - LtsSessionManager* getSessionManager(); -#endif - bool init(const DdsConnectionParams& connParams, const QVector& dataPluginList, - const QVector& guiPluginList, bool hidden); -#ifdef BUILD_RTI - bool init(const dds::domain::DomainParticipant& dp, const DdsConnectionParams& connParams, - const QVector& dataPluginList, - const QVector& guiPluginList); -#endif -#ifdef BUILD_RTI - void makeVisible(); -#else - void makeVisible(); // no-op when BUILD_RTI=OFF -#endif - - void setPayloadsEnabled() - { - m_payloads = true; - } // cannot be reverted - - void aboutToClose(); - - bool isInitializationDone() const - { - return m_initDone; - } - - void presetBaseLayoutAsSecondaryWidget() - { - m_presetBaseLayoutAsSecondaryWidget = true; - } - - /** - * @brief addAction - IMPORTANT: Actions have to be translated on retranslate by adding the same action (with same - * identifier) but with translated text & menu group! This replaces the text & menu group only. The pointer to the - * action stays the same. - * @param plData - * @param isFirstRun - * @return - */ - LtsWidgets::LtsDragIcon* addAction(const LtsWidgets::PluginData& plData, bool isFirstRun = false); - void setMenuVisible(bool visible = true); - #ifdef BUILD_RTI - bool removeMenuAction(LtsWidgets::LtsDragIcon* icon); -#endif - 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); - - /** - * @brief setExerciseData is called by the ExerciseManagement plugin and sets the human reabale name of the exercise - * and puts the text into all base layouts of this session - * @param exerciseData the human reable exercise name of the current exercise - */ -#ifdef BUILD_RTI - void setExerciseData(const LtsWidgets::ExerciseData& exerciseData); - const LtsWidgets::ExerciseData* exerciseData() const; - void setProfilePicture(const QPixmap& pixmap); -#else - void setExerciseData(const LtsWidgets::ExerciseData& exerciseData) { Q_UNUSED(exerciseData) } - const LtsWidgets::ExerciseData* exerciseData() const { return nullptr; } - void setProfilePicture(const QPixmap& pixmap) { Q_UNUSED(pixmap) } -#endif - - void setBaseLayoutVisible() - { - m_hasBaseLayout = true; - } // cannot be reverted after base layout was created (creation begins after all payloads have been executed OR if - // makeVisible was called)! - - bool isVisible() const - { - return (m_baseLayout != nullptr); - } - - const QString& name() const - { - return m_name; - } - - void setName(const QString& name) - { - m_name = name; - } - - /** - * @brief isChildOfSession can be used to check if a fiven QWidget is part of the current Session's application - * - * @param child widget to check against. - * @return true if widget is child of session baselayout - */ - bool isChildOfSession(QWidget* child); - - #ifdef BUILD_RTI - LtsWidgets::LtsBaseLayoutWidget* baseLayout() - { - return m_baseLayout; - } -#else - void* baseLayout() const { return nullptr; } -#endif - - QWidget* baseLayoutWidget(); // prevents some classes to include LtsBaseLayoutWidget.h just to specify the blw as a - // parent widget - - void setMarkForDeletion() - { - m_markedForDeletion = true; - } // session not needed anymore after payloads? then mark for deletion and after payloads, this session will be - // deleted(Later) - - void doneInitializingSession(); // TODO: private - bool notifyUnsavedChanges(); // true if NO unsaved changes - - const QVector& bgWidgets() const - { - return m_bgWidgets; - } - - // checks if a widget with the given data already exists - bool widgetExists(const QString& menuGroup, const QString& identifier, LtsWidgets::LayoutArea area) const; - /** - * @brief addWidget keep identifier empty to prevent checking for existing tabs - * @return (see addWidget in LtsBaseLayoutWidget) - */ - 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); - - /** WARNING: do not save the returned value, as baseLayouts can die (which kills LtsTabWidget also), so reuse this - * method instead of saving the pointer to the LtsTabWidget - * @brief locateWidget locates the given widget 'widget' and returning the TabWidget on which it is on - widgets can - * be available in one of many LayoutAreas (TabWidgets) of many LtsBaseLayoutWidgets - * @param widgetToLocate the widget to look for in the given layoutArea which is located in one of many - * LtsBaseLayoutWidget - * @param area the area the widget is located - * @return LtsTabWidget on which the widget is located - */ - LtsWidgets::LtsTabWidget* locateWidget(QWidget* widgetToLocate, LtsWidgets::LayoutArea area); - - /** - * @brief enable/disable baseLayout menu - * @param on if true enable otherwise disable - */ - void enableBaseLayoutMenu(bool on = true); - - /** - * @brief enable/disable LayoutArea - * @param area the area to enable/disable - * @param on if true enable otherwise disable - */ - void enableBaseLayoutArea(LtsWidgets::LayoutArea area, bool on = true); - - /** - * @brief Return the workstation ip for this workstation (== full ipv4 adress) - workstation id comes later from Asm - */ - static int workstationIp(); // really in LtsSession? - /** - * @brief Return the workstation name - */ - static QString workstationName(); // really in LtsSession? - - /** - * @brief missingRightActions Do not use. - * @return - */ - /*const*/ QVector& missingRightActions() /*const*/ - { - 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); - -#ifdef BUILD_RTI - LtsWidgets::ExcState exerciseState() const; -#else - LtsWidgets::ExcState exerciseState() const { return LtsWidgets::ExerciseState::eUnknown; } -#endif // BUILD_RTI - - void setLoadedFromSavegame() - { - m_loadedFromSavegame = true; - } - - bool isLoadedFromSavegame() const - { - return m_loadedFromSavegame; - } - - void addDeveloperActions(); -#ifdef BUILD_RTI - void refetchActions(); // Do not use. -#else - void refetchActions(); // Do not use. (no-op when BUILD_RTI=OFF) -#endif - void setDependency(LtsSession* session) - { -#ifdef BUILD_RTI - if (m_dependency) - { - ERROR_LOG(m_logger) << "Tried to set session dependency on self"; - return; - } - m_dependency = session; -#else - Q_UNUSED(session) -#endif - } - - bool hasDependency(LtsSession* session) - { - return (m_dependency.data() == session); - } - - LtsSession* dependency() const - { - return m_dependency; - } - - // bogus. - void setNeedsToBeSuccessfullyPlaced(bool needed = true) - { - m_needsToBeSuccessfullyPlaced = needed; - } // works even after session creation as 'slotStartPluginInit', which finalizes the session (creates blw, etc.) is - // called async! - - int id() const - { - return m_id; - } - - public slots: -#ifdef BUILD_RTI - void slotExecuteNextPayload(); // NEVER call this directly, queue a signal to this slot ! - void slotPluginInitDone(); // NEVER call this directly, queue a signal to this slot ! -#endif - void slotShowOnlineHelp(const QString& fileName); - // void takeOverBdv(); - - private slots: -#ifdef BUILD_RTI - void slotShowOperatorSettings(); -#endif - void slotSwap(); - void slotStapleTopicLockHandler(LtsWidgets::LtsDialog* widget); - void slotStopWaitDialog(); -#ifdef BUILD_RTI - void slotStartPluginInit(); - void slotMonitorSelected(); - void delayScriptEditorStart(); -#endif - - signals: - void signalInitializationAborted(); - void _signalDoneInitializingSession(); // internal - void signalDoneInitializingSession(); // is called AFTER all payloads have been executed (on no payloads, called - // after session was created) FOR LTSLOGIN ! - void signalClose(); // tells the session manager to close this session - void signalLogout(); - void signalQuit(); // for payloads, so that they can quit an app - 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; -#ifdef BUILD_RTI - friend class LtsSessionManager; -#endif - -#ifdef BUILD_RTI - bool event(QEvent* e) override; -#endif - void createBaseLayout(); - #ifdef BUILD_RTI - 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(); -#endif - void connectOnlineHelpButton(LtsWidgets::LtsDialog* widget); - // TODO: AFTER FAT !! void stapleWidget(LtsWidgets::LtsDialog *dialog); // FIXME: HACK: because an LtsDialog can - // directly add widgets to the base layout and misses the staples... - void aboutToCloseTab(LtsWidgets::LayoutArea area); - -#ifdef BUILD_RTI - QPointer m_sessionManager; -#endif -#ifdef BUILD_RTI - QMap> - m_payloadPriorityMap; // QMap automatically sorts by key on iteration and overwrites multiple occurences with - // the latest) - QList> - m_payloadList; // for payloads that don't care when they are executed (prio '9999' plugins) -#endif - LtsWidgets::LtsBackgroundWidget* m_mainBackground; - bool m_hasBaseLayout; -#ifdef BUILD_RTI - LtsWidgets::LtsBaseLayoutWidget* m_baseLayout; -#else - void* m_baseLayout{nullptr}; // stub for non-RTI build -#endif - bool m_markedForDeletion; - bool m_payloads; - QVector m_notiCtlList; - bool m_initDone; // set by doneInitializingSession / signalDoneInitializingSession -#ifdef BUILD_RTI - bool m_payloadsDone{false}; // for debug -#endif - bool m_menuVisible; - // bool m_ignoreSignalInLoop = false; - bool m_firstActionsFetch; -#ifdef BUILD_RTI - LtsWidgets::ExerciseData* m_exerciseData; -#else - void* m_exerciseData{nullptr}; // stub for non-RTI build -#endif - QVector m_missingRightActions; - std::shared_ptr m_coreFtc; -#ifdef BUILD_RTI - GuiPluginBase::Interface* m_currentInitIfc; // not owning - bool m_slotPluginInitDoneMutex; - bool m_pluginInitDone; // set by slotPluginInitDone -#endif - QJsonObject m_savegame; - bool m_hidden; // if true, does not create BLW - 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; // if true, creates BLW but hides it and does not use MMM -#ifdef BUILD_RTI - QStringList m_failedFetches; -#endif - QVector m_bgWidgets; - QString m_workspaceId; - bool m_needsToBeSuccessfullyPlaced{ - true /* ALWAYS! */ /*false*/}; // if set to true, 'signalDoneInitializingSession' will only be called after the - // blw was successfully placed by MMM (even works if MMM inavtive (off)) - QString m_name; - QTimer* m_blub{nullptr}; - QTimer* m_scriptEditorTimer{nullptr}; - int m_blub2; - QPointer m_dependency; // when MMM iprovides the screen selection for this app, it isn't allowed to - // replace apps that are dependencies of this app (e.g. logout bug) - bool m_presetBaseLayoutAsSecondaryWidget{false}; - int m_id; -}; - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.cpp deleted file mode 100644 index 68341fbfd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.cpp +++ /dev/null @@ -1,92 +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(true); // IMPORTANT: if you want to set this to true for testing, you have to set TABLET_MODE cmake - // flag to true, otherwise this will crash - 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_domainIdPreset = -1; keep domain ID ! - m_partitionIdPreset = -1; - m_partitionTypePreset = IDL::PartitionType::Any; // FIXME: make invalid !? - - 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; - // m_debugFlags.insert(QStringLiteral("isTablet"), isTablet); -} - -} // namespace LtsSessionData diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.h deleted file mode 100644 index 90646a3df..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data.h +++ /dev/null @@ -1,277 +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(); - - // Presets: - 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; - } - - /** - * @brief removePresets IMPORTANT: call removePresets after using a Preset, otherwise other apps / sessions will - * pick these old Presets up.. - */ - void removePresets(); - - // Other: - 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; - } // Todo: Emit newOperatorRights() if changed? - - 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; - - // Presets to skip some Payloads - 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); -} - -} // namespace LtsSessionData diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.cpp deleted file mode 100644 index 2c16b999f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/** - * @RDE_HEADER@ - * DDS specific parts for a session - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Christian Ehrlicher, SLSE, 2017-02-27 - */ - -#include "lts_session_data_dds.h" - -#ifdef BUILD_RTI -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsSession -{ -/** - * We have to make sure that only one DdsConnector for a specific domain/partion/tye/connStr - * exists because we don't want to have more than one DomainParticipant and Publisher/Subscriber - * per domain/partion/tye/connStr. Therefore this ugly-looking key - */ - -// -// DdsConnectionContainer -// -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; - // ddsConnector is a nullptr -> useless -> remove - 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; - // ddsConnector is a nullptr -> useless -> remove - 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(); - // ddsConnector is a nullptr -> useless -> remove - 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::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) - { - // maybe we can at least find a useful DomainParticipant? - 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, // same as partition Id in live but not in replay - 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"); - default: - break; - } - - 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); -} - -} // namespace LtsSession - -#else // !BUILD_RTI - Stub implementation for non-RTI builds - -namespace LtsSession -{ - -LtsSessionDataDds::LtsSessionDataDds(QObject* parent) - : QObject(parent), m_exerciseId(-1), m_emittedTime(0), m_curMSecSinceEpoch(0), m_active(true), m_fetcher(nullptr) -{ -} - -LtsSessionDataDds::~LtsSessionDataDds() -{ - // m_fetcher is void* in stub mode, no deletion needed -} - -bool LtsSessionDataDds::init(const DdsConnectionParams& /*connParams*/) -{ - return false; -} - -bool LtsSessionDataDds::init(const void* /*dp*/, const DdsConnectionParams& /*connParams*/) -{ - return false; -} - -void* LtsSessionDataDds::fetcher(const QString& /*yourPluginsName*/) -{ - return nullptr; -} - -QString LtsSessionDataDds::partitionTypeString(PartitionType /*partitionType*/) -{ - return QString(); -} - -QString LtsSessionDataDds::toString() const -{ - return QStringLiteral("(LtsSessionDataDds: STUB - no RTI support)"); -} - -int LtsSessionDataDds::domainId() const -{ - return -1; -} - -int LtsSessionDataDds::exerciseId() const -{ - return -1; -} - -int LtsSessionDataDds::partitionId() const -{ - return -1; -} - -PartitionType LtsSessionDataDds::partitionType() const -{ - return PartitionType::Global; -} - -QStringList LtsSessionDataDds::qosFile() const -{ - return QStringList(); -} - -QUrl LtsSessionDataDds::tcpConnectionUrl() const -{ - return QUrl(); -} - -DdsConnectionParams LtsSessionDataDds::connParams() const -{ - return DdsConnectionParams(); -} - -bool LtsSessionDataDds::initInternal() -{ - return false; -} - -void LtsSessionDataDds::onPartitionTimeChangedInternal() -{ -} - -} // namespace LtsSession - -#endif // BUILD_RTI \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.h deleted file mode 100644 index d341d84f4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_data_dds.h +++ /dev/null @@ -1,356 +0,0 @@ -/** - * @RDE_HEADER@ - * DDS specific parts for a session - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Christian Ehrlicher, SLSE, 2017-02-27 - */ - -#pragma once - -#include -#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 /* FIXME: Name ist irrefuehrend. "LtsSessionData" hat mit "LtsSessionDataDds" nichts zu tun, klingen aber fast identisch. */ - : public QObject -{ - Q_OBJECT -protected: - // only used from LtsSession, no direct instantiation - 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; } - - // retrieve DTC/FTC inside DataPlugin::createDataInterface() - 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; - } - - // TODO: merge with getDtc() --> std::is_same, std::enable_if - 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; - } - - - // TODO: merge with getDtc() --> std::is_same, std::enable_if - template - bool getForeignDataInterface(std::shared_ptr &val) const - { - //qWarning() << "getForeignDataInterface is deprecated! get the plugin's dtc/ftc directly with getDtc or getFtc!"; - - 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; - } - - - /* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW */ - /* MW DEPRECATED - DO NOT USE IN NEW IMPLEMENTATIONS - CAN BE REMOVED AT ANY TIME WITHOUT WARNING ! MW */ - /* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW */ - /* MW */ /* MW */ - /* MW */ template /* MW */ - /* MW */ bool getForeignDataInterface_NEW(std::shared_ptr &val) const /* MW */ - /* MW */ { /* MW */ - /* MW */ //qWarning() << "getForeignDataInterface is deprecated! get the plugin's dtc/ftc directly with getDtc or getFtc!"; /* MW */ - /* MW */ /* MW */ - /* MW */ for (const auto &i : m_dataPluginInterfaces) /* MW */ - /* MW */ { /* MW */ - /* MW */ val = std::dynamic_pointer_cast(i); /* MW */ - /* MW */ if (val) /* MW */ - /* MW */ return true; /* MW */ - /* MW */ } /* MW */ - /* MW */ val.reset(); /* MW */ - /* MW */ DEBUG_LOG(m_logger) << "Trying to load foreign data ifc " << typeid(S).name() << " failed!" /* MW */ - /* MW */ << ", m_guiPluginInterfaces.size() = " << m_guiPluginInterfaces.size() << " - " << this; /* MW */ - /* MW */ return false; /* MW */ - /* MW */ } /* MW */ - /* MW */ /* MW */ - /* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW */ - - - 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 generic gui interfaces - { - 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 /* only fill if optional */, 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 // critical - { - ERROR_LOG(m_logger) << msg.arg(m_caller, m_resultType, m_sessionDataDds->toString(), QStringLiteral(" which is required")); - } - } -}; - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.cpp deleted file mode 100644 index b71b03ad1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "lts_session_lock_handler.h" - -#include - -namespace LtsSession -{ - -// STUB implementations - RTI Connext DDS not available. -// The topic-lock feature depends on the RTI DDS backend (Core::Interface::Ftc), -// which is an optional dependency not present in this build. These definitions -// exist so that LtsSession links; the moc-generated qt_metacall references every -// slot, so each slot needs a body. Semantics reflect an unavailable backend: -// init reports failure (the caller logs a warning and skips wiring the handler) -// and the handler is never locked. - -TopicLockHandler::TopicLockHandler() - : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.TopicLockHandler")) -{ -} - -TopicLockHandler::~TopicLockHandler() {} - -bool TopicLockHandler::init(const std::shared_ptr &coreFtc, int exerciseId) -{ - Q_UNUSED(coreFtc); - Q_UNUSED(exerciseId); - return false; // RTI DDS backend unavailable -} - -void TopicLockHandler::slotTryLock(const LtsWidgets::TopicInfo &topicInfo) -{ - Q_UNUSED(topicInfo); -} - -bool TopicLockHandler::slotUnlock() -{ - return true; // nothing is locked (no DDS backend) -} - -void TopicLockHandler::slotFinalize() {} - -bool TopicLockHandler::lock() -{ - return false; // no DDS backend to lock against -} - -bool TopicLockHandler::unlock() -{ - return true; // nothing to unlock -} - -std::string TopicLockHandler::objStateAsString() const -{ - return "stub (RTI DDS not available)"; -} - - -TopicLockHanderData::TopicLockHanderData() {} - -TopicLockHanderData::~TopicLockHanderData() {} - -void TopicLockHanderData::init(const std::shared_ptr &coreFtc, int exerciseId) -{ - Q_UNUSED(coreFtc); - Q_UNUSED(exerciseId); -} - -void TopicLockHanderData::unlock() {} - -void TopicLockHanderData::slotTryLock(const LtsWidgets::TopicInfo &topicInfo) -{ - Q_UNUSED(topicInfo); -} - -bool TopicLockHanderData::isLocked() const -{ - return false; // no DDS backend: never locked -} - -LtsWidgets::TopicInfo TopicLockHanderData::getTopicInfo() const -{ - return LtsWidgets::TopicInfo(); -} - -void TopicLockHanderData::slotLock(const QString &lockedBy) -{ - m_lockedBy = lockedBy; -} - -void TopicLockHanderData::slotUnlock() -{ - m_lockedBy.clear(); -} - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.h deleted file mode 100644 index 85bfd5650..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_lock_handler.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include - - -/* - Anmerkungen: - slotTryLock - -> slotTryLock - -> tryLockEntityRequest - <- onTryLockEntityReply - emit signalLockDialog | emit signalUnlockDialog - - PpmGuiPlugin: - - ParticipantOrganisationDialog (asSupportService==false) - a) avoid disabling by overloading ::lock() -> yes - b) use tryLockTopicNew -> yes - - - ParticipantEdit (asSupportService==false||true) - a) avoid disabling by overloading ::lock() -> yes - b) use tryLockTopicNew -> yes - - 3 TacticalOrganisation - -> left and right <- - onReinitLeft, onReinitRight -*/ - -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(); // unlock, finally delete - -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}; // reply.id(); / set by reply of ddsCall - - int m_exerciseId{0}; - bool m_locked{false}; - bool m_askedForLock{false}; // reply does not tell.. - 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; -}; - - -} // namespace LtsSession diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.cpp deleted file mode 100644 index 43d1b61ee..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#ifdef BUILD_RTI -/** - * @RDE_HEADER@ - * Manages all the Sessions - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-05-15 - */ - -#include "lts_session_manager.h" - -#include "lts_session.h" - -#include -#include -#include -#include -#include -#include -#include -#ifdef BUILD_RTI -#include -#endif - -#include - -namespace LtsSession -{ - -LtsSessionManager::LtsSessionManager() - : QObject(), m_appId(LtsAppDescriptor::App::Login), m_headless(false), - m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.Manager")) -{ -} - -LtsSessionManager::~LtsSessionManager() -{ - // qCritical() << "~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) // TODO: what to return ? -{ - // TODO: check if maps empty? - m_dataPluginList = dataPluginList; - m_guiPluginList = guiPluginList; - m_appId = appId; - m_headless = headless; - - // shortcut: on first login etc. we go through ExerciseChooser to select an exercise - // but when we're already in the exercise and starting an app from within, - // we don't want to have the exercise chooser a second time -> directly go through app - // and show the base layout - - // ????? Der Kommentar stimmt leider nicht. Loest leider nicht das Problem. - - bool bHasBaseLayout = connParams.exerciseId > 0; - - LtsSession* session = - createSession(connParams, bHasBaseLayout, true, hidden); // start Admin Session (for login, etc.) - session->setWorkspaceId(workspaceId); - return session; -} - -QVector LtsSessionManager::sessions() -{ - return m_sessionMap.values().toVector(); - - // QVector result; - // for (auto i = m_sessionMap.begin(); i != m_sessionMap.end(); ++i) - // result.append(i.value()); - - // return result; -} - -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); // for tablet mode! shows another hidden session from some other app or maybe the - // same app, after the visible one was hidden - - QMetaObject::invokeMethod(this, &LtsSessionManager::signalQuit, - Qt::QueuedConnection); // wait until all Session's deleteLater is done -} - -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); - // QueuedConnection since Qt5.9.1 seems to send the buttonRelease event again when entering an - // eventloop within this slot. Not yet clear why this happens - 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(); // after payloads are done (if any), the base layout will be created - - 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)) // init not successful? return invalid ptr - { - session->deleteLater(); - return nullptr; - } - } - else - { - /* - * init SOLLTE asynchron ablaufen. - * init läuft aber synchron, da die Payloads der Plugins DIrectConnections benutzen. - * AUf QueuedCOnnections umbauen ist schwerer, da init mit einem SessionCount von 0 rechnet, da init blocking - * ist und es daher nicht zum aufruf m_sessionMap[...] = ...; kommt...... - */ - - if (!session->init(connParams, m_dataPluginList, m_guiPluginList, - hidden)) // init not successful? return invalid ptr - { - 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); // when destruction takes a long time, a new session may have been added which - // invalidated the iterator - - found = true; - break; - } - } - - if (!found) - FATAL_LOG(m_logger) << "LtsSessionManager: Cannot find Session in map!"; - - emit hideAllOtherBLWs(nullptr); // for tablet mode! shows another hidden session from some other app or maybe the - // same app, after the visible one was hidden - - if (m_sessionMap.isEmpty()) // quit app if all sessions closed - emit signalQuit(); -} - -LtsSession* LtsSessionManager::getSession(IDL::PartitionType partitionType, int partitionId) -{ - auto it = m_sessionMap.find(SessionKey(partitionType, partitionId)); - - if (it != m_sessionMap.end()) // session found? return it! - return it.value(); - - return nullptr; // not found -} - -} // namespace LtsSession -#endif // BUILD_RTI diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.h deleted file mode 100644 index b1397fe32..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsSession/lts_session_manager.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @RDE_HEADER@ - * Manages all the Sessions - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-05-15 - */ - -#pragma once - -#ifdef BUILD_RTI - -#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(); - - // create a new session, return old session if already exists [allowClosingSession => true => base layout close = - // close session | false => base layout close = base layout hide + session still open] - LtsSession* createSession(const DdsConnectionParams& connParams, bool hasBaseLayout = true, bool payloads = true, - bool hidden = false); - // return an already created session or null - - 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: - /** - * @brief signalStopBlock (see slotCloseRequest) - */ - void signalStopBlock(); - void signalLogout(); - void signalQuit(); - void signalInitializationFinished(); - void signalInitializationAborted(); - void hideAllOtherBLWs(QObject* caller); - void taskSwitch(QObject* caller); -}; - -} // namespace LtsSession - -#endif // BUILD_RTI diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/CMakeLists.txt deleted file mode 100755 index 4e8f96589..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/CMakeLists.txt +++ /dev/null @@ -1,94 +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 -# - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -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}/../ - ${CMAKE_CURRENT_BINARY_DIR} -) -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsTimeline - PRIVATE - NGLLogger - ngltools # DTG - LtsWidgets # LtsMessageDialogNew for remove filter track - Qt6::Core - ${Qt6_Gui} - ${Qt6_Widgets} - ) -endif() -NGL_INSTALL_TARGET(LtsTimeline) - - -# LtsTimelineTest -NGL_ADD_EXECUTABLE(WS LtsTimelineTest main.cpp) -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsTimelineTest - LtsTimeline - nglapplication - NGLLogger - Qt6::Core - ${Qt6_Gui} - ${Qt6_Widgets} - ) -endif() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.cpp deleted file mode 100644 index 6639bcea5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.cpp +++ /dev/null @@ -1,36 +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(); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.h deleted file mode 100644 index eae58560a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item.h +++ /dev/null @@ -1,125 +0,0 @@ - - -#pragma once - -#include "ltstimeline_export.h" - -#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; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.cpp deleted file mode 100644 index 66140e9e5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.cpp +++ /dev/null @@ -1,171 +0,0 @@ - - -#include "item_delegate.h" - -#include "item.h" -#include "timeline_view.h" -#include "track.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"; - } -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.h deleted file mode 100644 index 597d1742e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/item_delegate.h +++ /dev/null @@ -1,40 +0,0 @@ - - -#pragma once -#include "ltstimeline_export.h" - -#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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/log4cxx_timeline.props b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/log4cxx_timeline.props deleted file mode 100755 index 499ebc0d3..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/main.cpp deleted file mode 100644 index 0698d83cb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/main.cpp +++ /dev/null @@ -1,218 +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 - { - } - 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; - (void)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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.cpp deleted file mode 100644 index d9246bc9c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.cpp +++ /dev/null @@ -1,636 +0,0 @@ -#include "timeline_header_view.h" - -#include "timeline_view.h" - -#include -#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::RenderHint::Antialiasing | QPainter::RenderHint::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::RenderHint::Antialiasing | QPainter::RenderHint::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::RenderHint::Antialiasing); - 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::RenderHint::Antialiasing); - 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 : std::as_const(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 : std::as_const(m_mediumTicks)) - painter->drawLine(tick.pos, bottom - static_cast(double(height) * 2 / 3), tick.pos, bottom); - - for (const Tick& tick : std::as_const(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); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.h deleted file mode 100644 index 0960162fa..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view.h +++ /dev/null @@ -1,302 +0,0 @@ -#pragma once -#include "ltstimeline_export.h" -#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; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp deleted file mode 100644 index 8892bfa40..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.cpp +++ /dev/null @@ -1,147 +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(); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h deleted file mode 100644 index 377e525eb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_header_view_mouse_handler.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" - -#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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.cpp deleted file mode 100644 index 4fea3e0c8..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.cpp +++ /dev/null @@ -1,709 +0,0 @@ - - -#include "timeline_model.h" - -#include "timeline_view_drag_handler.h" -#include "track.h" - -#include -#include -#include - -namespace LtsTimeline -{ - -TimelineModel::TimelineModel(TimelineView *view, QObject *parent) - : QAbstractItemModel(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineModel")) - , m_view(view) -{ - 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; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // model facade delegates to the deprecated root Track lookup - return m_rootTrack.track(id); - #pragma GCC diagnostic pop - } - - 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; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // model facade delegates to the deprecated root Track lookup - return m_rootTrack.track(id); - #pragma GCC diagnostic pop - } - - 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) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // model facade delegates to the deprecated root Track lookup - return m_rootTrack.item(id); - #pragma GCC diagnostic pop - - 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) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // model facade delegates to the deprecated root Track lookup - return m_rootTrack.item(id); - #pragma GCC diagnostic pop - - 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 -{ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // model facade delegates to the deprecated root Track lookup - return m_rootTrack.copyItemIdsBetween(start, end, strict, complete, true, sorted); - #pragma GCC diagnostic pop -} - -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 : std::as_const(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 : std::as_const(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 : std::as_const(t.items())) - { - const Item& i = *itemPtr; - if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration)) - itemIds += i.id(); - } - - if (recursive) - for (const TrackPtr& trackPtr : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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; -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.h deleted file mode 100644 index 733dc97e2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_model.h +++ /dev/null @@ -1,300 +0,0 @@ - - -#pragma once -#include "item.h" -#include "ltstimeline_export.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: - NGL::INGLLogger& m_logger; - 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; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.cpp deleted file mode 100644 index 2f7c654c4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.cpp +++ /dev/null @@ -1,334 +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 DateTime - -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; -} - -} // namespace Zoom - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.h deleted file mode 100644 index 0dd019e9d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_utils.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" - -#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); - -} // namespace DateTime - -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); - -} // namespace Zoom - -typedef Zoom::Factor ZoomFactor; -typedef QVector ZoomFactors; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.cpp deleted file mode 100644 index 69e509ba9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.cpp +++ /dev/null @@ -1,634 +0,0 @@ - - -#include "timeline_view.h" - -#include "item_delegate.h" - -#include -#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_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineView")) - , 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)) -{ - 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 : std::as_const(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->position().toPoint(); - - const QModelIndex index(indexAt(event->position().toPoint())); - - if (m_header) - { - m_header->setHoverPosition(isCursorWithinRange(event->position().toPoint()) ? event->position().toPoint().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->position().toPoint().x())); - int startPos = toPixel(item.startTime()); - int endPos = toPixel(item.startTime() + item.duration()); - if (endPos - startPos < 15 || (startPos + 5 < event->position().toPoint().x() && event->position().toPoint().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->position().toPoint()); - 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->position().toPoint()); - } -} - -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->position().toPoint().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(); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.h deleted file mode 100644 index fcfedea26..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view.h +++ /dev/null @@ -1,261 +0,0 @@ - - -#pragma once -#include "ltstimeline_export.h" -#include "timeline_header_view.h" -#include "timeline_model.h" -#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 -#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: - NGL::INGLLogger& m_logger; - 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; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp deleted file mode 100644 index fffaccb33..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp +++ /dev/null @@ -1,515 +0,0 @@ -#include "timeline_view_drag_handler.h" - -#include "item.h" -#include "track.h" - -#include -#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->position().toPoint(); - m_view->m_header->setHoverPosition(m_dragPos.x()); - e->setAccepted(canDragItemsTo(e->position().toPoint())); - 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->position().toPoint())) - { - clear(); - e->acceptProposedAction(); - return; - } - - qint64 dt = deltaTime(); - try - { - for (const int& itemId : std::as_const(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(); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.h deleted file mode 100644 index 224bed8d1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_drag_handler.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" -#include "timeline_view.h" - -#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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp deleted file mode 100644 index a6f41a9cf..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "timeline_view_resize_handler.h" - -#include "item.h" -#include "timeline_header_view.h" -#include "timeline_view.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& currentItem = 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& currentItem = 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; -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.h deleted file mode 100644 index 7fc200414..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_resize_handler.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" - -#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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp deleted file mode 100644 index bdb80c0e2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.cpp +++ /dev/null @@ -1,334 +0,0 @@ -#include "timeline_view_selection_handler.h" - -#include "item_delegate.h" - -#include -#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 : std::as_const(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; -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.h deleted file mode 100644 index e9519da4c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_selection_handler.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" -#include "timeline_view.h" - -#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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp deleted file mode 100644 index 118a39052..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "item.h" -#include "timeline_header_view.h" -#include "timeline_view.h" -#include "timeline_view_resize_handler.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 = {}; -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.h deleted file mode 100644 index 5d84316d4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_view_track_handler.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "ltstimeline_export.h" - -#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; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.cpp deleted file mode 100644 index 0cd9428b0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.cpp +++ /dev/null @@ -1,369 +0,0 @@ - - -#include "timeline_widget.h" - -#include "item_delegate.h" -#include "timeline_header_view.h" -#include "timeline_model.h" -#include "timeline_view.h" -#include "ui_timeline_widget.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LtsTimeline -{ - -TimelineWidget::TimelineWidget(QWidget *parent) - : QWidget(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineWidget")) - , m_ui(new Ui::Timeline) -{ - 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 : std::as_const(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 : std::as_const(m_ui->view->selectedItemIds())) - selection += QString::number(itemId) + QStringLiteral(", "); - if (selection.size() > 1) - selection.chop(2); - m_ui->lblDebugSelection->setText(selection); -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.h deleted file mode 100644 index aa9f9905f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.h +++ /dev/null @@ -1,107 +0,0 @@ - - -#pragma once -#include "ltstimeline_export.h" -#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: - NGL::INGLLogger& m_logger; - 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}; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/timeline_widget.ui deleted file mode 100755 index acc52166e..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.cpp deleted file mode 100644 index 1f212bd76..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.cpp +++ /dev/null @@ -1,657 +0,0 @@ - - -#include "track.h" - -#include - -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 : std::as_const(m_childrenTracks)) - { - try - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // self-recursive lookup: recurses via the deprecated public Track API - return track->track(id); - #pragma GCC diagnostic pop - } - 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 : std::as_const(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 : std::as_const(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 -{ - Q_UNUSED(minimumDuration); - auto itemPtrRefs = _copyItems(); - - if (recursive) - { - for (const TrackPtr& trackPtr : std::as_const(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); - } - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - return transformItemPtrsToIds(itemPtrRefs); - #pragma GCC diagnostic pop -} - -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); - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - return transformItemPtrsToIds(itemPtrRefs); - #pragma GCC diagnostic pop - } - - QVector itemIds; - itemIds.reserve(m_items.size()); - - for (const ItemPtr& itemPtr : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(m_copyItemIds)) - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal lookup via the deprecated Track::item - const Item& i = item(itemId); - #pragma GCC diagnostic pop - if (!ignoreIds.contains(i.id())) - if (isItemBetween(i, start, end, strict, complete, minimumDuration)) - return true; - } - - if (!recursive) - return false; - - for (const TrackPtr& trackPtr : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 -{ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - QVector itemIds(transformItemPtrsToIds(m_items)); - #pragma GCC diagnostic pop - - if (!recursive) - return itemIds; - - for (const TrackPtr& trackPtr : std::as_const(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 : std::as_const(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 : std::as_const(m_items)) - { - fprintf(stderr, "%d ", itemPtr->id()); - } - fprintf(stderr, ")"); - } - - fprintf(stderr, "\n"); - - for (const TrackPtr& trackPtr : std::as_const(m_childrenTracks)) - trackPtr->printTrackIds(level + 1); -} - -QVector Track::itemIds(bool recursive, bool sorted) const -{ - if (sorted) - { - if (!recursive) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - return transformItemPtrsToIds(m_items); - #pragma GCC diagnostic pop - - ItemPtrRefs is(_items(true)); - - sort(is); - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - return transformItemPtrsToIds(is); - #pragma GCC diagnostic pop - } - - QVector itemIds; - itemIds.reserve(m_items.size()); - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // internal call to the deprecated transformItemPtrsToIds helper - itemIds += transformItemPtrsToIds(m_items); - #pragma GCC diagnostic pop - - if (!recursive) - return itemIds; - - for (const TrackPtr& trackPtr : std::as_const(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 - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // self-recursive lookup: recurses via the deprecated public Track API - return track->track(id); - #pragma GCC diagnostic pop - } - 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 - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // self-recursive lookup: recurses via the deprecated public Track API - return track->item(id); - #pragma GCC diagnostic pop - } - 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 : std::as_const(m_childrenTracks)) - { - try - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // self-recursive lookup: recurses via the deprecated public Track API - return track->item(id); - #pragma GCC diagnostic pop - } - 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 : std::as_const(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!"); - ; -} - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.h deleted file mode 100644 index c7c78facb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimeline/track.h +++ /dev/null @@ -1,286 +0,0 @@ - - -#pragma once - -#include "item.h" -#include "ltstimeline_export.h" - -#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& copyItemAt(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& copyItemIds() 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; -}; - -} // namespace LtsTimeline diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp deleted file mode 100644 index dc4ef1f05..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.cpp +++ /dev/null @@ -1,230 +0,0 @@ - - -#include "AbstractTimelineModel.h" - -#include -#include - -namespace LtsTimelineClassic -{ - -AbstractTimelineModel::AbstractTimelineModel(QObject *parent) - : QAbstractItemModel(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) - , 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 : std::as_const(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; -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h deleted file mode 100644 index 07afb036b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/AbstractTimelineModel.h +++ /dev/null @@ -1,82 +0,0 @@ - - -#pragma once -#include "Event.h" -#include "Item.h" -#include "ltstimelineclassic_export.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); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/CMakeLists.txt deleted file mode 100755 index f75f465dc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/CMakeLists.txt +++ /dev/null @@ -1,96 +0,0 @@ -# -# CMakeLists.txt for Timeline Library -# -# $Id$ -# $HeadURL$ -# $Author$ -# -# Created by Mike Wille, SLSE, 2017-02-10 -# - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -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}) - -# Qt6 AutoUic configuration for UI files in Ui/ subdirectory -set_target_properties(LtsTimelineClassic PROPERTIES - AUTOUIC ON - AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/Ui" -) - -# Generate export header for shared library -include(GenerateExportHeader) -generate_export_header(LtsTimelineClassic - EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/ltstimelineclassic_export.h" -) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsTimelineClassic - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR} -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsTimelineClassic - PRIVATE - NGLLogger - ngltools # DTG - Qt6::Core - ${Qt6_Gui} - ${Qt6_Widgets} - LtsSessionDataDds - ) -endif() - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.cpp deleted file mode 100644 index 0f09f059c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.cpp +++ /dev/null @@ -1,162 +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_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()), 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); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.h deleted file mode 100644 index 1bfa9d2a6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Event.h +++ /dev/null @@ -1,199 +0,0 @@ - - -#pragma once - -#include "ltstimelineclassic_export.h" - -#include -#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(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); - - // Default constructor deleted due to reference member m_logger - Event() = delete; - - 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: - NGL::INGLLogger& m_logger; - 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(); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.cpp deleted file mode 100644 index 069096f36..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.cpp +++ /dev/null @@ -1,55 +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); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.h deleted file mode 100644 index 8fe2607ad..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventDelegate.h +++ /dev/null @@ -1,40 +0,0 @@ - - -#pragma once -#include "AbstractTimelineModel.h" -#include "Range.h" - -#include -#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); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.cpp deleted file mode 100644 index 07e6ac7dd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.cpp +++ /dev/null @@ -1,154 +0,0 @@ - - -#include "EventGraphic.h" - -#include -#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 : std::as_const(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(); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.h deleted file mode 100644 index a7e62a1f2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventGraphic.h +++ /dev/null @@ -1,46 +0,0 @@ - - -#pragma once - -#include "Event.h" -#include "EventList.h" -#include "ltstimelineclassic_export.h" - -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; -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventList.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventList.h deleted file mode 100644 index 39a8dcf09..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventList.h +++ /dev/null @@ -1,16 +0,0 @@ - - -#pragma once - -#include - -namespace LtsTimelineClassic -{ - -class Event; -using EventList = QVector; - -class EventGraphic; -using EventGraphicList = QVector; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.cpp deleted file mode 100644 index 3b9c0b670..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.cpp +++ /dev/null @@ -1,399 +0,0 @@ - - -#include "EventTools.h" - -#include "Event.h" -#include "EventGraphic.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(); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.h deleted file mode 100644 index 0a50787f7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/EventTools.h +++ /dev/null @@ -1,108 +0,0 @@ - - -#pragma once - -#include "EventList.h" -#include "ltstimelineclassic_export.h" - -#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; -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.cpp deleted file mode 100644 index 6e1d0b552..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.cpp +++ /dev/null @@ -1,337 +0,0 @@ - - -#include "Item.h" - -#include "EventGraphic.h" -#include "EventTools.h" - -#include -#include - -#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 : std::as_const(*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; -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.h deleted file mode 100644 index f25949ba5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Item.h +++ /dev/null @@ -1,84 +0,0 @@ - - -#pragma once - -#include "EventList.h" -#include "PassiveEventContainer.h" -#include "ltstimelineclassic_export.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; -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/LineMap.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/LineMap.h deleted file mode 100644 index 824200d5e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/LineMap.h +++ /dev/null @@ -1,12 +0,0 @@ - - -#pragma once - -#include - -namespace LtsTimelineClassic -{ - -using LineMap = QMap; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp deleted file mode 100644 index 798ad09d0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.cpp +++ /dev/null @@ -1,99 +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()); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h deleted file mode 100644 index 9d670e96b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/PassiveEventContainer.h +++ /dev/null @@ -1,40 +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; -}; - -} // namespace LtsTimelineClassic - -Q_DECLARE_METATYPE(LtsTimelineClassic::PassiveEventContainer*) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Range.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Range.h deleted file mode 100644 index 9635e6ee4..000000000 --- a/qt6-after/qt6-migrated-legatus/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) {} -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp deleted file mode 100644 index 314475ef7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.cpp +++ /dev/null @@ -1,33 +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()); - } -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.h deleted file mode 100644 index 2f7a600dc..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TestTimelineModel.h +++ /dev/null @@ -1,34 +0,0 @@ - - -#pragma once -#include "AbstractTimelineModel.h" - -#include - -namespace LtsTimelineClassic -{ - -class TestTimelineModelTest; - -class LTSTIMELINECLASSIC_EXPORT TestTimelineModel : public AbstractTimelineModel -{ - Q_OBJECT - - friend class TestTimelineModelTest; - - public: - TestTimelineModel(); - void addTestEvent(Event* event); - - // Public wrappers for testing (protected in base class) - using AbstractTimelineModel::rowCount; - using AbstractTimelineModel::columnCount; - - // Implement pure virtual from AbstractTimelineModel - qint64 currentTime() const override { return 0; } - - private slots: - void slotEventAction(EventGraphic*, int); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp deleted file mode 100644 index ebd48b819..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.cpp +++ /dev/null @@ -1,138 +0,0 @@ - - -#include "TimelineHeaderView.h" - -#include "TimelineWidget.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(); - } -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h deleted file mode 100644 index c5ac1fbc9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineHeaderView.h +++ /dev/null @@ -1,47 +0,0 @@ - - -#pragma once -#include "EventTools.h" - -#include -#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(); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp deleted file mode 100644 index 65f72aa52..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.cpp +++ /dev/null @@ -1,453 +0,0 @@ - - -#include "TimelineIndicator.h" - -#include "EventTools.h" -#include "TimelineWidget.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()) -{ - 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 (" << steps - << ") or less than 1 lines to draw. Loop will exit after 60000 lines."; - } - - if (steps < 1) - steps = 2; - - uint16_t currentStep = 0; - - dbg_print(steps); - - for (qint64 indicatorTime = minTimeline; indicatorTime < maxTimelineView + 1; indicatorTime += higherValue) - - { - if (currentStep > 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(); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.h deleted file mode 100644 index b99c072e1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineIndicator.h +++ /dev/null @@ -1,94 +0,0 @@ - - -#pragma once -#include "EventTools.h" -#include "LineMap.h" - -#include -#include -#include -#include -#include - -namespace NGL -{ -class INGLLogger; - -} -class QPushButton; -class QLabel; - -namespace LtsTimelineClassic -{ - -class TimelineIndicatorTest; - -class TimelineIndicator : public QWidget -{ - Q_OBJECT - - friend class TimelineIndicatorTest; - - 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; - - protected: - NGL::INGLLogger& m_logger; - void paintEvent(QPaintEvent*); - - public: - // Public getter for testing - QComboBox* zoomLevelComboBox() const { return m_cbZoomLevel; } - - 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); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.cpp deleted file mode 100644 index 2186d7050..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.cpp +++ /dev/null @@ -1,321 +0,0 @@ - - -#include "TimelineView.h" - -#include "EventDelegate.h" -#include "EventGraphic.h" -#include "TimelineHeaderView.h" -#include "TimelineWidget.h" - -#include -#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->angleDelta().y() / 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->globalPosition().toPoint(); - 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 : std::as_const(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(); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.h deleted file mode 100644 index 2959b8863..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineView.h +++ /dev/null @@ -1,72 +0,0 @@ - - -#pragma once -#include "AbstractTimelineModel.h" -#include "LineMap.h" -#include "Range.h" -#include "TimelineHeaderView.h" -#include "TimelineIndicator.h" - -#include -#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(); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp deleted file mode 100644 index 280fc5333..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.cpp +++ /dev/null @@ -1,276 +0,0 @@ - - -#include "TimelineWidget.h" - -#include "AbstractTimelineModel.h" -#include "EventDelegate.h" -#include "EventGraphic.h" -#include "ui_TimelineWidgetUi.h" - -#include -#include -#include -#include -#include -#include - -namespace LtsTimelineClassic -{ - -TimelineWidget::TimelineWidget(QWidget *parent) - : QWidget(parent) - , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) - , 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(); -} - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.h deleted file mode 100644 index 68551934b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/TimelineWidget.h +++ /dev/null @@ -1,86 +0,0 @@ - - -#pragma once -#include "Range.h" -#include "ltstimelineclassic_export.h" - -#include -#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: - NGL::INGLLogger& m_logger; - void setTimelineRange(qint64 min, qint64 max); - - void refreshData(); - 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(); -}; - -} // namespace LtsTimelineClassic diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/Ui/TimelineWidgetUi.ui deleted file mode 100755 index f3fb7bacb..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/log4cxx_timeline.props deleted file mode 100755 index 64c4f6833..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/ltstimelineclassic_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/ltstimelineclassic_export.h deleted file mode 100644 index 1cc2b4a61..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/ltstimelineclassic_export.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LTSTIMELINECLASSIC_EXPORT_H -#define LTSTIMELINECLASSIC_EXPORT_H - -// Stub export header for LtsTimelineClassic when not built with MIGRATE_MODULE scope -#define LTSTIMELINECLASSIC_EXPORT -#define LTSTIMELINECLASSIC_NO_EXPORT - -#endif // LTSTIMELINECLASSIC_EXPORT_H \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/main.cpp deleted file mode 100644 index 077211dd0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsTimelineClassic/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ - - -#include "TestTimelineModel.h" -#include "TimelineWidget.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::PdmEventType::EventType_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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt deleted file mode 100755 index 4945fbb82..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -# -# $Id$ -# $HeadURL$ -# $Author$ -# - -# -# Lts Widgets library -# - -# Make ${Qt6_Gui} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui) - set(Qt6_Gui ${Qt6_Gui}) -else() - set(Qt6_Gui "") -endif() - -# Make ${Qt6_Widgets} optional -if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets) - set(Qt6_Widgets ${Qt6_Widgets}) -else() - set(Qt6_Widgets "") -endif() - - -SET(ltsvkeyboard_SRCS - LtsKeyPushButton.h - LtsKeyPushButton.cpp - LtsVirtualKeyboard.h - LtsVirtualKeyboard.cpp -) - -NGL_WRAP_UI(ltsvkeyboard_SRCS -) - -IF(BUILD_RTI) -NGL_ADD_LIBRARY2(WS LtsVirtualKeyboard SHARED ${ltsvkeyboard_SRCS} ) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsVirtualKeyboard - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -if(QT6_GUI_AVAILABLE) - NGL_TARGET_LINK_LIBRARIES(LtsVirtualKeyboard - PRIVATE - PUBLIC - LtsWidgets - LtsSession - LtsAppManager - ${Qt6_Widgets} - ${Qt6_Gui} - ) -endif() -ENDIF(BUILD_RTI) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp deleted file mode 100644 index 8f51557f3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.cpp +++ /dev/null @@ -1,145 +0,0 @@ - - -#include "LtsKeyPushButton.h" - -#include "LtsVirtualKeyboard.h" - -#include -#include -#include -#include - -namespace LtsVirtualKeyboard -{ -LtsKeyPushButton::LtsKeyPushButton(const QString& notTranslatedText, const QString& notTranslatedTextAlt, - 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()); -} -} // namespace LtsVirtualKeyboard diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h deleted file mode 100644 index d051f0fe4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsKeyPushButton.h +++ /dev/null @@ -1,82 +0,0 @@ - - -#pragma once -#include "ltsvirtualkeyboard_export.h" - -#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& notTranslatedText, const QString& notTranslatedTextAlt, 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); -}; -} // namespace LtsVirtualKeyboard diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp deleted file mode 100644 index 5a4bf82bf..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.cpp +++ /dev/null @@ -1,886 +0,0 @@ - - -#include "LtsVirtualKeyboard.h" - -#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& notTranslatedValue, - const QString& notTranslatedValueAlt) -{ - 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 -{ - const QWindow* win = window()->windowHandle(); - const QScreen* scr = (win && win->screen()) ? win->screen() : QGuiApplication::primaryScreen(); - return scr ? scr->availableSize() : QSize(1280, 1024); -} - -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); -} - -} // namespace LtsVirtualKeyboard diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h deleted file mode 100644 index 67daed822..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsVirtualKeyboard/LtsVirtualKeyboard.h +++ /dev/null @@ -1,111 +0,0 @@ - - -#pragma once -#include "LtsKeyPushButton.h" -#include "ltsvirtualkeyboard_export.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& notTranslatedValue, - const QString& notTranslatedValueAlt = 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; -}; - -} // namespace LtsVirtualKeyboard diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists.txt deleted file mode 100755 index f9df0badd..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists.txt +++ /dev/null @@ -1,216 +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}) - -# ui_requirement.h is not a UI file but regular C++ code - disable AUTOUIC -set_target_properties(LtsWidgets PROPERTIES AUTOUIC OFF) - -NGL_TARGET_INCLUDE_DIRECTORIES(LtsWidgets - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../ -) - -NGL_TARGET_LINK_LIBRARIES(LtsWidgets - PRIVATE - Qt6::Network # for QHostInfo - for portability - Qt6::Svg - ngltools - ngltranslator - NGLLogger - TabBarTabStyle - LtsSessionData - LtsUtilsSettings - PUBLIC - LtsUtilsCore - Qt6::Core - Qt6::Widgets - Qt6::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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists_rc.txt b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/CMakeLists_rc.txt deleted file mode 100755 index 518b48522..000000000 --- a/qt6-after/qt6-migrated-legatus/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 - Qt6::Network # for QHostInfo - for portability - Qt6::Svg - ngltools - ngltranslator - TabBarTabStyle - LtsSessionData - LtsUtilsSettings - PUBLIC - LtsUtilsCore - Qt6::Core - Qt6::Widgets - Qt6::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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp deleted file mode 100644 index 6df03c7de..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * @RDE_HEADER@ - * graphicsview widget to resize qpixmaps/qimages with mouse drag and mwheel - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Jens Heilmann, SLTE, 2020-10-14 - */ - -#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; // delete previous pixmapitem - - m_pixmapItem = new QGraphicsPixmapItem(pixmap); - m_scene->addItem(m_pixmapItem); - - setTransform(QTransform()); // set a fresh transformation matrix - m_currentZoom = 1.0; // reset current zoom - - //check if image size is min view, if not scale it - 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); - - // calculate min scale from qMin(width,height); - double ratioWidth = 1.0 * size().width() / pixmap.width(); - double ratioHeight = 1.0 * size().height() / pixmap.height(); - m_minScale = qMax(ratioHeight,ratioWidth); - - calculateScaleStep(); - - // change scene boundaries to image rectangle and center scene on image center - 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; - - // calculate scrollZoom value to be added to the current zoom - 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; - - // scale the view - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h deleted file mode 100644 index 60d1669d9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/LtsImageCropper/lts_image_cropper.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * @RDE_HEADER@ - * graphicsview widget to resize qpixmaps/qimages with mouse drag and mwheel - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Jens Heilmann, SLTE, 2020-10-14 - */ - -#pragma once - -#include "lts_widgets_export.h" -#include - -namespace LtsWidgets -{ - -/** - * @brief The ImageCropper class provides a graphicsview widget to crop images with mouse drag and mwheel scroll. - * avatar() returns a pixmap of the current view (the cropped image). - */ -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: - /** - * @brief ImageCropper - * @param parent - * @param size specifies the size of the graphicsview widget, aswell as the size of the cropped image. - * @param image to crop - */ - LtsImageCropper(QWidget *parent = nullptr, const QSize &size = {} , const QPixmap &pixmap = {}); - ~LtsImageCropper(); - - /** - * @brief setter for image - * @param image - */ - void setPixmap(const QPixmap &pixmap); - - /** - * @brief setter for image - * @param image - */ - void setImage(const QImage &image); - - /** - * @brief set the zoom mutliplier for mwheel scaling e.g. 1.03. - * @param multiplier - */ - void setMultiplier(double multiplier); - - /** - * @brief set the maximum zoom in scale e.g. 2.0 = 200% zoom in. - * @param maxZoom - */ - void setMaxZoom(double maxZoom) { m_maxScale = maxZoom; } - - /** - * @brief returns the current graphicsview view as a pixmap. - * @return qpixmap - */ - QPixmap pixmap() { return grab(); } - - /** - * @brief returns the current graphicsview view as qimage. - * @return qimage - */ - QImage image(); - - /** - * @brief returns the current multiplier - * @return double - */ - double multiplier() const { return m_multiplier; } - - /** - * @brief returns the current max zoom value - * @return double - */ - 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; - -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp deleted file mode 100644 index b21bee229..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @RDE_HEADER@ - * ABC widget for creating custom views of sequences - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Verena Eberlein, SPMO, 2023-08-03 - */ -#include "lts_abstract_sequence_widget.h" - -#include - -#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); - Q_UNUSED(data); -} - -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()) // necessary? - 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(QRegularExpression(QLatin1String("\\.([0-9]*)")), newId)); - } - } -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h deleted file mode 100644 index b4e6b9454..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_sequence_widget.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef LTS_ABSTRACT_SEQUENCE_WIDGET_H -#define LTS_ABSTRACT_SEQUENCE_WIDGET_H - -/** - * @RDE_HEADER@ - * ABC for creating custom views of dds sequences - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Verena Eberlein, SPMO, 2023-08-03 - */ - -#pragma once - - -#include -#include -#include "lts_widgets_export.h" - -class QToolButton; -namespace LtsWidgets -{ - - -class LTSWIDGETS_EXPORT LtsAbstractSequenceWidget : public QGroupBox -{ - Q_OBJECT -public: - /** - * @brief LtsAbstractSequenceWidget inits pbAddMember and adds logic to it. - * @attention pbAddMember must be inserted into a layout - * @param readOnly if read only, disable all buttons on creation - * @param name - * @param contentType - * @param parent - */ - - explicit LtsAbstractSequenceWidget(QWidget *parent = nullptr, const bool &readOnly = false); - - ~LtsAbstractSequenceWidget(); - - /** - * @brief adds a given QWidget - * @param widget - */ - virtual void addWidget(QWidget* widget, bool isReadOnly, bool isButtonVisible = true); - /** - * @brief getValue get widget by objectname - * @param contentType - * @param findObjectName - */ - virtual void getValue(QWidget *contentType, - const QString &findObjectName) const; - - /** - * @brief setMemberCount - * @param count - */ - virtual void setMemberCount(int count); - - /** - * @brief getMemberCount by objectname - * @return count - */ - virtual uint32_t getMemberCount() const; - - /** - * @brief get the current memberlist size - * @return count - */ - virtual int getMemberlistSize(); - - -protected: - - /** - * @brief pbAddMember - * @return - */ - virtual QToolButton *pbAddMember() const; - - - /** - * @brief getObjectName generates a valid object name given by index - * @param index - * @return - */ - virtual QString getObjectName(int index) const; - - /** - * @brief setupMemberWidget place child and button widget into a layout - * @param child - * @param pbChild - * @param objectName - */ - virtual void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName, bool isButtonVisible = true) = 0; - -signals: - /** - * @brief signal is send when ToolButton is pressed so we can add a member widget via addWidget - */ - void addMember(); - -private slots: - /** - * @brief addMemberWidget create a new button and connect to child - * @param member new child - */ - virtual void addMemberWidget(QWidget *member, bool isReadOnly, bool isButtonVisible = true); - - /** - * @brief deleteMemberWidget clean up child and reindex other children objectnames - * @param member deleted child - */ - virtual void deleteMemberWidget(QObject *member); - -private: - bool m_readOnly; - QToolButton *m_pbAddMember = nullptr; - QList m_memberList; - - -}; -} - -#endif // LTS_ABSTRACT_SEQUENCE_WIDGET_H diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_table_model.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_table_model.h deleted file mode 100644 index a25fa09f1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_abstract_table_model.h +++ /dev/null @@ -1,296 +0,0 @@ -/** - * @RDE_HEADER@ - * - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Philipp Last, SLSE, 2017-06-20 - */ - -#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, static_cast(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); - } - - /* - * Emits Qt's signal dataChanged for the whole model data - */ - 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; - }; - - // enable getId() if there is a function F::id - template ::value, int>::type = 0> - int getIdInternal(const F &data) const - { - return data.id(); - } - // fallback, should assert since it does not return a valid value - // static_assert is not possible since virtual void onDataChanged() is instantiated (due to virtual) - 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; - }; - // if data.isDeleted() is available - return it's value - template ::value, int>::type = 0> - bool isDeletedInternal(const F &data) const - { - return data.isDeleted(); - } - // fallback - 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 to allow overriding this function for special cases - virtual bool isFiltered(const T &data, DataModification modType) const - { - Q_UNUSED(data); - Q_UNUSED(modType); - return false; - } - - // virtual to allow overriding this function for special cases - virtual bool onDataChanged(const T &data, DataModification modType) - { - if (isFiltered(data, modType)) - return false; - - const int id = getIdInternal(data); - const int row = findRowForId(id); - - // isDeletedInternal -> T::isDeleted() == true - 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; - } - - // Protected accessor for derived/proxy models - const QVector& getDataVector() const { return m_vecData; } - - QVector m_vecData; - QVector m_vecColumns; -}; -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.cpp deleted file mode 100644 index d01076a80..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/** - * LtsAngleEdit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Steve Kemayou, ESZS, 2024-10-16 - */ - -#include "lts_angle_edit.h" -#include "ui_lts_angle_edit.h" - -#include - - -namespace LtsWidgets -{ -// some helper functions -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); // angleMinVal, angleMaxVal, decimal place, spinboxStep -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)); -} - -/*! - * \brief LtsAngleEdit::LtsAngleEdit - * \param parent - */ -LtsAngleEdit::LtsAngleEdit(QWidget *parent) : - QWidget(parent), - m_ui(new Ui_LtsAngleEdit) -{ - m_ui->setupUi(this); - -} - -LtsAngleEdit::~LtsAngleEdit() -{ - delete m_ui; -} - -/*! - * \brief LtsAngleEdit::initAngleEditWidget - */ -void LtsAngleEdit::initAngleEditWidget(const unsigned int sboxCount) -{ - // set Limits - 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); // default hid rad unit - - // add new dboxes - for(unsigned int i=0; i spinbox = QSharedPointer(new QDoubleSpinBox(this) ); - spinbox->setValue(0); - spinbox->setAlignment(Qt::AlignRight); - spinbox->setMinimumWidth(91); - // set object name - 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); - - - // set combobox units - connect(m_ui->cBoxAngleUnit, qOverload(&QComboBox::currentIndexChanged), - this, &LtsAngleEdit::onCBoxAngleUnitChanged); - - setCboxUnit(); - // set default unit -} - -/*! - * \brief LtsAngleEdit::setValue - * \param value - * \param dboxIndex - */ -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); -} - -/*! - * \brief LtsAngleEdit::setValue - * \param value - * \param valueUnit - * \param dboxIndex - */ -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(); -} - -/*! - * \brief LtsAngleEdit::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); -} - -/*! - * \brief LtsAngleEdit::setValue - * \param value - * \param valueUnit - */ -void LtsAngleEdit::setValue(const double value, AngleUnitType valueUnit) -{ - // get current unit of given angle and convert it to current defined in combobox unit - setValue(getAngleConverted(value, getAngleCurrentUnit(), valueUnit),0); -} - -/*! - * \brief LtsAngleEdit::setAngleUnit - * \param type - */ -void LtsAngleEdit::setAngleUnit(const AngleUnitType &type) -{ - m_ui->cBoxAngleUnit->setCurrentIndex(static_cast(type)); -} - -/*! - * \brief LtsAngleEdit::getCurrentAngleUnit - * \return - */ -LtsAngleEdit::AngleUnitType LtsAngleEdit::getAngleCurrentUnit() const -{ - return static_cast(m_ui->cBoxAngleUnit->currentIndex()); -} - -/*! - * \brief LtsAngleEdit::getAngleValue - * \param dboxIndex default index value is 0; also when more than one dbox is created - * \return - */ -double LtsAngleEdit::getAngle(unsigned int sboxIndex) const -{ - if ((int)sboxIndex > m_sBoxMap.count()) - return 0; - auto spinbox = m_sBoxMap.value(sboxIndex); - return spinbox->value(); -} - -/*! - * \brief LtsAngleEdit::toString - * \param t - * \return - */ -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 : std::as_const(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; -} - -/*! - * \brief LtsAngleEdit::convertAngleValue - * \param angle - * \param newAngleUnit - * \param oldAngleUnit - * \return - */ -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); -} - -}// namespace LtsAngleEdit - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.h deleted file mode 100644 index 6b5e12347..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * LtsAngleEdit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Steve Kemayou, ESZS, 2024-10-16 - */ - -#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); /// set value of spinbox with index. index start from 0. Counting from Left to right. - void setValue(const double value, AngleUnitType valueUnit); /// set value with current unit of given angle and convert it to current defined unit in combobox unit - void setValue(const double value, AngleUnitType valueUnit, unsigned int dboxIndex); - - void hideUnits(const QVector &list); /// list of units that will not be displayed in cbox - void showUnits(const QVector &list); /// list of units to be displayed in cbox - LtsAngleEdit::AngleUnitType getAngleCurrentUnit() const; /// get cbox current unit - void initAngleEditWidget(const unsigned int sboxCount); /// this method initialize the angle edit widget. sboxCount give number of spinbox to be created - double getAngle(unsigned int sboxIndex = 0) const; /// sboxIndex default index value is 0; also when more than one dbox is created - double getAngleConverted(const double angle, LtsAngleEdit::AngleUnitType newAngleUnit, LtsAngleEdit::AngleUnitType oldAngleUnit); - - void setEnable(int sbIndex, bool enabled); /// set spinbox at index enabled or disabled - void showSpinbox(int sbIndex, bool visible); /// show spinBox of specific index. Indexes start from 0. - -protected: - void setCboxUnit(); /// fill combobox with units - -private Q_SLOTS: - void onCBoxAngleUnitChanged(); -private: - Ui_LtsAngleEdit *m_ui; - QMap> m_sBoxMap; /// Map - QMap> m_dboxLimitMap; /// minAngle, maxAngle, decimal place, single step - AngleUnitType m_currAngleUnit; - QSet m_hiddenUnits; -}; - -}//namespace LtsAngleEdit - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_angle_edit.ui deleted file mode 100755 index 8d96c3ea3..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.cpp deleted file mode 100644 index 36369cd14..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @RDE_HEADER@ - * Background Widget that covers a monitor with the project image - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Mike Wille, SLSE, 2016-05-05 - */ - -#include "lts_background_widget.h" - -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include -#endif - - -namespace LtsWidgets -{ - -LtsBackgroundWidget::LtsBackgroundWidget(QWidget *parent) - : QDialog(parent) - , m_isUltraWide(false) -{ - setMouseTracking(true); // register mouse movement - setWindowFlags(windowFlags() | Qt::FramelessWindowHint /*| Qt::WindowStaysOnBottomHint*/); - 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) // do not set / unset ultra wide if it's already set to specified state - return; - - m_isUltraWide = set; - setBackground(); - update(); // force redraw on next event loop -} - - -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); // draw project background on login splash - } - QDialog::paintEvent(e); -} - - -bool LtsBackgroundWidget::event(QEvent *e) -{ - if (e->type() == QEvent::KeyPress || // respect typing into textfields - e->type() == QEvent::MouseButtonPress || - e->type() == QEvent::MouseButtonRelease || - e->type() == QEvent::MouseMove) - { - //qWarning() << "BGW operator action!"; - emit signalOperatorAction(); - e->ignore(); - return true; - } - - return QDialog::event(e); -} - - -void LtsBackgroundWidget::showFullScreen() -{ - //qWarning() << "show fullscreen !"; -#ifdef Q_OS_WIN - winId(); // make sure windowHandle() is not a nullptr - QWindowsWindowFunctions::setHasBorderInFullScreen(windowHandle(), true); -#endif - QDialog::showFullScreen(); - raise(); // as this is a widget for the background, it has to be raised, but only once at startup -} - - -void LtsBackgroundWidget::closeEvent(QCloseEvent *e) -{ -#ifdef DEVEL - deleteLater(); -#else - e->ignore(); -#endif -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.h deleted file mode 100644 index 6e3947348..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_background_widget.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @RDE_HEADER@ - * Background Widget that covers a monitor with the project image - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Mike Wille, SLSE, 2016-05-05 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -class QPixmap; -class QEvent; - -namespace LtsWidgets -{ - -/** - * @brief The LtsBackgroundWidget class creates a fullscreen Widget that covers a monitor with the project image (used for payloads in opApp) - */ -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: - /** - * @brief signalOperatorAction is emitted when the operator moves/clickes the cursor or enters some letters - for example, show window again after it's hidden (screen saver effect) - */ - void signalOperatorAction(); - -public slots: - /** - * @brief showFullScreen shows the widget and raises it. Otherwise the widget would be far in the background (and wihtout a show not shown) - */ - void showFullScreen(); -private: - QPixmap m_pixmap; - bool m_isUltraWide; - -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base.h deleted file mode 100644 index 0296f6e3f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base.h +++ /dev/null @@ -1,288 +0,0 @@ -/** -* @RDE_HEADER@ -* Enumerations for ltsbase -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#pragma once - -#include "lts_widgets_export.h" - -#include - -#include -#include -#include -#include -#include -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -#endif - -#include - - -namespace LtsWidgets -{ -class LtsDialog; - -inline QString readableDuration(qint64 msecs, bool foldDays = true) -{ - if (msecs == 0) - return QObject::tr("---"); - - // If there's a subsecond value it's less than a minute, then show decimals, Otherwise ignore. - 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 only one is not equal 0, return long readable text: - 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; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - 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; - } - } -#else - // Qt6: Touch scrolling is enabled by default for scrollable widgets - Q_UNUSED(widget) -#endif -} - -enum class ButtonSize -{ - eDefault = 40, -// eTiny = 15, -// eExtraSmall = 20, -// eSmall = 30, -// eMedium = 48, -// eBig = 50, -// eGigantic = 70 -}; - - -enum class ButtonStyle -{ - eAll = 1, - eTextOnly, - eIconOnly, - eTextNextToIcon -}; - -enum class LayoutArea -{ - eUnknown, - eInfoTab, - eDialogTab, - eSpecialTab, - eMainTab, -}; - -enum class ExercisePart -{ - ePlanning, - eExecution, - eAfterAction, - eSpecial, -}; - -// Damit Q_DECLARE_OPERATORS_FOR_FLAGS funktioniert muss das Enum in einem namespace gekapselt sein -namespace ExerciseState -{ - enum State - { - // the shift amount has to be the value of the Corresponding enum (EXM::ExerciseState) entry minus one - 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) -} -// convenience -typedef ExerciseState::State ExcState; -typedef QFlags ExerciseStates; - -enum class CreateInstantType -{ - DontCreateInstant, - CreateInstantAndAddAction, - CreateInstantAndDiscardAction, -}; - -struct PluginData -{ - // QWidget *createFunc(QWidget *parent); - 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; // uuid - bool closable = true; - LayoutArea layoutArea = LayoutArea::eDialogTab; - int operatorRight = 0; - CreateInstantType createInstant = CreateInstantType::DontCreateInstant; - bool visibleInMenu = true; // add to menu tab - 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; } -}; - -/** - * Dummy class to be able to display enums defined outside of a qobject - * as properties in designer - */ -class LTSWIDGETS_EXPORT Designer -{ - Q_GADGET -public: - enum ButtonSize - { - eDefault = static_cast(LtsWidgets::ButtonSize::eDefault), -// eTiny = static_cast(LtsWidgets::ButtonSize::eTiny), -// eExtraSmall = static_cast(LtsWidgets::ButtonSize::eExtraSmall), -// eSmall = static_cast(LtsWidgets::ButtonSize::eSmall), -// eMedium = static_cast(LtsWidgets::ButtonSize::eMedium), -// eBig = static_cast(LtsWidgets::ButtonSize::eBig), -// eGigantic = static_cast(LtsWidgets::ButtonSize::eGigantic) - }; - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout.ui deleted file mode 100755 index 30f16a1cd..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp deleted file mode 100644 index 08a6b2f15..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.cpp +++ /dev/null @@ -1,1839 +0,0 @@ -/** -* @RDE_HEADER@ -* Base Layout Widget with all Areas (Info, Main, Detail, Menu) -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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) /* First ever Session must be the Administrative Global Partition */ - , 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) // at least 1 screen, always - , m_menuCollapsed(true) - , m_notiBox(nullptr) -{ - m_ui->setupUi(this); - -#ifndef LTS_DEBUG - m_ui->pbDebug->hide(); -#endif - - addDebugMenu(); - - - { - // Current Time label's context menu - 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)); }); - } - - - { - // Exercise Time label's context menu - 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); // only the detail tab widget has sub tabs !! - - //Register - 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(); - - // master splitter stretch - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->infoWidget), 0); - m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->mainSplitter), 20); - - // main splitter stretch - m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->mainWidget), 20); - m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->detailWidget), 0); - - // right splitter stretch - 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->lbExerciseTime->hide(); - //m_ui->lbExercisePhase->hide(); - //m_ui->lbExerciseStatus->hide(); - //m_ui->lbExerciseName->hide(); - 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); // initially set labels to invalid values - - 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](QAbstractButton* btn) - { - int id = m_bg->id(btn); - m_ui->stackedWidget->setCurrentWidget(getToolBox((ExercisePart) id)); - }); - m_ui->pbSpecial->click(); - } - else - { - m_ui->stackedWidget->setCurrentWidget(getToolBox(ExercisePart::eSpecial)); - } - - checkExercisePartButtonVisibility(); -} - - -LtsBaseLayoutWidget::~LtsBaseLayoutWidget() -{ - // memleak .. x) - - // qCritical() << "~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; - // do not delete m_progressDialogHelper because it is parented with this object (??!!) -} - - -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 /* INTENDED! */, 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); - // RDE SPLITTER LEFT - 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); - } - - { - // PROJECT LOGO - 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; // copy - - 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() -{ - // nothing... - - // QMap fKeys; - // for(auto it = variantMap.cbegin(); it != variantMap.cend(); ++it) - // fKeys[it.key().toInt()] = it.value().toString(); - - // setToolBarActions(settings); - // setFunctionKeys(fKeys); -} - -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; -} - - -// public -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 : std::as_const(m_mapButtons)) - { - if (pd.identifier == QStringLiteral("_OpmShowOperatorSettings")) - { - hasOperatorSettings = true; - break; - } - } - operatorMenu->setOperatorSettingsButton(hasOperatorSettings); // available for Replay - - 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; // no unsaved changes -} - - -//LtsUtils::Promise *notifyUnsavedChangesAsync() -//{ -// if (haveUnsavedChanges()) -// { -// LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.
Please save or discard their changes before closing."), tr("Unsaved changes")); -// 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() // TODO: rm, then signal on signal -{ - emit signalLogout(); -} - - -bool LtsBaseLayoutWidget::slotClose() -{ - if (!notifyUnsavedChanges()) - return false; - - // // no unsaved changes? delete everything - // getTabFromLayoutArea(LayoutArea::eDialogTab)->clearAll(); - // getTabFromLayoutArea(LayoutArea::eInfoTab)->clearAll(); - // getTabFromLayoutArea(LayoutArea::eMainTab)->clearAll(); - // getTabFromLayoutArea(LayoutArea::eSpecialTab)->clearAll(); - - // // save toolbar entries - // auto toolBarItems = m_ui->toolBar->getMenuEntries(); - // Q_FOREACH(const QString &entry, toolBarItems) - // qCritical() << entry; - - // clearMenuActions(); // delete all menu entries - - // //delete m_ui; // now delete the ui, incl. widgets from any plugin, first ! - // //m_ui = nullptr; // TODO: remove this !! - - // if (!m_progressDialogHelper) - // m_progressDialogHelper = new LtsProgressDialogHelper(this); - - // m_progressDialogHelper->slotShowWaitForReplyWindow(); // this will run, until the LtsSession closes the BaseLayout - this will also delete the pdh, as it's parented - - hide(); - - emit signalClose(); //CloseRequest(m_domainId, m_partitionType, m_partitionId); - return true; -} - - -//void LtsBaseLayoutWidget::removeNotification(LtsNotificationIcon *noti) -//{ -// ... -//} - - -void LtsBaseLayoutWidget::closeEvent(QCloseEvent *event) -{ - event->ignore(); // do not close this BaseLayout, the Session will do this on signalCloseRequest ! - - 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(); // bye bye existingWidget, may be deleteLater already has been called before - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling deleteLater multiple times is discuraged.. - } - else - { - qWarning() << __FUNCTION__ << " : Remove existing widget from TabWidget failed."; - } - } - } - return addWidget(dialog, menuGroup, identifier, area, setClosable, focusNewTab); -} // bool LtsBaseLayoutWidget::addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool 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 /*LtsDialogHelper*/ *helper = dialog->getHelper(); - if (!helper) // LtsDialog is in compatibility mode ! bcuz it's old - { - helper = dialog; // pretend that the helper exists - dialog->setAttribute(Qt::WA_DeleteOnClose); // dialog must delete itself, as there is no Dialog Helper - } - - if (!identifier.isEmpty()) // do not check for existing tabs, if ident is empty - { - QWidget *existingWidget = tab->getWidget(menuGroup, identifier); - if (existingWidget != nullptr) // check here also if widget is already there (as widgets can call addWidget when e.g. editing something) - { - helper->deleteLater(); // delete existing widget's helper! - - if (focusNewTab) // "foxus existing tab" - tab->focusTab(menuGroup, identifier); - - // animation tab is hidding? - int indexOfSplitter = getIndexOfTab(tab); - // animate if splitter is collapsed - 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; // already exists, do nothing, as focus is done - } - } - - dialog->setBaseLayoutWidget(this); - - connect(dialog, &LtsDialog::startMainSelect, - this, &LtsBaseLayoutWidget::onHideAnimated); - connect(dialog, &LtsDialog::endMainSelect, - this, &LtsBaseLayoutWidget::onReShowAnimated); - - // Lock : - emit signalStapleTopicLockHandler(dialog); // must be called before addTab, as addTab calls retranslate, which also might try a lock so the connection must be already made - this slot is called sync/directly - sequential - dialog->triggerTopicLock(); - // : Lock - - - - tab->addTab(menuGroup, identifier, helper, setClosable, focusNewTab); // tell the MAIN tab that he should add a new MAIN tab, when there is none, and to add a SUB tab on the MAIN tab, with the given widget - // animate if splitter is collapsed - 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); - } - } - //#define ADJUST_SPLITTER_SIZES_DIALOGTAB -#ifdef ADJUST_SPLITTER_SIZES_DIALOGTAB - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * In layout area DialogTab dialogs are created and destroyed after use. The requested size of the tab widget changes every time. - * But the superordinate splitter doesn't adjust its geometry automatically. Do it here manually to get the PlanEditor working now. - * - * JT 2017/02/10 - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - 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 // ADJUST_SPLITTER_SIZES_DIALOGTAB - - 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() -{ - // menu - while (m_ui->naviActionsPlanning->count()) // remove all items - m_ui->naviActionsPlanning->removeItem(0); - while (m_ui->naviActionsExecution->count()) // remove all items - m_ui->naviActionsExecution->removeItem(0); - while (m_ui->naviActionsAfterAction->count()) // remove all items - m_ui->naviActionsAfterAction->removeItem(0); - while (m_ui->naviActionsSpecial->count()) // remove all items - m_ui->naviActionsSpecial->removeItem(0); - - checkExercisePartButtonVisibility(); - - // map buttons - 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) // init and show every action, that goes must be created instantly - { - case CreateInstantType::CreateInstantAndAddAction: - { - executeCreateInstantFunctions(plData); - break; - } - - case CreateInstantType::CreateInstantAndDiscardAction: - { - executeCreateInstantFunctions(plData); - return nullptr; - } - - default: // case CreateInstantType::DontCreateInstant: - { - break; - } - } - } - else - { - if (plData.createInstant == CreateInstantType::CreateInstantAndDiscardAction) // discarded actions are not shown, ignore them - 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) // refetch - { - 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; - // rename group - 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() /* the group entry */); - if (groupWidgetIndex >= 0) - { - tb->setItemText(groupWidgetIndex, plData.menuGroup); - groupFound = true; - break; - } - } - if (!groupFound) - { - qWarning() << "group widget not found"; - } - found = true; - break; - } - } - - if (!found) - { - //qWarning() << "Could not find and replace the menu item" << plData.identifier << " - creating it!"; - } - else - { - return nullptr; // only replace texts on re-adding actions - } - } - - LtsWidgets::LtsToolBox *tb = getToolBox(m_bg ? plData.exercisePart : ExercisePart::eSpecial); - - // otherwise create an action and add it to the menu - QWidget *tmpW = tb->findChild(plData.menuGroup); - if (!tmpW) - { - // no separator on first place - if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) - return nullptr; - - // no tab for this menu group available -> create new one - 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); - } - - // layout should now already be here ... - QLayout *widgetLayout = tmpW->layout(); - LtsGridLayout *ltsGridLayout = qobject_cast(widgetLayout); - if (!ltsGridLayout) - { - //ERROR_LOG(m_logger) << "temp widget has no layout! cannot add action to menu!"; - 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; - } - - // remove old spacer (a new one is created at the end) - if (widgetLayout->count() > 0) - { - QSpacerItem *spacer = dynamic_cast(widgetLayout->itemAt(widgetLayout->count() - 1)); - if (spacer) - { - ltsGridLayout->removeItem(spacer); - delete spacer; - } - } - - // create entry - LtsDragIcon *dragIcon = nullptr; - if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) - { - QFrame *line = new QFrame(tmpW); - line->setLineWidth(2); - line->setFrameStyle(QFrame::HLine); - // add to layout - ltsGridLayout->add_widget(line); - } - else - { - if (isFirstRun || !found) - { - dragIcon = createButton(plData); - // add to layout - ltsGridLayout->add_widget(dragIcon); - m_mapButtons.insert(dragIcon, plData); // FIXME: rm destroyed buttons !! - - auto it = std::find_if(m_notFoundYet.begin(), m_notFoundYet.end(), [plData](const QString &s) - { - return s == plData.identifier; - }); - - if (it != m_notFoundYet.end()) // found - { - m_ui->toolBar->addMenuEntry(dragIcon); - m_notFoundYet.erase(it); - } - } - } - - checkExercisePartButtonVisibility(); - // add spacer at end - 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)) // focus widget - is there a widget to focus? no? add a new one! yes? focus and do nothing.. - { - //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: cannot focus - creating new widget!"; - - if (plData.createWidgetFunc) - { - LtsDialog *w = plData.createWidgetFunc(this); - if (w) - { - //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: adding widget!"; - addWidget(w, menuGroup, identifier, plData.layoutArea, plData.closable); - } - else - { - //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: no widget to add.."; - } - } - else - { - if (plData.directFunc) - plData.directFunc(plData); - } - } - else - { - //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: focused!"; - } -} - -//SLOTS -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()); - - // hiding an invisible tab makes the splitter reduce the tab's size to 0 - only hide if visible - 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 /* main, info, etc. */ << 0 /* menu */); - } -} - - -void LtsBaseLayoutWidget::onReShowAnimated() -{ - QPropertyAnimation *animation = new QPropertyAnimation(m_ui->mainSplitter, "sizes", this); - animation->setDuration(500); - //Save Old Sizes - - 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); - //Save Old Sizes - 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); - // etc. -} - - -void LtsBaseLayoutWidget::changeEvent(QEvent *e) -{ - QWidget::changeEvent(e); - if (e->type() == QEvent::LanguageChange) - retranslate(); -} - - -int LtsBaseLayoutWidget::getIndexOfTab(LtsTabWidget *tab) -{ - int result = 0; // emergency value - - result = m_ui->masterSplitter->indexOf(tab); - - if (result > -1) // found? - return result; - - - // not found? - result = m_ui->mainSplitter->indexOf(tab); - - if (result > -1) // now found? - return result; - - - // still not found? - result = m_ui->rightSplitter->indexOf(tab); - - if (result > -1) // and now? - 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; // should not happen - -#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 - - // Something is wrong here! The printing shows an additional object besides the - // LtsDragIcons of the menuGroup. - // As workaround chnage condition - // if (tmpW->children().size() == 0) to - if (tmpW->children().size() <= 1) // JT 2016/08/03 - 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) -{ - // TODO: really nullptr? - 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: // better than returning a 0ptr - 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()) // empty / unset identifiers can't be removed - { - return; - } - - m_messageQueue.erase(std::remove_if(m_messageQueue.begin(), m_messageQueue.end(), [messageIdentifier](const Message &m) // remove all queued messages with the same identifier - { - return (m.identifier == messageIdentifier); - }), m_messageQueue.end()); - - if (!m_currentMessage.isEmpty()) - { - if (m_currentMessage.first().identifier == messageIdentifier) - { - // this is the current active message - hide it and call show next queued message (if any) - 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) - { - // qWarning() << "link" << link; - - auto *event = new LtsLinkEvent(QUrl(link), this); - event->setAccepted(false); - QApplication::postEvent(qApp, event); // receive link events by overriding eventFilter of your *parented* QObject class - - if (clickedLinkCallback) - clickedLinkCallback(link); - - if (closeOnLinkClicked) - { - m_notiBox->hide(); - m_currentMessage.clear(); - showNextQueuedMessage(); // show next message (if any) after closing current message - } - }); - - 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(); // show next message (if any) after closing current message - }); - - 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); // send link event to tell NotificationManagement to ack this notification - - m_notiBox->hide(); - m_currentMessage.clear(); - showNextQueuedMessage(); // show next message (if any) after closing current message - }); - - 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) // remove all queued messages with the same identifier (if not empty) - { - 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) // replace current message with new message if identifier is same (and not empty) - { - m_messageQueue += m; - } - } - - m_currentMessage += Message({ title, text, type, clickedLinkCallback, closeOnLinkClicked, identifier }); - - m_notiBox->message(title, text, type); - - - // TODO: ahtc set new x on resize - 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()) // empty / unset identifiers never exist - { - return false; - } - - if (!m_currentMessage.isEmpty() && m_currentMessage.first().identifier == identifier) - { - return true; - } - - for (const Message &m : std::as_const(m_messageQueue)) - { - if (m.identifier == identifier) - { - return true; - } - } - - return false; -} - -} // namespace LtsWidgets - - - - - - - - - - - - - - - - - - - -// diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.h deleted file mode 100644 index 22eac510b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_base_layout_widget.h +++ /dev/null @@ -1,450 +0,0 @@ -/** -* @RDE_HEADER@ -* Base Layout Widget with all Areas (Info, Main, Detail, Menu) -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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; // Already translated. Todo: Probably won't change, if Language is changed on the fly. - 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); - } - -}; - - -/** - * @brief The LtsBaseLayoutWidget class represents the Patric-Layout-System (all Areas: Info, Main, Detail, Menu), which is used as stand-alone widget per Screen. - * It is created per Session by the LtsSession, created by the LtsSessionManager whenever a new Session was created. - */ -class LTSWIDGETS_EXPORT LtsBaseLayoutWidget : public QWidget -{ - Q_OBJECT - -public: - - explicit LtsBaseLayoutWidget(QWidget *parent = nullptr, bool splitMenu = true); - ~LtsBaseLayoutWidget(); - - /** - * @brief hideVisibleLayouts hides all layouts without interfering layout sizes and saves all layout's old states that can be restored by calling resetLayoutVisibility - */ - void hideVisibleLayouts(); - - /** - * @brief resetLayoutVisibility reverts the actions done by hideVisibleLayouts - reshows all hidden layouts - */ - void resetLayoutVisibility(); - - /** - * @brief setMenuVisible sets the visibility of the menu (and the special tab, too!) - * @param visible true to reshow the menu and special tab, false to completely hide them - */ - void setMenuVisible(bool visible); - - /** - * @brief isMenuVisible returns the visibility state of the menu (and special tab, too!) - * @return true if both are visibly, false if not - */ - bool isMenuVisible() const; - - bool isTopBarVisible() const; - void setTopBarVisible(bool visible); - - /** - * @brief isMenuCollapsed returns if the menu was collapsed programmatically by calling clappseMenu. - * NOTE: does not consider the collapsed state if the user collapsed the menu himself by reducing the menu size to zero by moving the splitter. - * @return true is collapsed before, false if menu is not size zero. - */ - bool isMenuCollapsed() const; - - /** - * @brief collapseMenu reduces the size of the menu to zero, but the visibility doesn't change, so that the user can still restore the menu with the splitter. - * NOTE: the special tab widget is collapsed, too. - * @param collapse true to collapse, false to restore to the state before the collapse. - */ - void collapseMenu(bool collapse = true); - - /** - * @brief setSessionInfo sets the informations about the Session of this BaseLayout, so that the window can request a Session close (by logging out) when this window receives a close request. - */ - void setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId); - - /** - * @brief addMenuAction adds an action (aka. menu entry) to the menu layout. The Action will be added into the given group. - * @param plData Action data with information about where to put the menu entry and what to do when the menu entry is clicked - * @return the LtsDragIcon that was now created and put into the menu layout - */ - LtsDragIcon *addMenuAction(const PluginData &plData, bool isFirstRun = false); - - /** - * @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the information in the given plData - * @param plData Action data to identify the menu entry with (group name + captioin text of the menu entry) - * @return true when found & deleted, false when not found and also not deleted - */ - bool removeMenuAction(const PluginData &plData); - - /** - * @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the pointer value - * @param icon to identify the action (aka. menu entry) by the menu entry the LtsDragIcon that was created by the addMenuAction method and put into the menu layout - * @return true when found & deleted, false when not found and also not deleted (as it was deleted before) - */ - bool removeMenuAction(LtsDragIcon *icon); - - bool removeMenuAction(const QString &byIdentifier); - - /** - * @brief clearMenuActions deletes all actions (aka. menu entries) in the menu layout - */ - void clearMenuActions(); - - void setAppName(const QString &appName); - bool isDialogFocused(const LtsDialog *dialog) const; - - /** - * @brief setCustomText shows a custom text to the top layout of the base layout, right after the partition type text. - * Also prepends a bullet before the text to clearly differenciate between the partition type text and the custom text. - * Call with an empty string to hide the custom text again. - * @param text to print on the label - */ - void setCustomText(const QString &text); - const QString customText() const; - - /** - * @brief getTabFromLayoutArea returns the pointer to the LtsTabWidget that is of the type LayoutArea 'area' - * @param area the area of which the LtsTabWidget's type of - * @return the pointer to the LtsTabWidget - */ - LtsTabWidget *getTabFromLayoutArea(LayoutArea area) const; - - /** TODO: rename to getDialog - * @brief getWidget returns the Dialog which is located in the given LayoutArea, identified by the group + identifier strings - * @param menuGroup the name of the main tab of the LayoutArea - * @param identifier the name of the sub tab (when sub-tabbed) of the LayoutArea, otherwise the identifier is added to the menuGroup string - * @param area the LayoutArea (/LtsTabWidget) the Dialog is located - * @return the LtsDialog when found, otherwise nullptr when not found - */ - LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown); - - /** - * @brief getMenu returns the Widget of the Menu Layout Area (as the Menu is not an actual LayoutArea (not an LtsTabWidget which could be returned by 'getTabFromLayoutArea') - * @return the pointer to the Widget of the Menu Layout - */ - QWidget *getMenu() const; - - /** - * @brief getToolbar returns the LtsToolBarArea of the Menu Layout Area - * @return the pointer to the LtsToolBarArea of the Menu Layout - */ - LtsToolBarArea *getToolbar() const; - - /** - * @brief setOperatorName sets the operator name of this session's operator and sets the operator name widget's text - * @param operatorName the human readable operator name of this session's operator - */ - void setOperatorName(const QString &operatorName, bool showTerminalButton); - - /** - * @brief setProfilePicture sets the profile picture of this session's operator and sets the profile picture widget's picture - * @param profilePicture the profgile picture of this session's operator - */ - void setProfilePicture(const QPixmap &profilePicture); - - /** - * @brief setExerciseData sets the name and phase of this Exercise (if it is an Exercise) of this session - only sets the text of a label (label is hidden of not set) - * @param exerciseData necessary data to show - */ - void setExerciseData(const ExerciseData &data); - - /** - * @brief Call this function when the base layout was properly constructed so baselayout can do some internal stuff - */ - void initilizationFinished(); - - /** - * @brief addNotification [Do not use.] used by LtsSession to add a Notification to the Notification Bar, but as a QToolButton - * @param noti the noti to add to the Notification Bar - */ - void addNotification(QToolButton *noti); - - /** - * @brief isWidgetsHidden returns the visibility state of the detail tab - the state changes with calling either of - * these methods: onReShowAnimated, onHideAnimated, showWidgets, hideWidgets - * @return detail tab visible (not to be confused with resizing the detail widget with the splitter, that only - * minized the tab, but the tab is still visible) - */ - 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; } - - /** - * @brief showMessage adds a notification bubble to the bottom right of the blw. Text can be formatted with HTML. - * If a message is already visible, it will be replaced and will be shown after the new message was closed. - * A message with the same identifier will be replaced in the queue or if currently shown. - * @param title The title in bold which is show obove the text. - * @param text The text to show in the bubble - should be as short as possible as bubbles are small. Can be HTML. - * @param type The type which sets the color and icon the bubble should have. See LtsNotificationBox::MessageType. - * @param clickedLinkCallback The function to call if a link inside the bubble has been clicked (HTML 'a' tag) - * @param closeOnLinkClicked Closes the bubble if a link has been clicked. Note: queued messages (if any) will be shown next - * @param identifier An invisible identifier a message will be queued with. Can be left empty. If another message with the - * same identifier (if not empty) is shown, the other message with the same identifier will be removed from the queue or if - * it's the shown message, it will be closed. - */ - 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; } // for the LtsScreenContainer to determine if this is a 'Hilfsansicht' for GUEZ SWP 2023 (https://devhub.defence-elec.de/confluence/x/yLjeDg) (siehe 1.1.1) - void setSecondaryWidget() { m_isSecondaryWidget = true; } - - /** - - * @brief Remove Messages from Queue, as it's handled by someones else - * @param message identifier to remote from que or diplay. - */ - void removeQueuedMessage(const QString &messageIdentifier); - - -public slots: - bool slotClose(); // checks for unsaved changes first and aborts if there are any - void slotLogout(); // checks for unsaved changes first and aborts if there are any - - void slotScreenCountChanged(int count); - - /** - * @brief onMenuEntryClicked is triggered when an action (aka. menu entry) in the menu layout is clicked, executes the action's payload (or focuses the coherant dialog when already open) - */ - void onMenuEntryClicked(); - - void onHideAnimated(); - void onReShowAnimated(); - - /** - * @brief addWidget adds a widget to the operator-specified base layout (either selection by cursor or by permanent selectioin) NOTE: deletes dialog if it already exists - * @param widget the Dialog to be added - * @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into - * @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs - * @param area the LayoutArea to add the Dialog into - * @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator - * @return true when added, false when not (invalid widget or non-existant LayoutArea) - */ - bool addWidget(LtsWidgets::LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true); - - /** - * @brief Like addWidget addOrReplaceWidget adds a dialog widget to base layout with the difference that an existing widget with same identifier is removed from layout before adding he new one. - * The new dialog will not be deleted (compare with adddWidget) - * @param widget the Dialog to be added - * @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into - * @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs - * @param area the LayoutArea to add the Dialog into - * @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator - * @return true when added, false when not (invalid widget or non-existant LayoutArea) - * @see addWidget - */ - 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); - - /** - * @brief executeCreateInstantFunctions executes both the createWidgetFunc and the directFunc (if available). (only used by LtsSession & this addMenuAction) - * @param plData - */ - void executeCreateInstantFunctions(const LtsWidgets::PluginData &plData); - - void setExerciseTime(qint64 mSecsSinceEpoch); - bool haveUnsavedChanges(); - - - - /** - * @brief notifyUnsavedChanges - * @return true if there were NO unsaved changes (method 'failed') and vice versa - * - */ - bool notifyUnsavedChanges(); - -// LtsUtils::Promise *notifyUnsavedChangesAsync(); - - void setIrreplaceableOnce() { m_irreplaceableOnce = true; } - bool irreplaceableReset() { bool r = m_irreplaceableOnce; m_irreplaceableOnce = false; return r; } - bool isIrreplaceable() const { return m_irreplaceableOnce; } - - /** - * @brief showWidgets shows the Dialog Layout again after it was previously hidden - */ - void showWidgets(); - - /** - * @brief hideWidgets hides the visible Dialog Layout (for example e.g. when more space is needed for selection on the map) - */ - void hideWidgets(); - - -private slots: - /** - * @brief slotOperatorMenuClicked shows a menu to change things affecting the operator (logout, etc.) - */ - void slotOperatorMenuClicked(); - - void setCurrentTime(); - - -signals: - /** - * @brief signalCloseRequest is emitted by the closeEvent and requests a logout / close of the current Session (if this is the last BaseLayout) - */ - //void signalCloseRequest(const int domainId, const IDL::PartitionType &partitionType, const int partitionId); - - void signalClose(); - void signalLogout(); - void signalShowOperatorSettings(); - void signalRetranslateActions(); - void signalSwap(); - void signalStapleTopicLockHandler(LtsWidgets::LtsDialog *widget); - void signalSuccessfullyPlaced(); // called by MMM if this widget was successfully placed (even works if MMM is inactive (off)) - - /** - * Notify about unsaved changes before popup message box. - */ - void signalNotifyUnsavedChanges(bool &reject); - -protected: - /** - * @brief closeEvent requests to close a session. As a BaseLayout is linked to a Session, the Session must be closed before allowing - * the BaseLayout to close. So a logout is executed before closing the BaseLayout. - * TODO: One or more BaseLayouts can be linked to a Session - look if it's the last BaseLayout - * @param event - */ - 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(); - //void removeQueuedMessage(const QString &messageIdentifier); - - 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; // optional - QVector m_messageQueue; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp deleted file mode 100644 index c3d7eafe9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** -* @RDE_HEADER@ -* Bitfield Widget for bitwise displaying and editing of quint8 values -* Created by Christian Kaemmerer -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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); - } // for(..) - - 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); -} - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.h deleted file mode 100644 index 62b9db483..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_bitfield_widget.h +++ /dev/null @@ -1,141 +0,0 @@ -/** -* @RDE_HEADER@ -* Bitfield Widget for bitwise displaying and editing of quint8 values -* Created by Christian Kaemmerer -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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: - /** - * @brief setvalue - * sets m_value to the given quint8 - * @param quint8 value - */ - void setValue(quint8 value); - - /** - * @brief setIsEditable - * sets m_isEditable to option. - * true meaning the value of the widget can be changed via mousePressEvent - * @param bool option - */ - void setEditable(bool editable); - - /** - * @brief setHasCaption - * sets m_hasCaption to 0 or 1 which toggels if text should be - * drawn on each bitGraphic - * @param bool hasCaption - */ - void setHasCaption(bool hasCaption); - - /** - * @brief setOnColor - * sets the onColor for m_bitGraphic - * @param QColor onColor - */ - void setOnColor(const QColor &onColor); - - /** - * @brief setOffColor - * sets the offColor for m_bitGraphic - * @param QColor offColor - */ - void setOffColor(const QColor &offColor); - - -public: - /** - * @brief value - * get the current value - * @return quint8 of current value - */ - quint8 value() const { return m_value; } - - /** - * @brief isEditable - * get the current state of editability - * @return bool of editability state - */ - bool isEditable() const { return m_isEditable; } - - /** - * @brief hasCaption - * gets state of m_hasCaption - * @return bool hasCaption - */ - bool hasCaption() const { return m_hasCaption; } - - /** - * @brief onColor - * gets the onColor for m_bitGraphic - * @return QColor m_onColor - */ - QColor onColor() const {return m_onColor;} - - /** - * @brief offColor - * gets the offColor for m_bitGraphic - * @return QColor m_offColor - */ - QColor offColor() const {return m_offColor;} - -private: - /** - * @brief mousePressEvent - * Checks if the click position falls within any of the bit rectangles. If there - * is a match the value of that bit gets flipped - * @param event QMouseEvent - */ - void mousePressEvent(QMouseEvent *event) override; - - /** - * @brief paintEvent - * Draws m_bitGraphic.Size() rectangles and depending on the bit value of m_value draw either - * a onColor rectangle with a offColor 1 or a offColor rectangle with a onColor 0 - * @param pe QPaintEvent (isn't used to do anything) - */ - 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; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_checkable_table_model.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_checkable_table_model.h deleted file mode 100644 index ed95fd18e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_checkable_table_model.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @RDE_HEADER@ - * - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Malte Diers, SLSE, 2017-10-10 - */ - -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp deleted file mode 100644 index 56c06401a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @RDE_HEADER@ - * Advanced Filter, enables filtering per column - * $Id: lts_filter_proxy.cpp 27949 2016-06-22 18:31:13Z ehrlicher $ - * $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/LtsSearchModel/lts_filter_proxy.cpp $ - * $Author: ehrlicher $ - * Created by Mike Wille, SLSE, 2016-03-14 - */ - -#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; - } - } - beginFilterChange(); - endFilterChange(); -} - -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; - } - } - beginFilterChange(); - endFilterChange(); -} - - -bool LtsColumnFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const -{ - // filtering - 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(); // MW: variant => string => variant? is this intended? - 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; - beginFilterChange(); - endFilterChange(); -} - -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 - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h deleted file mode 100644 index 21daf8521..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_filter_proxy_model.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @RDE_HEADER@ - * Advanced Filter, enables filtering per column - * $Id: lts_filter_proxy.h 27949 2016-06-22 18:31:13Z ehrlicher $ - * $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/LtsSearchModel/lts_filter_proxy.h $ - * $Author: ehrlicher $ - * Created by Mike Wille, SLSE, 2016-03-14 - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -struct ComboboxFilterData -{ - QString text; // visible text in ComboBox - QVariant data; // data cor comparison during filtering - QIcon icon; // decoration of text in ComboBox - - 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; // sort items in ComboBox yes/no - 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; // internal, will be set to true/false in fromVariant() -}; - - -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 -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_column_organiser_widget.ui deleted file mode 100755 index e51415e80..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.cpp deleted file mode 100644 index a779cb982..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @RDE_HEADER@ - * ComboBox, optimized for tablets (bigger dropdown/popup and more comfortable completer) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-03-09 - */ - -#include "lts_combo_box.h" -#include "lts_selection_popup.h" -#include "lts_message_dialog.h" - -#include -#include - -//#define NEVER -#ifdef NEVER -# include -#endif // NEVER - -namespace LtsWidgets -{ - -bool LtsComboBox::s_useSelectionDialog = false; - -LtsComboBox::LtsComboBox(QWidget *parent) - : QComboBox(parent) - , m_currentIndex(-1) -{ - connect(this, &LtsComboBox::signalExternallyChanged, - this, &LtsComboBox::signalRefill); // sig on sig (to fetch cbx as sender) -} - -void LtsComboBox::setUseSelectionDialog(bool bUseSelectionDialog) -{ - s_useSelectionDialog = bUseSelectionDialog; -} - -void LtsComboBox::showEvent(QShowEvent *e) -{ - QComboBox::showEvent(e); - - m_currentIndex = currentIndex(); -} - -void LtsComboBox::enterEvent(QEnterEvent *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(); // show original popup when CTRL is pressed - return; - } - - if (!model()->rowCount()) // nothing to show? tell the user. - { - LtsWidgets::LtsMessageDialog::showMessage(this, tr("There are no items available!")); - return; - } - - // create the touch-friendly, screen-centered custom popup and show it - LtsSelectionPopup* popup = new LtsSelectionPopup(model(), currentIndex(), this); - popup->setAttribute(Qt::WA_DeleteOnClose); // dialog must delete itself, as there is no Dialog Helper - -#ifdef NEVER - // debugging : - // // highlighted -> UserInteraction -// connect(popup, static_cast(&LtsSelectionPopup::highlighted), this, [](int idx){ -// qCritical() << " LtsComboBox: called: highlighted(int), idx=" << idx; -// }); - - // currentIndexChanged -> programmatically & UserInteraction - connect(popup, static_cast(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){ - qCritical() << " LtsComboBox: called: currentIndexChanged(int), idx=" << idx; - }); - - // activated -> UserInteraction - connect(popup, static_cast(&LtsSelectionPopup::activated), this, [](int idx){ - qCritical() << " LtsComboBox: called: activated(int), idx=" << idx; - }); - // : debugging -#endif // NEVER - - // Although we derive from QComboBox, there seems to be no signal emitted implicitly. - // Therefore we have to listen to our popup: - connect(popup, &LtsSelectionPopup::currentIndexChanged, - this, &LtsComboBox::userChangedIndex); // listen on changes - connect(popup, &LtsSelectionPopup::activated, - this, static_cast(&LtsComboBox::activated)); // SIGNAL on SIGNAL ! - - 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); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.h deleted file mode 100644 index 285a68c78..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_combo_box.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @RDE_HEADER@ - * ComboBox, optimized for tablets (bigger dropdown/popup and more comfortable completer) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-03-09 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace LtsWidgets -{ - -/** - * @brief The LtsComboBox class provides an almost identically version of the QComboBox, with the main difference that this version is more touch friendly. - * The LtsComboBox's Popup (LtsComboBoxPopup) is a LtsFramelessDialog (screen-centered) with bigger selection-items. - * For debug purpose: press CTRL while clicking on the popup-button to open the original QComboBox-Popup - */ -class LTSWIDGETS_EXPORT LtsComboBox : public QComboBox // TODO: change title of selection window -{ - Q_OBJECT - using BaseClass = QComboBox; - -public: - explicit LtsComboBox(QWidget *parent = nullptr); - ~LtsComboBox() = default; - - /** - * @brief Set the default widget to use - either the default QComboBox or the selection widget - * @param bUseSelectionDialog true if the selection widget should be used, false for the normal combobox - */ - static void setUseSelectionDialog(bool bUseSelectionDialog); - - /** - * @brief hasValueChanged tells if the value set by default (initialization) or by the user to another value, - * different by the default value. If the user sets the value back to the default value, 'hasValueChanged' will return false again. - * Use this in your UiRequirement to see if value differs from the default value to determine a change. - * @return true if value differs from default value and v.v. - */ - bool hasValueChanged() const; - - void setExternalContentType(int ect) { m_externalContentType = ect; } - int externalContentType() const { return m_externalContentType; } - -protected: - /** - * @brief showPopup is emited by the native widget when the popup should be triggered (e.g. by clicking a non-writeable LtsComboBox or by clicking the popup-button) and shows an touch-friendly, screen-centered LtsComboBoxPopup - */ - void showPopup() override; - - void showEvent(QShowEvent *e) override; - void enterEvent(QEnterEvent *e) override; - void leaveEvent(QEvent *) override; - -protected slots: - void userChangedIndex(int index); // user change - for 'hasValueChanged' - -signals: - void signalExternallyChanged(); // e.g. PPM's parties changed, ComboBox should report to PpmComboBox for a refill - void signalRefill(); // outgoing - void signalEnterEvent(); - void signalLeaveEvent(); - -private: - int m_currentIndex; - int m_externalContentType; - static bool s_useSelectionDialog; -}; -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.cpp deleted file mode 100644 index 2c3451398..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.cpp +++ /dev/null @@ -1,1793 +0,0 @@ -/** - * @RDE_HEADER@ - * DTGEdit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#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 -#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; // don't know why -#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->setContentsMargins(0, 0, 0, 0); - - int iStart = 3; // 3 invisible widgets before - - 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); - } // setupUi - - 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; // 3 from above, 3 by default - 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); - } //addButtons - - void addSlider(QWidget *parent) - { - slTime = new QSlider(parent); - slTime->setOrientation(Qt::Horizontal); - gridLayout->addWidget(slTime, 1, 0, 1, 7); - } //addSlider -}; -} - - -namespace LtsWidgets -{ - -// -// helper (via overloading) -// -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(); -} -// helper (via overloading) END - - -// -// class LtsDateTimeEdit -// -LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool checkable) - : QWidget(parent) -// , m_uiLe(new Ui::LtsDateTimeEditLe) - , 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) // see below - , m_withNowButton(withNowButton) // see below -{ - -#ifdef TABLET_MODE - // entweder TOUCH (SpinBox/ComboBox) - setupUiTouch(); - return; -#else - // oder default (LineEdit) - 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, static_cast(&QComboBox::currentIndexChanged), - // this, &LtsDateTimeEdit::valueMonthChanged); - connect(m_uiTouch->cbxMonth, &QComboBox::currentTextChanged, - this, &LtsDateTimeEdit::valueMonthChanged); - - connect(checkBox(), &QCheckBox::toggled, this, &LtsDateTimeEdit::toggled); - - setMinMaxMonth(1,12); - - setTimezone(DTG::timezone()); - //m_tzOffset = DTG::timezone2Secs(QLatin1Char(DTG::timezone()).toLatin1()); - - 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) // If no checkBox -> set other Elements enabled - 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; // will be correctly set in isDataAcceptable() - if (msec == 0) - return false; - - // nothing changed ? - // deactivate since m_offsetTimeZone may have changed: - // if (m_curTimeMSec / 1000 == msec / 1000) - // return isDataAcceptable(); - - 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, QTimeZone(0))); - 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 (!setTimezone(tz) || - 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); // to update GUI - - 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; - - // DTG-Widget: DTG = DateTimeGroup. - // So using the english abbreviations (available via DTG::months()) is correct: - const QStringList &monthSl = DTG::months(); - m_uiTouch->cbxMonth->addItem(monthSl[i-1], i); - // Former (incorrect) way - // QDate date(1777,i,1); - // m_uiTouch->cbxMonth->addItem(date.toString(QStringLiteral("MMM")), i); - - } - // cbxMonth.currentData return the correct inht value for the month :-) - - 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; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyHHMMSS() const -{ - if (m_uiLe) - return m_uiLe->leLongHHMMSS->isEmpty(); - return false; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyMonth() const -{ - if (m_uiLe) - return m_uiLe->leLongMonth->text().isEmpty(); - return false; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyTimezone() const -{ - if (m_uiLe) - return m_uiLe->leLongTZ->text().isEmpty(); - return false; // comboBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyYear() const -{ - if (m_uiLe) - return m_uiLe->leLongYear->isEmpty(); - return false; // spinBox can´t be empty -} - -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()), QTimeZone(0)).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)) - // centennial years (2100, 2200...) are not leap, unless they divide by 400 (2000, 2400...)! - 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; // spinBox/combobox !acceptable -} - -bool LtsDateTimeEdit::isTzAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongTZ); - return true; // spinBox/combobox !acceptable -} - -void LtsDateTimeEdit::showInvalidData(bool setFocus) -{ - if (!m_uiLe) - return; - - bool focus = setFocus; // Fokus bei Fehler setzen ??? - - 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(); - // we only show seconds, so strip away milliseconds - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - 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()); - - // - // Wertebereich überprüfen: 000000 bis 235959 zulässig - // - - - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - } - - int m = mm.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2) && (m > 59)) // WICHTIG: beide Stellen fuer Stunde bereits gesetzt !!! - { - uhrzeit.remove(curpos - 1, 1); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - } - - int s = ss.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2 && mm.length() == 2) && (s > 59)) // WICHTIG: beide Stellen fuer Stunde und Minute bereits gesetzt !!! - { - uhrzeit.remove(curpos - 1, 1); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - - if (changed) - { - m_uiLe->leLongMonth->setText(monStr); - // WICHTIG: keine negativen Cursorpositionen setzen, da sonst Absturzgefahr !!! - // Bem.: wird in DigitsLineEdit bereits abgefangen - if (cp > 0) - m_uiLe->leLongMonth->setCursorPosition(cp); - else - m_uiLe->leLongMonth->setCursorPosition(1); // Ausweichposition linker Textanfang - } - - 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 : std::as_const(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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(), QTimeZone(0))); - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(), QTimeZone(0))); - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(), QTimeZone(0))); - 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(); - // should always be displayed with 2 digits - if (value.length() == 1) - { - value = QLatin1Char('0') + value; - m_uiLe->leLongYear->setText(value); - } - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(), QTimeZone(0))); - 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, QTimeZone(0)).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, QTimeZone(0)).addYears(ofs)); - setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); - m_uiLe->leLongYear->setCursorPosition(cursorPos); - m_uiLe->leLongYear->setSelection(0, 2); - emit dateTimeChanged(); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.h deleted file mode 100644 index 65dca4889..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit.h +++ /dev/null @@ -1,220 +0,0 @@ -/** - * @RDE_HEADER@ - * DTG Edit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#pragma once - -//#include "lts_date_time_edit_touch.h" -#include "lts_widgets_export.h" -#include "qcheckbox.h" -#include "qpushbutton.h" - -#include -#include - - -namespace Ui -{ -class LtsDateTimeEditLe; // default, based on QLineEdit -class LtsDateTimeEdit; // touch , based on QSpinBox, QComboBox -} - -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: - // returns the current time in msec - 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; // should be preferred over isEmpty() - - 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; } - - // Setzt die minimal eingebbare Zeit - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const { return m_minTimeMSec; } - - // Setzt die maximal eingebbare Zeit - 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; // IMMER UTC - - 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; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.cpp deleted file mode 100644 index 1298bbeab..000000000 --- a/qt6-after/qt6-migrated-legatus/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 -{ - -// -// LineEdit -// -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); - // damit das Default-Kontextmenü nicht erscheint -} - -void LineEdit::focusOutEvent(QFocusEvent *event) -{ - QLineEdit::focusOutEvent(event); - emit focusOut(); -} - -// -// DigitsLineEdit -// -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) -{ - // WICHTIG: keine negativen Cursorpositionen setzen, da sonst Absturzgefahr !!! - 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->angleDelta().y() > 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); - } -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_p.h deleted file mode 100644 index 47e8f06c6..000000000 --- a/qt6-after/qt6-migrated-legatus/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; -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp deleted file mode 100644 index 956d41f6d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.cpp +++ /dev/null @@ -1,1799 +0,0 @@ -/** - * @RDE_HEADER@ - * DTGEdit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#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; // don't know why -#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->setContentsMargins(0, 0, 0, 0); - - int iStart = 3; // 3 invisible widgets before - - 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); - } // setupUi - - 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; // 3 from above, 3 by default - 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); - } //addButtons - - void addSlider(QWidget *parent) - { - slTime = new QSlider(parent); - slTime->setOrientation(Qt::Horizontal); - gridLayout->addWidget(slTime, 1, 0, 1, 7); - } //addSlider -}; -} - - -namespace LtsWidgets -{ - -// -// helper (via overloading) -// -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(); -} -// helper (via overloading) END - - -// -// class LtsDateTimeEdit -// -LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool withCheckBox) - : QWidget(parent) -// , m_uiLe(new Ui::LtsDateTimeEditLe) - , 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) // see below - , m_withNowButton(withNowButton) // see below -{ - -#ifdef TABLET_MODE - // entweder TOUCH (SpinBox/ComboBox) - setupUiTouch(); - return; -#else - // oder default (LineEdit) - 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, static_cast(&QComboBox::currentIndexChanged), - // this, &LtsDateTimeEdit::valueMonthChanged); - connect(m_uiTouch->cbxMonth, &QComboBox::currentTextChanged, - this, &LtsDateTimeEdit::valueMonthChanged); - - - setMinMaxMonth(1,12); - - setTimezone(DTG::timezone()); - //m_tzOffset = DTG::timezone2Secs(QLatin1Char(DTG::timezone()).toLatin1()); - - 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) // If no checkBox -> set other Elements enabled - m_uiLe->checkBox->setChecked(true); - } - if (m_uiTouch) - { - m_uiTouch->checkBox->setVisible(m_withCheckBox); - if (!m_withCheckBox) // If no checkBox -> set other Elements enabled - 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; // will be correctly set in isDataAcceptable() - if (msec == 0) - return false; - - // nothing changed ? - // deactivate since m_offsetTimeZone may have changed: - // if (m_curTimeMSec / 1000 == msec / 1000) - // return isDataAcceptable(); - - 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 (!setTimezone(tz) || - 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); // to update GUI - - 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); - // QDate date(1777,i,1); - // m_uiTouch->cbxMonth->addItem(date.toString(QStringLiteral("MMM")), i); - - } - // cbxMonth.currentData return the correct inht value for the month :-) - - 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; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyHHMMSS() const -{ - if (m_uiLe) - return m_uiLe->leLongHHMMSS->isEmpty(); - return false; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyMonth() const -{ - if (m_uiLe) - return m_uiLe->leLongMonth->text().isEmpty(); - return false; // spinBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyTimezone() const -{ - if (m_uiLe) - return m_uiLe->leLongTZ->text().isEmpty(); - return false; // comboBox can´t be empty -} - -bool LtsDateTimeEdit::isEmptyYear() const -{ - if (m_uiLe) - return m_uiLe->leLongYear->isEmpty(); - return false; // spinBox can´t be empty -} - -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)) - // centennial years (2100, 2200...) are not leap, unless they divide by 400 (2000, 2400...)! - 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; // spinBox/combobox !acceptable -} - -bool LtsDateTimeEdit::isTzAcceptable() const -{ - if (m_uiLe) - return isLineEditDataAcceptable(m_uiLe->leLongTZ); - return true; // spinBox/combobox !acceptable -} - -void LtsDateTimeEdit::showInvalidData(bool setFocus) -{ - if (!m_uiLe) - return; - - bool focus = setFocus; // Fokus bei Fehler setzen ??? - - 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(); - // we only show seconds, so strip away milliseconds - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - 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()); - - // - // Wertebereich überprüfen: 000000 bis 235959 zulässig - // - - - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - } - - int m = mm.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2) && (m > 59)) // WICHTIG: beide Stellen fuer Stunde bereits gesetzt !!! - { - uhrzeit.remove(curpos - 1, 1); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - } - - int s = ss.toInt(&ok, 10); - - if (ok) - { - if ((hh.length() == 2 && mm.length() == 2) && (s > 59)) // WICHTIG: beide Stellen fuer Stunde und Minute bereits gesetzt !!! - { - uhrzeit.remove(curpos - 1, 1); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - 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); // letztes Zeichen wieder entfernen - if (cp >= curpos) - cp--; // Korrektur der neuen Cursorposition - changed = true; - } - - if (changed) - { - m_uiLe->leLongMonth->setText(monStr); - // WICHTIG: keine negativen Cursorpositionen setzen, da sonst Absturzgefahr !!! - // Bem.: wird in DigitsLineEdit bereits abgefangen - if (cp > 0) - m_uiLe->leLongMonth->setCursorPosition(cp); - else - m_uiLe->leLongMonth->setCursorPosition(1); // Ausweichposition linker Textanfang - } - - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); - // should always be displayed with 2 digits - if (value.length() == 1) - { - value = QLatin1Char('0') + value; - m_uiLe->leLongYear->setText(value); - } - // Leeres Feld nur mit aktueller Uhrzeit auffüllen, wenn irgendein Feld schon befüllt ist: - 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(); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h deleted file mode 100644 index 569efb33c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_rc.h +++ /dev/null @@ -1,213 +0,0 @@ -/** - * @RDE_HEADER@ - * DTG Edit - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#pragma once - -//#include "lts_date_time_edit_touch.h" -#include "lts_widgets_export.h" - -#include -#include - - -namespace Ui -{ -class LtsDateTimeEditLe; // default, based on QLineEdit -class LtsDateTimeEdit; // touch , based on QSpinBox, QComboBox -} - -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: - // returns the current time in msec - 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; // should be preferred over isEmpty() - - 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; } - - // Setzt die minimal eingebbare Zeit - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const { return m_minTimeMSec; } - - // Setzt die maximal eingebbare Zeit - 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; // IMMER UTC - - 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; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp deleted file mode 100644 index f1afdd3bf..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/** - * @RDE_HEADER@ - * Edit which contains some widgets to edit each: Date + Timezone + Time - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Patric Karwat, NLE, 2019-06-27 - */ - -#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); - - //m_ui->pbNow->setMaximumHeight(m_ui->dteLeft->height()); - 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.isLower()) -// tz = tz.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 = m_dateTime; // Keep to emit signal only when actually changed! (i.e. non-active or out of bounds) - QDateTime oldDateTime = dateTime(); - if (dt == oldDateTime) - return; - - blockSignals(true); // don't emit dateTimeChanged() twice! - - m_ui->spDay->setValue(dt.date().day()); //day - m_ui->cbxMonth->setCurrentIndex(m_ui->cbxMonth->findData(dt.date().month())); //month - // year - //if (m_ui->spYear->maximum() > 1000) - // m_ui->spYear->setValue(dt.date().year()); - //else - 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; - // emit dateTimeChanged(m_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) -{ - // TODO - 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) // If no checkBox -> set other Elements enabled - 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(); - - // Todo: Enhance? - 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); - - //m_ui->spDay->setMaximum(maximumDate.day()); - // setMinMaxMonth(maximumDate.month(), m_maxMonth); - -} - -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()); - //setMinMaxMonth(minimumDate.month(), m_maxMonth); - m_ui->spYear->setMinimum(minimumDate.year()-2000); - //m_ui->spDay->setMinimum(minimumDate.day()); -} - -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(); - // we only show seconds, so strip away milliseconds - 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); - -} -} //namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h deleted file mode 100644 index 9d4f23422..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @RDE_HEADER@ - * Edit which contains some widgets to edit each: Date + Timezone + Time - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Patric Karwat, NLE, 2019-06-27 - */ - -#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); - - // Setzt die minimal eingebbare Zeit - void setMinimumTimeMSec(qint64 msec); - qint64 minimumTimeMSec() const; - - // Setzt die maximal eingebbare Zeit - 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 - { - //Face validator for touch. - 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; // in secs - int m_minMonth; - int m_maxMonth; - QShortcut *m_scFixed; - QShortcut *m_scOffset; - QDateTime m_dateTime; - CurrentTimeCallback m_getCurrentTimeCallback; - Ui::LtsDateTimeEdit *m_ui; -}; - -} //namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_date_time_edit_touch.ui deleted file mode 100755 index fe28a140c..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.cpp deleted file mode 100644 index 1bf897677..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.cpp +++ /dev/null @@ -1,819 +0,0 @@ -/** -* @RDE_HEADER@ -* Dialog for the new Layout system -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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 "ui_requirement.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 - -#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::LtsDialog(QWidget *parent, ButtonCombination buttonCombination, bool closeable) - : QWidget(/*parent*/) - , m_changesMade(false) - , m_firstShow(true) - , m_baseLayoutWidget(nullptr) - , m_isClosable(closeable) - , m_noLockTopic(false) - , m_unchangeableDialog(false) - , m_dialogHelper(new LtsDialogHelper(nullptr)) // has no parent, but LtsTabWidget sets it to itself - //, m_progressDialogHelper(new LtsProgressDialogHelper(this)) - - // protected - , 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); // shadow this dialog on the helper class - m_buttonBox = m_dialogHelper->getButtonBox(); // for new dialogs, so they can connect to it - - -// connect(this, &QObject::destroyed, -// new QObject, [this]() -// { -// qWarning() << (qintptr)this << "LtsDialog destroyed"; -// }); - -// connect(m_dialogHelper, &QObject::destroyed, -// new QObject, [this]() -// { -// qWarning() << (qintptr)m_dialogHelper.data() << "LtsDialogHelper destroyed"; -// }); - - - addDebugMenuButton(); - - - /* - OkApplyCancel, // Ok saves changes, closes the Dialog - Apply only saves changes, does not close the Dialog - Cancel closes the Dialog if there are no changes made - OkCancel, // Special case: only use if you are 100% sure what this is => example: New Message: there is only a SEND button, if sent, the dialog MUST be closed - CloseOnly, // For Dialogs that do not save anything - SeamlessDialog, // No buttons, no margins! - */ - - connect(m_buttonBox->getButton(DialogButton::Close), &QPushButton::clicked, this, &LtsDialog::slotClose); // is (almost) always shown - - switch(buttonCombination) - { - case ButtonCombination::OkCancel: - { - m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); // kb den button enum zu "cancel" umzubenennen - connect(m_buttonBox->getButton(DialogButton::Ok), &QPushButton::clicked, this, &LtsDialog::slotOkay); - break; - } - - case ButtonCombination::OkApplyCancel: - { - m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); // kb den button enum zu "cancel" umzubenennen - 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); - } - 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); - - // do not delete the dialog helper, as the dialog helper deletes this class - // also do not delete, as it is parented! => delete m_progressDialogHelper; - - 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; - // NOTE: we didn't create the widget so we don't delete it - } - - m_overlayContainer->hide(); - m_overlayContainer->setParent(nullptr); - m_overlayContainer->hide(); - m_overlayContainer->deleteLater(); - m_overlayContainer = nullptr; - } - - // add a cool transparent background as an overlay to block the underlying dialog from user input, etc. - OverlayContainer *overlayContainer = new OverlayContainer(m_dialogHelper); // TODO: check race condition with dialogHelper's deletion / unparenting - - 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); // every widget has its own container, so deleteLater is okay here - - 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) // string without file extension !! -{ - m_onlineHelpFileName = fileName; - QPushButton *btn = m_buttonBox->getButton(DialogButton::Help); - - if (btn == nullptr) // paranoid - return; - - if (LtsSessionData::LtsSessionData::getInstance()->isTablet() && strcmp(PROJECTBUILD, "guez") == 0) - return; //no more help in Guez OC - - connect(btn, &QPushButton::clicked, - this, [this]() - { - emit signalShowOnlineHelp(m_onlineHelpFileName); - }); - - btn->setVisible(true); -} - - -void LtsDialog::addDebugMenuButton() -{ -#ifdef LTS_DEBUG // debug menu - 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")); // add modified-indicator unicode 'bullet' - - 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::checkStateChanged)) 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; // TODO: rm - plain text only! - if (addChangesMadeSignal(widget, &QAbstractButton::toggled)) return; - if (addChangesMadeSignal(widget, &LtsDateTimeEdit::dateTimeChanged)) return; -} - - -void LtsDialog::setUnchangeable(bool unchangeable) // adjust structure to new functionality (NOTE: calling this twice without restoring will currently result in bad behaviour) -{ - 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) // do not reconnect if already connected (and do nothing if it's an unchangeable dialog) - { - QWidget::showEvent(event); - return; - } - - m_unchangeableDialog = true; // do not monitor changes, yet - m_firstShow = false; - - // the fact that all widgets are created before a window is shown, lets us connect to all editable widgets and listen to changes - - // TODO: mit chris drueber reden ;) evtl. event listener, im widget event abfangen oder an ganz anderer stelle ? - - QList listenWidgets = findChildren(); - - for (auto i = listenWidgets.begin(); i != listenWidgets.end(); ++i) - { - // TODO: more widgets - // TODO: NGLPositionEdit / LtsPositionEdit - - 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; // start monitoring changes only after this point, as user changes may now be made -} - - -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; - - - // case ParticipantEdit: - // LtsDialog::tryLockTopicNew is called by ParticipantEdit::onPartyChanged - // This slot is executed while - // - initialization (signal not connected) - // and - // - by user interaction (signal connected) - 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); // blocks! - // TODO: setUnchangeable(true); - m_topicInfo.setLocked(); - } - TRACE_LOG(m_logger) << LOGFUNCTION << " END"; -} - -void LtsDialog::initTopicLockHandler(const QString &topicName, int entityId) -{ - //qWarning() << "initTopicLockHandler"; - - 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; - - // similar to "initTopicLockHandler" - - 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) // Only use if Mike says it's okay! -{ - bool isAlreadyUnchangeable = unchangeable(); - - if (!isAlreadyUnchangeable) - setUnchangeable(true); // retranslation is no change! - - setWindowTitle(title); - -// qCritical() << " m_topicLockedBy : " << m_topicLockedBy.lockHint -// << ", operatorId=" << m_topicLockedBy.operatorId -// << ", workstationId=" << m_topicLockedBy.workstationId; - - if (!isAlreadyUnchangeable) - setUnchangeable(false); // retranslation done - - { - bool isNowAlreadyUnchangeable = unchangeable(); - - QPushButton *b = m_buttonBox->getButton(DialogButton::ReadOnly); - if (b) - { - connect(b, &QPushButton::clicked, this, &LtsDialog::onReadOnlyButtonClicked, Qt::UniqueConnection); - - // ExerciseDialog (CloseOnlyDialog -> no Ok-, Apply-Btn) - 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); - // setWidgetsCustom([](QComboBox *cb, bool readonly) { if (cb->lineEdit()) cb->lineEdit()->setReadOnly(readonly); }, unchangeable); - 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(); -// setWidgetsCustom([](QComboBox *cb, bool readonly) { if (cb->lineEdit()) cb->lineEdit()->setReadOnly(readonly); }, unchangeable); - restoreReadonly(); - restoreReadonly(); - restoreReadonly(); - restoreReadonly(); - - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); - restoreEnabled(); -} - - -void LtsDialog::slotChangesMade() // TODO: get receiver's name, add to changes-made-list to identify changes to specific widgets -{ - if (m_unchangeableDialog) - return; - - m_changesMade = true; - updateWindowTitle(); - - m_buttonBox->getButton(DialogButton::Ok)->setEnabled(true); - m_buttonBox->getButton(DialogButton::Apply)->setEnabled(true); - - emit signalChangesMade(); // tell dialogs that a Widget has received changes -} - - -void LtsDialog::resetChanges() -{ - m_changesMade = false; - - updateWindowTitle(); - //if (!m_title.isEmpty()) - //setWindowTitle(m_title); // set title back to original - no more [*] needed - - 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(); // as all changes were saved successfully - return true; - } - - return false; -} - - -Button LtsDialog::askApplyDiscardCancel() -{ - return LtsMessageDialog::askApplyDiscardCancel(this); -} - - -void LtsDialog::slotApply() // does not close! only saves. -{ - _apply(); -} - - -void LtsDialog::slotOkay() // closes and saves. (calls 'apply()', then calls 'close()') -{ - if (_apply()) // if there were errors while applying, do not close! If not, close. - _close(); -} - - -void LtsDialog::slotHide() -{ - _hide(); -} - - -void LtsDialog::slotClose() // closes but does NOT save (but asks for it (SaveDialog) - if 'save' was selected by the user in the SaveDialog, then 'okay()' will be called). -{ - // m_changesMade == true means that apply-btn is enabled - if ((m_changesMade) && (!m_unchangeableDialog)) - { - switch (askApplyDiscardCancel()) - { - case Button::Apply: - slotOkay(); // save & close - break; - case Button::Discard: - _discard(); // do not save, just close - break; - default: // cancel - break; // do nothing - } - } - else - { - _close(); // nothing has changed, so just 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); - //m_dialogHelper->hide(); - } -} - - -void LtsDialog::_close() -{ - resetChanges(); // for safety reasons - aboutToClose(); - if (m_dialogHelper != nullptr) - m_dialogHelper->close(); -} - -void LtsDialog::_discard() -{ - dialogCanceled(); - _close(); -} - -} // namespace LtsWidgets - - - - -#include "lts_dialog.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.h deleted file mode 100644 index 906d4cf7b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog.h +++ /dev/null @@ -1,544 +0,0 @@ -/** -* @RDE_HEADER@ -* Dialog for the new Layout system -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" -#include "lts_dialog_button_box.h" // so that LtsDialogs don't have to include it - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// Forward declaration - ui_requirement.h is not a UI file but regular C++ code -// Include is in cpp file to avoid AutoUIC issues -#include "ui_requirement.h" - - -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, // Ok saves changes, closes the Dialog - Apply only saves changes, does not close the Dialog - Cancel closes the Dialog if there are no changes made - - OkCancel, // TODO: rm - /* [deprecated => do not use! use 'OkApplyCancel' instead and tell the Dialog that 'this is a new element' by giving the - * translate-method inside the retranslate-method inside your LtsDialog either an empty/new dds element or by setting - * 'isNew' to true on one of the overloaded translate-methods - * OLD: Special case: only use if you are 100% sure what this is => example: New Message: there is only a SEND button, if sent, the dialog MUST be closed - */ - - CloseOnly, // For Dialogs that do not save anything - SeamlessDialog, // No buttons, no margins! -}; - - -/** - * @brief The LtsDialog class provides a Dialog for the Lts Layout System and is the equivalent to the QDialog. An LtsDialog can be added to a LayoutArea to interact with the user. - * WARNING: do not add widgets once the window is shown, as the ui requirements cannot be triggered after this point. - */ -class LTSWIDGETS_EXPORT LtsDialog : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool unchangeable READ unchangeable WRITE setUnchangeable) -public: - static int s_object_count; - - /** - * @brief LtsDialog TODO: ! - * @param parent - */ - explicit LtsDialog(QWidget *parent = nullptr, ButtonCombination buttonCombination = ButtonCombination::CloseOnly, bool closeable = true); - ~LtsDialog(); - - /** - * @brief getHelper returns the internal helper class which contains this LtsDialog on it (do not use, for LtsTabWidget's addTab only!) - * @return class internal helper class - */ - LtsDialogHelper *getHelper(); - - bool hasUnsavedChanges() const { return m_changesMade || m_currentlyBlocking; } - - /** - * @brief retranslate is called on first show and when Qt is told to retranslate the whole application, should be used in all LtsDialogs - * NOTE: Everything inside this method does NOT trigger slotChangesMade (which is good ;). Is called by the framework. - * NOTE: Do not call retranslate yourself. Not even if you've overriden it. That won't work. The frameworks needs to call it. - */ - virtual void retranslate() = 0; - - void closeRequest(); // call this from outside of this class to request the close of this dialog (save request dialog is shown if there are unsaved changes) - void setUnchangeable(bool unchangeable = true); - bool unchangeable() const { return m_unchangeableDialog; } - - /** - * @brief addDialog - * @param dialog - * @param menuGroup use m_menuGroup / menuGroup() << This is NOT the Tab Title - override 'retranslate' for Tab Titles - * @param identifier use QStringLiteral - example: "exmEditDialog" << This is NOT the Tab Title - override 'retranslate' for Tab Titles - * @param layoutArea - * @return false if dialog / tab already exists - determined by menuGroup + identifier - and so the Dialog / Tab is not added - */ - 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); // Do not use. - void __unchange__() { resetChanges(); } // Do not use. - void setClosable(bool closable) { m_isClosable = closable; } - bool isClosable() const { return m_isClosable; } - virtual void aboutToClose() {} // called just before the dialog is closed - void setMenuGroup(const QString &menuGroup) { m_menuGroup = menuGroup; } // Do not call. Called by Framework. - const QString &menuGroup() const { return m_menuGroup; } - void setIdentifier(const QString &identifier); // Do not call. Called by Framework. - virtual void tableColumnOrganiserSetting(){}; - const QString &identifier() const { return m_identifier; } - - void setOverlay(QWidget *widget); - void unsetOverlay(); - - /** - * @brief Sets the current width of the given widget as minimum width. - * Useful for Labels showing changing values to not jitter when then value changes - * Therefore you have to make sure, that the biggest (textual) value is set when calling this function - * @param Widget to set fixed width for - */ - static inline void setMinWidthForWidgetHelper(QWidget *w) - { - int width = w->sizeHint().width(); - w->setMinimumWidth(width); - } - - QString windowTitle() const - { - return m_title; - } - - - // neu tryLockTopic methode mit n Parametern, je nach Einsatzfall: - // ( LtsWidgets::TopicInfo ) - // Diese n Überladungen entsprechen jeweils eine Lock-Bedarfsfall. - // a) Dlg über pro Exercise sperren ("Exercise Purpose") - // b) Dlg über pro Exercise, PartyId sperren ("Edit Partitipants") - // jeder PartyChanged @ "Edit Partitipants" aktualisieren - // c) 2 verschiedene Dialog (pro Exercise) -> nur einer ReadWrite - // In beiden Dialog DENSELBEN topicName nutzen - - // "Gesperrt von anzeigen" - - // a) - void tryLockTopicNew(); - - - // b) - virtual void tryLockTopicNew (const QString &topicName, int entityId = -1); - - - void triggerTopicLock(); - - void setOnlineHelpFilename(const QString &fileName); // string without file extension !! - void setCurrentlyBlocking(bool currentlyBlocking = true) { m_currentlyBlocking = currentlyBlocking; } - -signals: - /** - * @brief Emit this signal to start the onHideAnimated Animation - * Only works when the dialog is a child of the LtsBaseLayoutWidget - * @param layoutArea the LayoutArea that should hide with an animation - */ - void startMainSelect(LtsWidgets::LayoutArea); - - /** - * @brief Emit this signal to start the onReShowAnimated Animation - * Only works when the dialog is a child of the LtsBaseLayoutWidget - * @param layoutArea the LayoutArea that should be shown again with an animation - */ - void endMainSelect(LtsWidgets::LayoutArea); - - /** - * @brief signalChangesMade is emitted after slotChangesMade was called - * (if you want to enable the Ok and/or Apply button, use slotChangesMade) - * For a list of supported Widgets, see LtsDialog::showEvent. - */ - void signalChangesMade(); - - void signalHide(QObject *object); // tells the TabWidget to remove the tab - - /** - * @brief Emit tells that all changes are reset - * and ok/apply buttons are now disabled - */ - void signalChangesReset(); // - - void signalFocused(); - void visibilityChanged(bool visible); - void signalLockTopic(const LtsWidgets::TopicInfo &topicInfo); - void signalShowOnlineHelp(const QString &fileName); - -protected: - /** - * @brief translate #1 is the same as translate #2, but does not retranslate the ui. DO NOT USE until permission by Mike! - * @param title - */ - void translate(const QString &title); - - - - template - /** - * @brief translate #2 is the same as translate #3, but only sets the title by a single parameter. DO NOT USE until permission by Mike! - * @param ui - * @param title - */ - void translate(T *ui, const QString &title) // main translate method! Only use if Mike says it's okay! - { - translate(title); - if (ui) - ui->retranslateUi(this); - // after ui->retranslate() to make sure all labels have the correct content - m_uiRequirements->retranslate(); - } - - void hideEvent(QHideEvent *event) override - { - QWidget::hideEvent(event); - emit visibilityChanged(false); - } - - template - /** - * @brief translate #3 is the same as translate #4, but lacks Topic handling. The window title is a combination of the given parameters. - * @param ui - * @param custom - * @param isNew set to true if this dialog is about creating a new entry (equivalent to Topic id 0) - * @param title - */ - 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)); - -// tryLockTopic(); - tryLockTopicNew(); - } - } - else - { - newTitle = (tr("New %1").arg(title)); - m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->hide(); // otherwise applying something after clicking apply results in another new object being created - m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setText(tr("Create")); // Deutsch: "Anlegen" - } - - translate(ui, newTitle); - } - - - template - /** - * @brief translate #4 is the same as translate #5, but without a Topic. 'customUniqueTopicName' and 'topicId' replace the missing Topic. - * @param ui - * @param customUniqueTopicName the name you would give the Topic class (e.g. "MeteoDataManagement::StaticWeather") - * @param topicId the Topic's id (e.g. 1) - use id smaller than 1 if it's a non-existent (new) entry - * @param custom - * @param title - */ - void translate(T* ui, const QString &customUniqueTopicName, int topicId, const QString &custom, const QString &title) - { - Q_UNUSED(customUniqueTopicName); - - initTopicLockHandler(customUniqueTopicName, topicId); - - translate(ui, custom /* can be empty */, - (topicId < 1 /* 0 is dds's equivalent to 'new', sadly there are cases where the id == -1, catch this case */), - title); - } - - - template - /** - * @brief translate #5 is the same as translate #6, but uses 'custom' as Topic name (for Topics without a 'name' entry) - * @param ui - * @param data - * @param custom the Topic's name (e.g. "Test Exercise 42") - * @param title - */ - void translate(T *ui, const S &data, const QString &custom, const QString &title) // same as 'void translate(T *ui, const S &data, const QString &title)', but for topics without the 'name' entry - { - translate(ui, QString::fromUtf8(typeid(data).name()), data.id(), custom /* can be empty */, title); - } - - - template - /** - * @brief translate #6 should be the most common and is easiest translate method for exactly one Topic. - * It locks the specific topic and creates a complex tab title with only a Topic and a general title. - * @param ui - * @param data the Topic which contains the id and the name (e.g. instance of ExerciseManagement::Exercise) - * @param title the topic of this dialog (e.g. "Exercise" if this Dialog is about a specific Exercise) - */ - void translate(T *ui, const S &data, const QString &title) - { - translate(ui, data, QString::fromStdString(data.name()), title); // -> translate #5 - } - - - // this is called when the dialog was cancel to be able to reset changes made - virtual void dialogCanceled() {} - LtsBaseLayoutWidget* baseLayoutWidget(); - void setNoTopicLock() { m_noLockTopic = true; } // Call in LtsDialog c_tor and nowhere else! - -private: - friend class LtsDialogHelper; - - bool m_changesMade; - bool m_currentlyBlocking { false }; // set to true if the dialog must not be closed because it is currently in a blocking call - bool m_firstShow; - QPointer m_baseLayoutWidget; // "cannot" outlive BLW, but to be sure.. - bool m_isClosable; - TopicInfo m_topicInfo; - -public: - QString m_topicLockedBy; - -private: - bool m_noLockTopic; - bool m_unchangeableDialog; // no '[*]' or asking in case of unsaved changes - QPointer m_dialogHelper; - //LtsProgressDialogHelper *m_progressDialogHelper; - QString m_title; - QString m_onlineHelpFileName; - QString m_menuGroup; - QString m_identifier; - - // overlay stuff: - 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); - - /** - * @brief setWindowTitle sets this window title and of the Dialog Helper (use retranslate() instead!) - * @param title - */ - 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()) - 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()) - widget->setEnabled(lastState.toBool()); - } - } - -public slots: - /** - * @brief slotChangesMade emits 'signalChangesMade()' and tells the dialog that changes on editable Widgets were made. So that the Save-Dialog will be shown on Close. - * For a list of supported Widgets, see LtsDialog::showEvent. - */ - void slotChangesMade(); - - void slotHide(); // hides dialog, removes tab, does not delete dialog - void slotLock(const QString &lockedBy); - void slotUnlock(); - -private slots: - void slotApply(); // does not close! only saves. - void slotOkay(); // closes and saves. (calls 'apply()', then calls 'close()') - void slotClose(); // closes but does NOT save (but asks for it (SaveDialog) - if 'save' was selected by the user in the SaveDialog, then 'okay()' will be called). - //void slotSizeChanged(const QSize &size); - void onReadOnlyButtonClicked(); - -protected: - virtual void lock(); - virtual void unlock(); - - virtual bool apply() { return true; } // reimpl to save your changes here, DO NOT CLOSE! return true if changes could be saved (this will close the dialog on 'okay') or return false if NOT (dialog will NOT be closed then) - void showEvent(QShowEvent *event) override; - Button askApplyDiscardCancel(); - - LtsDialogButtonBox *m_buttonBox; - UiRequirementList *m_uiRequirements; - - NGL::INGLLogger &m_logger; -}; - - -// support init WITHOUT parameter -template -T *initDialog(T *dialog) -{ - if (!dialog->init()) - { - dialog->deleteLater(); - return nullptr; - } - return dialog; -} - -// support init WITH parameter -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; -} - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp deleted file mode 100644 index f19e6bf92..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/** - * @RDE_HEADER@ - * Bottom Button Box for Dialogs which need actions - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#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); // hide all buttons - - // Btn 'Library' - 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); - - // exceptions for call above: - m_ui->pbOk->setVisible(true); // c LtsDialog's impl 2 c y ! - m_ui->pbClose->setVisible(true); // s.a.a. - // no help file name set = no help button ! => m_ui->pbHelp->setVisible(true); // must be explicitly hidden for dialogs without help content -} - - -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) -{ - // TODO: check if setText was called by a 'retranslate' method, if not debug msg ! - - 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; // sec -} - - -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: - //case DialogButton::Yes: - //case DialogButton::No: - { -#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); -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.h deleted file mode 100644 index bd34863e6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.h +++ /dev/null @@ -1,252 +0,0 @@ -/** - * @RDE_HEADER@ - * Bottom Button Box for Dialogs which need actions - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#pragma once - -#include "lts_widgets_export.h" -#include "lts_message_dialog.h" - -#include -#include - -namespace Ui -{ -class LtsDialogButtonBox; -} - -namespace LtsWidgets -{ -enum class DialogButton -{ - // Do not connect these: - ReadOnly, - Ok, - Apply, - Close, - Yes, - No, - - // You can connect these: - Remove, - Hide, - Add, - Help, - Edit, - Save, - Load, - Show, - Import, - Export, - CenterMap, - ZoomMapTo, - Library, - Print, - -#ifdef LTS_DEBUG - _debug_, -#endif -}; - - - -// TODO: doc: close is now autoconnected by NEW LtsDialogs not running in compatibility mode ! - -/** - * @brief The LtsDialogButtonBox class is a replacement for the NGLDialogButtonBox. With the significant difference that - * Actions ('Ok', 'Close', etc.) are select in code, not in the designer. - * - * Pro: - * > no dead/unconnected buttons - * > no designer plugin needed - * > no killed dialog boxes after editing uis - * > auto connecting the close button to the Dialogs accept / Widgets close (important for close requests on dialogs before closing) - * > solid connections only (safer & faster than connections by char*) [also con] - * - * Con: - * > no possibility to see which Actions are available for a specific dialog - * > solid connections only (more complicated) [also pro] - * - * To make Actions visible & usable, use 'connectButton' with the coherant Action (DialogButton). - * 'connectButton' works similar to Qt's Connect mechanism, with the main difference that there is neither a sender nor a signal to specify. - * - * Example: connectButton(DialogButton::Ok, this, &ThisDialog::accept); - * - * - * >>You have overloaded slots?<< - * For example you have: slotSomethingChanged(int) and slotSomethingChanged(QString) - * For the 'int'-version the result should look like: connect(DialogButton::Ok, this, static_cast(&ThisDialog::slotSOmethingChanged)); - * Unfortunately this is the Qt version to solve this problem.. - * - */ -class LTSWIDGETS_EXPORT LtsDialogButtonBox : public QWidget -{ - Q_OBJECT - -public: - explicit LtsDialogButtonBox(QWidget *parent); - ~LtsDialogButtonBox(); - - /** - * @brief connectButton connects a Button (like 'Apply', 'Ok', etc.) to the given receiver + slot. - * This method prevents the existence of dead buttons (not connected) and that a missing Designer plugin messes up the button properties. - * So, to make a button visible, use this method. - * - * Important: only solid connections possible (yet) - * Example: connectButton(DialogButton::Ok, this, &ThisDialog::accept); - * - * @param button the DialogButton to make visible and connect it to the given 'slot' - * @param receiver the receiver of the signal when a DialogButton is clicked - like the receiver in Qt's connect method - * @param slot the slot on the 'receiver' called when the clicked signal is called by a DialogButton - like the slot in the Qt's connect method - * @param type Qt::ConnectionType (does not need bo be set) - */ - template - void connectButton(DialogButton button, const typename QtPrivate::FunctionPointer::Object *receiver, - Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) - { - //if (!LtsDialog::plan_editor_compatibility) - { - 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; - - // special cases for Buttons with embedded Dialogs - 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); // this method's magic - } - 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)); - } - - /** - * @brief setText changes the text of a DialogButton to the 'text' specified. - * @param button the DialogButton to rename - * @param text new text of DialogButton - */ - 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); - - /** - * @brief setButtonEnabled enables / disables a DialogButton. - * @param button the DialogButton to dis-/enable - * @param enabled true = enabled; false = disabled - */ - void setButtonEnabled(DialogButton button, bool enabled = true); - bool isButtonEnabled(DialogButton button) const; - - void setButtonVisible(DialogButton button, bool visible = true); - bool isButtonVisible(DialogButton button) const; - - /** - * Do not use! - * @brief getButton DialogButton to real QPushButton* widget (do not use outside of this class!) - * @param button - * @return Pointer to QPushButton or nullptr when not found / available - */ - QPushButton *getButton(DialogButton button) const; - - /** - * @brief _showAll Do not use. - */ - void _setVisibilityForAll(bool visible); - -signals: - void signalRemoveClicked(); - void signalHelpClicked(); - void signalLibraryLoadClicked(); - void signalLibrarySaveClicked(); - -private slots: - void slotRemoveClicked(); - -private: - const bool m_isTablet; - - /** - * @brief setButtonVisible makes a QPushButton visible and also shows the horizontal line, etc. when it's first called (every button box is empty before one button was made visible) - * @param pb - */ - void setButtonsVisible(QPushButton *pb); - - Ui::LtsDialogButtonBox *m_ui; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_button_box.ui deleted file mode 100755 index 32152ae7f..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.cpp deleted file mode 100644 index ed035a63d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * @RDE_HEADER@ - * Helper Class for LtsDialog - creates Dialog Button Box and Scroll Area automatically - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-09-23 - * - */ - -#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) // set dialog only one time - return; - - m_dialog = dialog; - m_dialog->setContentsMargins(QMargins()); - if (m_dialog->layout()) - m_dialog->layout()->setContentsMargins(QMargins()); - m_ui->scrollArea->setWidget(m_dialog); - - //if (LtsDialog::plan_editor_compatibility) - { - connect(dialog, &LtsDialog::destroyed, this, [this]() - { - m_dialog = nullptr; - deleteLater(); - }); - } - -// connect(this, &LtsDialogHelper::signalSizeChanged, -// dialog, &LtsDialog::slotSizeChanged); -} - -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; - - // TODO: clear uiRequirements !! But tell devs first to move all uiReqs to retranslate() - - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.h deleted file mode 100644 index bb614fb76..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @RDE_HEADER@ - * Helper Class for LtsDialog - creates Dialog Button Box and Scroll Area automatically - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-09-23 - * - */ - -#pragma once - -#include -#include - - -namespace Ui -{ -class LtsDialogHelper; -} - - -namespace LtsWidgets -{ -class LtsDialogButtonBox; -class LtsDialog; - -/** - * @brief The LtsDialogHelper class => ***DO NOT USE***, so no export! - */ -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); } // to keep constness... - 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; - -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_dialog_helper.ui deleted file mode 100755 index ef1550ab4..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.cpp deleted file mode 100644 index d4d4b2199..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.cpp +++ /dev/null @@ -1,494 +0,0 @@ -/** -* @RDE_HEADER@ -* Draggable Icon with Text for Menus -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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::setStyleToButton() - - -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->setContentsMargins(0, 0, 0, 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; // Huchhhhhhhh, that should never happen - 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::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); // TODO: working version of this: , QTextOption(Qt::AlignRight | Qt::AlignBottom)); - painter.restore(); - - return iconImage; -} - -} // namespace LtsWidgets - - -#include "lts_drag_icon.moc" - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.h deleted file mode 100644 index 7a8bd3cb0..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drag_icon.h +++ /dev/null @@ -1,339 +0,0 @@ -/** -* @RDE_HEADER@ -* Draggable Icon with Text for Menus -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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; - -/** - @brief Tool button with drag & drop functionality - */ -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) - - - //last - Q_PROPERTY(LtsWidgets::Designer::ButtonStyle eStyle READ buttonStyleDesigner WRITE setButtonStyleDesigner) - - - /** - * @brief Set the ButtonStyle in Designer - * @param ButtonStyle to define the possible IconStyles - */ - void setButtonStyleDesigner(Designer::ButtonStyle t) { setButtonStyle(static_cast(t)); } - - /** - * @brief ButtonStyle Getter for Designer - * @return the current ButtonStyle for the DragIcon in the designer - */ - Designer::ButtonStyle buttonStyleDesigner() const { return static_cast(buttonStyle()); } - - /** - * @brief Set the ButtonSize in Designer - * @param ButtonSize - */ - void setIconSizeDesigner(Designer::ButtonSize t) { setIconSize(static_cast(t)); } - - /** - * @brief OpenType Getter for Designer - * @return the current BUttonSize Value for the DragIcon in the designer - */ - Designer::ButtonSize iconSizeDesigner() const { return static_cast(iconSize()); } - -public: - /** - * @brief Constructor for LtsDragIcon - * @param parent for the Widget - */ - explicit LtsDragIcon(QWidget *parent); - - virtual ~LtsDragIcon() = default; - - /** - * @brief set a QMenu to the Button - * @param QMenu to show - */ - void setMenu(QMenu *menu); - - /** - * @brief get QMenu from the Button - * @return QMenu or nullptr if no button or menu is defined - */ - QMenu* menu() const; - - /** - * @brief set a text to the Button - * @param QString as text shown - */ - void setText(const QString &text); - - /** - * @brief Returns the widget's text value - * @return Current text - */ - QString text() const { return m_text; } - - /** - * @brief set a context to the Button - * @param QString as context shown - */ - void setContext(const QString &context); - - /** - * @brief Returns the widget's context value - * @return Current context - */ - QString context() const { return m_context; } - - /** - * @brief set if the Button should be checkable - * By default, the button is not checkable. - * @param new checkable state - */ - void setCheckable(bool checkable); - - /** - * @brief Return if Button is checkable - * @return This property holds whether the button is checkable. - */ - bool isCheckable(); - - - /** - * @brief set if the Button should be draggable - * By default, the button is draggable. - * @param new dragabble state - */ - void setDraggable(bool draggable); - - /** - * @brief Return if Button is checkable - * @return This property holds whether the button is checkable. - */ - bool isDraggable() const { return m_isDraggable; } - - /** - * @brief (un)check the Button - * Only checkable buttons can be checked. By default, the button is unchecked. - * @param new checked state - */ - void setChecked(bool checked); - - /** - * @brief Return the current check state - * @return This property holds whether the button is checked. - */ - bool isChecked() const; - - /** - * @brief Use Icon / Default Icon when dragging - * By default, the icon is used. - * @param new checked state - */ - void setShowDragIcon(bool showDragIcon) { m_showDragIcon = showDragIcon; } - bool showDragIcon() const { return m_showDragIcon; } - - /** - * @brief set new the SameWidgetDrop State - * drag&drop property to allow sameWidgetDrops - * @param new drop state - */ - void setSameWidgetDrop(bool drop); - - /** - * @brief get wether SameWidgetDrop is enabled - * @return the drop state - */ - bool sameWidgetDrop() const; - - /** - * @brief Set the ButtonStyle in Code - * @param ButtonStyle to define the possible IconStyles - */ - void setButtonStyle(ButtonStyle style); - - /** - * @brief Returns the currently used ButtonStyle Value - * @return Current ButtonStyle - */ - ButtonStyle buttonStyle() const { return m_style; } - - /** - * @brief Set the ButtonSize in Code - * @param ButtonSize - */ - void setIconSize(ButtonSize buttonSize); - - /** - * @brief Returns the current used ButtonSize Value - * @return Current ButtonSize - */ - ButtonSize iconSize() const { return m_buttonSize; } - - /** - * @brief set new visibilty fo the Icon - * @param New visibilty - */ - void setButtonVisible(bool visible); - - /** - * @brief set new visibilty fo the Icon - * @param New visibilty - */ - bool buttonVisible() const; - - /** - * @brief set Icon to the Button - * @param New QIcon to use - */ - void setIcon(const QIcon &icon); - - /** - * @brief Return the current used QIcon - * @return This property holds the current used QIcon. - */ - QIcon icon() const { return m_icon; } - - /** - * @brief Set to Force the given IconSize and forbid resizing - * By default, the button resizes into given space - * @param bool to force the Icon to hold it's given - * IconSize even if the LayoutSystem provides more space to fill - */ - void setForceFixedSize(bool force); - - /** - * @brief Return the current ForeFixedSize value - * @return This property holds the set ForceFicedSize value - */ - bool isForceFixedSize() const { return m_forceFixedSize; } - - /** - * @brief set a QAction to the Button - * @param New QAction - * the new Action will override the old one and replaces - * the ObjectName with the new ActionName - */ - void setAction(QAction *action); - - /** - Adds dragButton to a button group - @param btnGrp button group to add dragButton - @param btnGrpId Button identifier in button group - */ - void addToButtonGroup(QButtonGroup *btnGrp, int btnGrpId); - - /** - * @brief Sets a dropHandler. The dropHandler must be allocated with new operator. The owner ship now has the LtsDragIcon. The LtsDragIcon calls delete in hier dtor - * @see LtsDropHandler - */ - void setDropHandlerKey(qulonglong key) { m_dropHandlerKey = key; } - - /** - * @brief Adds MimeData. - * This sets MimeData to the Drag Action - * @param QString with MimeType Description - * @param QByteArray with MimeData - */ - void addMimeData(const QString &mimeType, const QByteArray &data); - bool removeMimeData(const QString &mimeType); - void removeAllMimeData(); - - /** - * @brief Sets the drop acceptance of the Icon - * @param bool value to set drop acceptance - */ - void setAcceptDrops(bool on); - - qulonglong dropHandlerKey() const { return m_dropHandlerKey; } - - // Todo: Isn't this something like objectName? - 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: - /** - * @brief The Signal is emitted when the Button is activated - * (i.e. pressed down then released while the mouse cursor is inside the button) - * @param bool checked = false - */ - void clicked(); - - /** - * @brief The Signal is emitted when the Button is toggled - * @param bool checked - */ - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drop_handler.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drop_handler.h deleted file mode 100644 index 52dd5103f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_drop_handler.h +++ /dev/null @@ -1,141 +0,0 @@ -/** -** -** @RDE_HEADER@ -** Virtual base class of a simple drop handler. -** $Id$ -** $HeadURL$ -** $Author$ -** -** ----------------------------------------------------------------------------------------------- -** -** Hardware-Environment: Intel-PC -** Software-Environment: Windows 7, Linux, Qt 5.4. ArcGIS 10.2.5 -** -** Language ........: C++ 11 -** -** ----------------------------------------------------------------------------------------------- -** -** Author ...........: Jörg Turla, SPD G, Tel. 3132 -** -** ----------------------------------------------------------------------------------------------- -** -** Notes .........: -** -** 18.12.2015, JT : First edition for use with Qt5 -** -** ----------------------------------------------------------------------------------------------- -*/ - -#pragma once - - -class QMimeData; - - -namespace LtsWidgets -{ - /** - @class LtsDropHandler pdrophandler.h - @brief Interface class to handle drop events of special mime data type. - @see SituationMap::Map, SituationMap::DropEventManager - - The LtsDropHandler class is an interface class to handle drop event for one type of mime data. It is used from DropEventManager to process drop events. - The DropEventManager will call the doDrop function to process events. The doDrop fuction has to be implemented in derived class. - - Following example shows how to implement a drop handler. - - @code - - #include - - class DropCreatePolyLine : public LtsWidgets::LtsDropHandler - { - PlanEditor * m_planEditor; - PlanEditor::InternalData * m_planEditorInternal; - bool m_smooth; - - public: - - DropCreatePolyLine( PlanEditor * planEditor - , PlanEditor::InternalData * planEditorInternal - , bool smooth = false ) - : LtsWidgets::LtsDropHandler() - , m_planEditor(planEditor) - , m_planEditorInternal(planEditorInternal) - , m_smooth(smooth) {;} - - virtual ~DropCreatePolyLine() = default; - - void doDrop( int x, int y, const QMimeData * mimeData = nullptr) const override; - - }; // DropCreatePolyLine - - - void PlanEditor::installLineDropHandler( LtsWidgets::LtsDragIcon * dragIcon ) - { - qulonglong key = m_dropEventManager->add(new DropCreatePolyLine(this,m_internal)); - dragIcon->setDropHandlerKey( key ); - } // PlanEditor::installLineDropHandler( LtsWidgets::LtsDragIcon * dragIcon ) - - - void DropCreatePolyLine::doDrop( int x, int y, const QMimeData * mimeData ) const - { - SituationMap::Map * map; - SituationMap::RB::RubberbandMultiPointFeature * rubberband; - SituationMap::RB::RubberbandFactory & rbFactory; - - map = m_planEditorInternal->m_map; - rbFactory = map->rubberbandFactory(); - - if (m_smooth) rubberband = rbFactory.create(); - else rubberband = rbFactory.create(); - - if (rubberband != nullptr) - { - rubberband -> setAutoDelete( true ); - rubberband -> enableClosed( false ); - - uint dx = map->width() * 0.25; - uint dy = map->height() * 0.25; - - QVector lineCoordinates; - - lineCoordinates += map->viewPortWindowPointToGeoCoordinate(QPoint(x-dx,y-dy)); - lineCoordinates += map->viewPortWindowPointToGeoCoordinate(QPoint(x+dx,y+dy)); - - rubberband->setCoordinateVector( lineCoordinates ); - - QObject::connect( rubberband , &SituationMap::RB::Rubberband::apply - , m_planEditor, &PlanEditor::onPolylineCreate ); - QObject::connect( rubberband , &SituationMap::RB::Rubberband::abort - , m_planEditor, &PlanEditor::onRubberbandAbort ); - - } // DropCreatePolyLine::doDrop( int x, int y ) - - } // DropCreatePolyLine::doDrop( int x, int y ) const - - @endcode - */ - class LtsDropHandler - { - public: - - LtsDropHandler() = default; - virtual ~LtsDropHandler() = default; - - LtsDropHandler( const LtsDropHandler & ) = delete; - LtsDropHandler & operator = ( const LtsDropHandler & ) = delete; - - /** - Function to process drop events. - - @param x x coordinate of cursor position referred to widget pixel coordinates. - @param y y coordinate of cursor position referred to widget pixel coordinates. - @param mimeData Pointer to data to be dropped. - */ - virtual void doDrop( int x, int y, const QMimeData * mimeData ) const = 0; - - }; // class LtsDropHandler - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.cpp deleted file mode 100644 index 08576ede3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @RDE_HEADER@ - * LtsWidget - a QLabel which elides the text if necessary - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, NLE, 2018-12-17 - */ - -#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() -{ - // setText() is not virtual ... :/ - 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); - } -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.h deleted file mode 100644 index 4dbd3201b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_elided_label.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @RDE_HEADER@ - * LtsWidget - a QLabel which elides the text if necessary - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Christian Ehrlicher, NLE, 2018-12-17 - */ - -#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; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.cpp deleted file mode 100644 index 5eab728c1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/** -* @RDE_HEADER@ -* Events provided by LtsWidgets -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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; -} - - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.h deleted file mode 100644 index 06e06e44d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_events.h +++ /dev/null @@ -1,99 +0,0 @@ -/** -* @RDE_HEADER@ -* Events provided by LtsWidgets -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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; -}; - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp deleted file mode 100644 index 05679276b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/** -* @RDE_HEADER@ -* FrameLess Dialog with alternatie basecolor -* $Id$ -* $HeadURL$ -* $Author$ -* -* Created by Mike Wille, SLSE, 2016-03-09 -*/ - -#include "lts_frameless_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace LtsWidgets -{ -LtsFramelessDialog::LtsFramelessDialog(QWidget *parent, const Behaviour &behaviour) - : QDialog(parent) -{ - // Qt-Bug? Qt::FramelessWindowHint in the underlying windows results in - // mapToGlobal(QPoint(0,0)) == QPoint(0,0) when a popup window is show on top of it - Qt::WindowFlags wf; // TODO: FIX WM AFTER FAT!! = Qt::FramelessWindowHint; // make frameless, composite manager drops the shadow - // frameless dialogs cannot have (completety working) children dialogs => frames 4 fat - - 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]() - { - //qWarning() << "my pos" << QCursor::pos(); - //qWarning() << "my screen rect" << screenRectFromPoint(QCursor::pos()); - - QSize s = size(); - setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, screenRectFromPoint(QCursor::pos()))); - }); - //t->start(); TODO: impl this! -} - - -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); // graceful offset for desktop managers - - bool contains = s.contains(pos); - - //qWarning() << "screen" << s << " widget" << w << " contains?" << contains; - //qWarning() << "screen: x" << s.x() << "y" << s.y() << "h" << s.height() << "w" << s.width(); - //qWarning() << "widget: x" << w.x() << "y" << w.y() << "h" << w.height() << "w" << w.width(); - - 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) // get the parent's window and center the dialog on the center of the parent's window - { - QWidget *window = p->window(); - QPoint global = window->mapToGlobal(QPoint(0,0)); - QSize size = window->size(); - - r = QRect(global, size); - } - else // no parent - no alignment! - { - r = QGuiApplication::primaryScreen()->geometry(); - } - - QSize s = size(); - setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, r)); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.h deleted file mode 100644 index 4505e9bd2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_frameless_dialog.h +++ /dev/null @@ -1,61 +0,0 @@ -/** -* @RDE_HEADER@ -* FrameLess Dialog with alternatie basecolor -* $Id$ -* $HeadURL$ -* $Author$ -* -* Created by Mike Wille, SLSE, 2016-03-09 -*/ - -#pragma once - -#include - -#include "lts_base.h" -#include "lts_widgets_export.h" - - -namespace LtsWidgets -{ -/** - * @brief The Behaviour enum describes how the FramelessDialog handles user input while shown. - */ -enum class Behaviour -{ - Popup = 0, // a mouse click on any surface other than this dialog will close/hide this dialog - Modal, // disallow any input, but do not block the program execution (does not apply when exec() is used), and avoid rejecting when dialog is modal -}; - - -/** - * @brief The LtsFramelessDialog class creates a Popup-Dialog, without a frame, but with a shadow as border. This Popup-Dialog is centered in the window . - * The purpose is to cover the window the user is working with, to interupt the user with a message he has to attend first before continuing work. - * LtsFramelessDialog is actually a QWidget as this class uses a QWidget which casts a shadow and lays itself on it. Sorry. - * The Application must never be interrupted, so there is no exec(). - */ -class LTSWIDGETS_EXPORT LtsFramelessDialog : public QDialog // TODO: many methods use this to show messages, use LtsMessageDialog instead -{ - Q_OBJECT - -public: - /** - * @brief LtsFramelessDialog - * @param parent the widget to be covered - * @param behaviour see enum LtsWidgets::Behaviour - only affects this Dialog when currently shown - */ - 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; - -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp deleted file mode 100644 index 1caa38162..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// TODO: hdr - -#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 -//#include - -#ifdef Q_OS_UNIX -#include // backtrace(..) -#include // dladdr(..) -#include // demangle -#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)) // avoids blinking windows by waiting a little until the wait window will be hidden -{ - m_hideTimer->setSingleShot(true); - m_hideTimer->setInterval(500); - connect(m_hideTimer, &QTimer::timeout, - this, &LtsGlobalWaitDialog::hideIt); -} - - -void LtsGlobalWaitDialog::unarm() -{ - m_armed = false; - hideIt(); // force hide it (don't worry, does nothing if now shown) -} - - -void LtsGlobalWaitDialog::rearm() -{ - m_armed = true; - setVisible(true); // show again (if some component is still waiting) -} - - -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; // aka. invalid - - 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]); - } - - //qWarning() << "frame" << i << address << function << offset; - (void)offset; - } - - free(symbols); - -// if (nFrames == nMaxFrames) -// qWarning() << "[BACKTRACE TRUNCATED]"; -#endif -} - - -void LtsGlobalWaitDialog::wait(QWidget *parent, const QString &customText) -{ - m_progressDialog->setParentWidget(parent); - if (m_waitingCount < 1 && m_visible) // nothing is waiting - showIt(); - - setCustomText(customText); - - m_waitingCount++; // count even if not visible ! - - //qWarning() << "LtsGlobalWaitDialog ++" << m_waitingCount; - - //bt(); -} - - -void LtsGlobalWaitDialog::setCustomText(const QString &customText) -{ - m_progressDialog->setCustomText(customText); -} - - -void LtsGlobalWaitDialog::done() -{ - if (m_waitingCount == 0) - { - //qWarning() << "LtsGlobalWaitDialog: called done() while there is nothing waiting!"; - m_hideTimer->start(); - return; - } - - Q_ASSERT(m_waitingCount > 0); - - m_waitingCount--; - //qWarning() << "LtsGlobalWaitDialog --" << m_waitingCount; - - if (m_waitingCount == 0) // no more waiting classes - 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; -} -} // namespace LtsWidgets - - -/// Waiter -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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.h deleted file mode 100644 index db1bc62f5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_global_wait_dialog.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @RDE_HEADER@ - * Frameless Dialog, modal, exists exactly once per - a global/static wait dlg which counts for all compontens that are waiting for their operation to be done but don't want to own a progress dialog ! - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2017-03-17 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -class QTimer; -class QWidget; - - -namespace LtsWidgets -{ -class LtsDialog; -class LtsOverlayLoading; -class LtsProgressDialogHelper; -/** - * @brief The LtsGlobalWaitDialog class is a global/static wait dlg which counts for all compontens that are waiting for their operation to be done but don't want to own a progress dialog ! - */ -class LTSWIDGETS_EXPORT LtsGlobalWaitDialog : public QObject -{ - Q_OBJECT - -public: - static LtsGlobalWaitDialog* getInstance(); - void wait(QWidget *parent, const QString &customText = QString()); // shows the Progress Dialog - void wait(const QString &customText = QString()) { wait(nullptr, customText); } - void done(); // hides the Progress Dialog IF all wait()s were canceled with done() ! - void setVisible(bool visible = true); - void setCustomText(const QString &customText = QString()); // call with empty string to reset text to default text "waiting for server" - void unarm(); // deactivate the global wait dialog from showing up (e.g. wait dialog should not be recorded while recording a script in the script editor) - void rearm(); // undo the unarm (re-activate global wait dialog) - -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; -}; - -} // namespace LtsWidgets - - - -// 'hyper-global' stuff into main namespace -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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.cpp deleted file mode 100644 index 4cc3163c1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** -* @RDE_HEADER@ -* GridLaout with auto column layouter. -* Layouts all widgets depending on the maxColumn value -* releayouts if maxColumn changed -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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); - //setHorizontalSpacing(1); - setVerticalSpacing(1); - setContentsMargins(0, 0, 0, 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; - - //resetInternalSize - m_colSize = 0; - - for (QWidget *wid : std::as_const(m_widgetList)) - removeWidget(wid); - - for (QWidget *wid : std::as_const(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); // resort, also remove the layoutitem - break; - } - } -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.h deleted file mode 100644 index 4620eee63..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_grid_layout.h +++ /dev/null @@ -1,92 +0,0 @@ -/** -* @RDE_HEADER@ -* GridLaout with auto column layouter. -* Layouts all widgets depending on the maxColumn value -* releayouts if maxColumn changed -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#pragma once -#include -#include - -#include "lts_widgets_export.h" -#include "lts_base.h" - -class QWidget; - -namespace LtsWidgets -{ -/** -* @brief The LtsGridLayout class provides a LayoutItem for the Lts Layout System and is the equivalent to the QGridLayout. -* An LtsGridLayout can be added to a Widget as any other LayoutItem -* A LtsGridLayout got two modes: -* The first one is the normal operation Mode with a fixed ColumnSize -* every time a widget is added wit add_widget (not addWidget!) -* it will use the next column at the current used row -* when maxColum is reached in the current row -* the Widget will be added to a new row -* -* The Second Mode is enabeled with AutoSize -* There the MaxColumnSize will be calculated -* when a rezize event accure -*/ -class LTSWIDGETS_EXPORT LtsGridLayout : public QGridLayout -{ - Q_OBJECT -public: - /** - * @brief Constructor for LtsGridLayout - * @param ButtonSize for calcualting Gridzize on autoSize - * @param Parent widget - * @param max Column to use - */ - explicit LtsGridLayout(LtsWidgets::ButtonSize buttonSize, QWidget *parent = nullptr, int maxColumn = 1); - - /** - * @brief adds a QWidget to the Layout in the current Row - * if MaxColumn is reached, the widget will be added to a new row - * @param QWidget addet to the Layout - */ - void add_widget(QWidget *widget); - - /** - * @brief enables autoSize - * calculates on rezize the MaxColum count for given ButtonSize - * @param enable autoSize - */ - void setAutoSize(bool enable); - - /** - * @brief set MaxColumn - * @param int maxColum count - */ - 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; - - -}; - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp deleted file mode 100644 index 78da73dc6..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/** -* @RDE_HEADER@ -* Widget that visualizes the topography of a bunch of points along a rubberband -* $Id$ -* $HeadURL$ -* $Author$ -* Created by Malte Schroeder, NLSE, 2018-03-07 -*/ - -#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; // Laenge, Hoehe - - double m_length; - double m_minElevation; - double m_maxElevation; - double m_nullX; - double m_nullY; - double m_scaleX; - double m_scaleY; - QPainterPath m_drawPoints; // PainterPath der Daten, unskaliert - 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; // aktuelle Groesse des Widgets - QTransform m_trans1; // nur Verschiebung des Koordinatensystem - QTransform m_trans2; // Translation Pixel <-> Daten - QTransform m_transInverted; // m_trans1.inverted() * m_trans2.inverted() - QTransform m_transCombined; // m_trans2 * m_trans1 - - QPixmap m_pixmap; - - mutable int iPositionHint; -}; - -double LtsHeightPlotWidget::Private::getDataHeight(double pos) const -{ - if (m_heightList.count() <= 1) - return 0; - // boundary checks - 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; - // y = m*x + n - 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(); - // boundary checks - 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; - // pos = pos1 + pos1 * prozent(p1,p2) - 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) - { - // y = m*x + n - 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(); - - // Punkt testen ob er auf der Linie liegt - const double yRes = m * point.x() + n; - if (fabs(yRes - point.y()) < 1.) - { - // und schauen ob er nicht hinter dem zweiten Punkt liegt - 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); - } - // some sanity checks: - 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; - } - - // Laenge auf 5^(n-1) m runden - double n2 = pow(5, floor(log5(fabs(minElevation))) - 1); - if (qFuzzyIsNull(n2)) - n2 = 0.1; // avoid division by zero - - 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; - - // Laenge auf 10^n / 2 m runden - 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)); - //fprintf(stderr, "point at len %f\n", len); - } - 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(); - - // 10 Pixel nach rechts und y = 0 ist unten anstatt oben - m_trans1.translate(10, height); - // + und - y tauschen - damit im ersten Quadrant x und y positiv sind - m_trans1.scale(1, -1); - // und noch etwas vom Rand weg - m_trans1.translate(border, border); - // und wieder zurueck - m_trans1.scale(1, -1); - - // die aktuelle Breite und Hoehe relativ zu der Breite und Hoehe der Daten - m_trans2.scale((width - 2 * border) / (wertebereichX), - (height - 2 * border) / (wertebereichY)); - // m_minElevation ist unser '0-Wert', minY demnach der kleinste y-Wert - m_trans2.translate(0, minY); - // + und - y tauschen - damit im ersten Quadrant x und y positiv sind - 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::SmoothPixmapTransform | - QPainter::TextAntialiasing | - QPainter::Antialiasing; - p.setRenderHints(hints); - p.setTransform(m_trans1); - - // Daten zeichnen - p.setPen(QPen(m_fillBrush, 0.1)); - p.setBrush(m_fillBrush); - p.drawPath(m_trans2.map(m_drawPoints)); - p.setRenderHint(QPainter::Antialiasing, false); - - // Marker zeichnen - 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); - //fprintf(stderr, "marker at len %f\n", len); - - p.drawLine(m_trans2.map(l)); - } - - // Koordinatensystem + Beschriftung - p.setPen(m_axisPen); - p.setFont(m_textFont); - - // Y-Achse - p.drawLine(m_trans2.map(QPointF(m_nullX, m_minElevation - m_scaleY)), - m_trans2.map(QPointF(m_nullX, m_maxElevation + m_scaleY))); - - // kleine Striche - 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)); - } - - // grosse Striche - 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.horizontalAdvance(str)); // important, see docu! - r.moveTo(p1.x() - r.width() / 2., p1.y() - r.height() / 2.); - p.drawText(r, str, Qt::AlignHCenter | Qt::AlignVCenter); - } - - // X-Achse - p.drawLine(m_trans2.map(QPointF(-m_scaleX, m_nullY)), - m_trans2.map(QPointF(m_scaleX + m_length, m_nullY))); - - // kleine Striche - 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)); - } - - // grosse Striche - 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.horizontalAdvance(str)); // important, see docu! - 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) - { - // Mouse-Position zeichnen - 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); - - //keine Transformation setzen da sonst die Linienstaerke des Pens veraendert wird! - //p.setTransform(d->m_transCombined); - 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))); - - // Sichtlinie zeichnen - 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()))); - - // nur hier da die anderen beiden Linien sonst nicht schoen aussehen - 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::Window, 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); - - // vorgefertiges Bild zeichnen - p.drawPixmap(0, 0, d->m_pixmap); - - // und jetzt die Hilfslinien - 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->position()); - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.h deleted file mode 100644 index e4d83f1eb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_height_plot_widget.h +++ /dev/null @@ -1,97 +0,0 @@ -/** -* @RDE_HEADER@ -* Widget that visualizes the topography of a bunch of points along a rubberband -* $Id$ -* $HeadURL$ -* $Author$ -* Created by Malte Schroeder, NLSE, 2018-03-07 -*/ - -#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(); - - /** - * Set the data in the form - * QPoint - x/y values - * double - height values - * @param data the height data to display - */ - void setData(const QVector > &data); - /** - * Set the markers to show - * QPoint - x/y values - * @param data the markers to display - */ - void setMarks(const QVector &data); - - /** - * Set the background of the widget - */ - void setBackgroundBrush(const QBrush &brush); - /** - * Set the font for the axis labels - */ - void setTextFont(const QFont &f); - /** - * Set the pen used for the axis lines - */ - void setAxisPen(const QPen &pen); - /** - * Set the pen for the position marker - */ - void setPositionPen(const QPen &pen); - /** - * Set the pen for the viewline - */ - void setViewLinePen(const QPen &pen); - /** - * Set the brush for the contour (+ fill) - */ - void setContourBrush(const QBrush &brush); - /** - * Set the pen for the markers - */ - void setMarkerPen(const QPen &pen); - -Q_SIGNALS: - /** - * The current mouse position has changed. - * @param curPoint the current point (in map coordinates) of the mouse - * @param curHeight the current height at the given point - * @param mouseHeight the current height at the current mouse point (>= curHeight) - */ - void mousePositionChanged(const QPointF &curPoint, double curHeight, double mouseHeight); - /** - * A line of sight has started. This signal can emitted more than once - * wich means that the old line of sight was stopped - * @param startPoint the startpoint (in map coordinates) of the line - */ - void startLineOfSight(const QPointF &startPoint); - /** - * A line of sight has stopped - */ - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.cpp deleted file mode 100644 index 398eae7af..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @RDE_HEADER@ - * An Image Editor which shows an Image, and emits a signal when clicked (to edit/swap an Image) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-09-19 - */ - -#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) // enabled by default - , m_bDrawOuterRect(true) -{} - - -void LtsImageEditor::setPixmap(const QPixmap &pixmap) -{ - if (pixmap.isNull()) // invalid / empty pixmap given, trying to replace it with one from lts_resources - m_pixmap = QPixmap(QStringLiteral(":style/noimage.png")); // when this fails, the pixmap is still invalid, so no checks needed TODO: add noimage.png ! - else - m_pixmap = pixmap; - - update(); - emit imageChanged(); -} - - -void LtsImageEditor::enterEvent(QEnterEvent *) -{ - //qWarning() << "enter"; - m_isHovered = true; - update(); -} - - -void LtsImageEditor::leaveEvent(QEvent *) -{ - //qWarning() << "leave"; - m_isHovered = false; - update(); -} - - -bool LtsImageEditor::event(QEvent *event) -{ - if (event->type() == QEvent::MouseButtonPress && m_menuEnabled) - { - emit signalClicked(); - event->accept(); - m_isHovered = false; // unhovered on click - update(); - return true; - } - - return QWidget::event(event); // otherwise.. -} - - -void LtsImageEditor::paintEvent(QPaintEvent* event) // TODO: colors from palette (style plugin) ? -{ - QPainter p(this); - - // draw profile picture - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - QBrush brush = QColor(QStringLiteral("white")); // TODO: get from palette - - - if (!m_pixmap.isNull()) - { - if (!(m_isHovered && m_menuEnabled)) // draw if NOT hovered or draw when hovered but menu is disabled - { - 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; - } - } - - - // draw frame - p.setPen(QPen(QColor("gray"), 2)); - p.setBrush(brush); - p.drawRect(rect()); - - - // draw menu icon - if (m_isHovered && m_menuEnabled) - { - p.setPen(QPen(QColor(QStringLiteral("gray")), (size().height() / 48 * 2))); // respect height, width is unnecessary - - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.h deleted file mode 100644 index 32284f424..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_image_editor.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @RDE_HEADER@ - * An Image Editor which shows an Image, and emits a signal when clicked (to edit/swap an Image) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-09-19 - */ - -#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); - - /** - * @brief setPixmap sets the picture of this button. If no pixmap / picture is set, the picture-layer is button colored. - * @param pixmap The picture to draw on the button. - */ - void setPixmap(const QPixmap &pixmap); - - /** - * @brief getPixmap TODO - * @return - */ - QPixmap pixmap() const { return m_pixmap; } - - /** - * @brief setMenuEnabled changes the hover & click behaviour. If the menu is enabled, the profile button indicates a menu on hovering, and by clicking it, the signal 'signalClicked' is emitted (menu is enabled by default) - * @param enabled true if menu is enabled, false if not - */ - 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(QEnterEvent *); - void leaveEvent(QEvent *); - bool event(QEvent *event); - -private: - QPixmap m_pixmap; - bool m_isHovered; - bool m_menuEnabled; - bool m_bDrawOuterRect; - -signals: - /** - * @brief signalClicked is emitted when this widget has been clicked. - */ - void signalClicked(); - - void imageChanged(); -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.cpp deleted file mode 100644 index d6a150c58..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @RDE_HEADER@ - * Info Dialog, containing informations about this software & build - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, NLSE, 2018-02-26 - */ - -#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)); - - // project logo (have different sizes) - 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; -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.h deleted file mode 100644 index 72a25648d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @RDE_HEADER@ - * Info Dialog, containing informations about this software & build - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, NLSE, 2018-02-26 - */ - -#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; -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_info_dialog.ui deleted file mode 100755 index c45855d1b..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.cpp deleted file mode 100644 index d22bc6623..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @RDE_HEADER@ - * Class of Led Widget - * $Id$ - * $HeadURL$ - * $Author$ - */ - -#include "lts_led_widget.h" -#include -#include -#include -namespace LtsWidgets -{ - -void LtsLedWidget::resizeEvent(QResizeEvent *event) -{ - switch (m_aspectHint) { - case AspectHint::NoHint: - { - // NOTE: the next 4 lines would overwrite set properties from ui files -// setMinimumWidth(0); -// setMaximumWidth(65000); -// setMinimumHeight(0); -// setMaximumHeight(65000); - 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; - } -} - -/** - * The following values were determined by trial and error: - */ -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, // Center - minSize, // Radius - QPointF(width(), height()) / GRADIENT_FOCALPOINT_OFFSET); // FocalPoint - 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(); -} - -} // namespace namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.h deleted file mode 100644 index 55c54d61b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_led_widget.h +++ /dev/null @@ -1,178 +0,0 @@ -/** - * @RDE_HEADER@ - * LTS LED Widget - * $Id$ - * $HeadURL$ - * $Author$ - */ -#pragma once - -#include -#include "lts_widgets_export.h" - -namespace LtsWidgets -{ -/** - * This class generates a LED with 2D/3D design, customizable colors and animation. - * - * If you need more than two states, simple derive from this class, only use "status=on" - * generate pairs of identifiers and colors and change the colors with appropriate method: - * e.g.: - * DerivedLEDWidget(...) : LtsLedWidget() {} - * enum Status { ST_STATUS1 = 0, ST_STATUS2, ST_STATUS3 }; - * static QMap cMap; // Fill it respectively - * DerivedLEDWidget::setStatus(Status b) { setOnColor(cMap.value(b, Qt::black)); } - */ - -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) - - /** - * Constructor - * @param parent The widget's parent. Default value is NULL. - * @param oncolor The color for LED when turned on. Default value is Qt::red, see also setOnColor() for details. - * @param offcolor The color for LED when turned off. Default value is Qt::black (i.e. "Use darker color"), see also setOffColor() for details. - * @param useGradient The 2D/3D design status. Default value is true (3D). - * @param animtime The animation time in msecs. Default value is 0. - */ - 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); - - /** - * Set animation time in msec - * Value = 0 shows no animation - * Reasonable value is between 400 and 700 msecs - * @param time time in msecs - */ - void setAnimTime(int time) { m_animTime = time; } - - /** - * Get current animation time in msecs - * @return animation time in msecs - */ - int animTime() const { return m_animTime; } - - /** - * Get current led status - * @return the LED's status - */ - bool status() const { return m_status; } - - /** - * Set 2D/3D design - * Value true set a 3D design of the led widget, - * Value false set a 2D design of the led widget - * @param useGradient - */ - void setUseGradient(bool useGradient); - - /** - * Get a state info about the current 2D/3D design - * @return true for 3D and false for 2D design - */ - bool usesGradient() const { return m_gradient; } - - /** - * Set the color for the LED when turned "on". - * If status is "on" and the oncolor is changed - * the animation will be run. - * @param oncolor color for LED in state "on" - */ - void setOnColor(const QColor &oncolor); - - /** - * Get the current oncolor - * @return the current oncolor - */ - QColor onColor() const { return m_oncolor; } - - /** - * Set the color for the LED when turned "off". - * If offcolor is black the wigdet use a darker version of oncolor as offcolor value. - * If status is "off" and the offcolor is changed - * the animation will be run. - * @param offcolor color for LED in state "off" - */ - void setOffColor(const QColor &offcolor); - - /** - * Get the current offcolor - * @return the current offcolor - */ - QColor offColor() const { return m_offcolor; } - - /** - * Convenience function set oncolor and offcolor in one step - * Default value for offcolor is black. - * If offcolor is black the wigdet use a darker version of oncolor as offcolor value. - * @param oncolor The color for LED when turned on. - * @param offcolor The color for LED when turned off. Default value is Qt::black (i.e. "Use darker color"). - */ - void setColor(const QColor &oncolor, const QColor &offcolor = Qt::black); - - /** - * Get current color - * @return the currently visible color - */ - QColor currentColor() const { return m_currentcolor; } - - void resizeEvent(QResizeEvent *event); - - AspectHint aspectHint() const { return m_aspectHint; } - -public slots: - /** - * Public slot set current led status - * @param status The LED's status (on/off) - */ - void setStatus(bool status); - - void setAspectHint(AspectHint aspectHint) - { - m_aspectHint = aspectHint; - } - -private: - /** - * Set current color - * Needed for color animation - * ***This method is private for a reason!*** - * @param currentcolor - */ - void setCurrentColor(const QColor ¤tcolor); - - /** - * Private function to start the color animation - */ - 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; -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.cpp deleted file mode 100644 index d002b89b0..000000000 --- a/qt6-after/qt6-migrated-legatus/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(); // NOT 'parent' ! - 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); -} - - -} // namespace LtsMessageDialog - - - -// LtsWidgets -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); // Message Dialogs are loose instances that must delete theirselves, because noone keeps a pointer to the message dialog - - connect(m_dialog, &LtsFramelessDialog::destroyed, - this, &LtsMessageDialogNew::deleteLater); - - QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog); - - QString text; - if (!title.isEmpty()) // title given? - { - text = QStringLiteral("%1

").arg(title); - } - - text += message; - - QLabel *messageLabel = new QLabel(text, m_dialog); - messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); // make text selectable by mouse - mainLayout->addWidget(messageLabel); - - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->setContentsMargins(0, 0, 0, 0); - - // TODO: FIXME: can this be called after the selection was made and the shadow just dies ? - //connect(m_dialog, &LtsFramelessDialog::rejected, this, &LtsMessageDialogHelper::slotCancel); // respect that non-modal dialogs can be closed by clicking outside of it - - 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); // same as yes - - if (buttons.testFlag(Button::Discard)) - connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::discard); // same as no - - 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) // ok and cancel should not be used together, so there is no check needed (as only one button can be the default button) - { - btn->setDefault(true); - } - - return btn; -} - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.h deleted file mode 100644 index e39986c9a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include "lts_widgets_export.h" -#include "lts_frameless_dialog.h" - -#include -#include - - -class QWidget; -class QHBoxLayout; - - -namespace LtsWidgets -{ - -/** - * @brief The Button enum describes one or more buttons, which should be shown on a Dialog which supports LtsWidgets::Button. Also this enum - * is the return type of LtsMessageDialog and provides the button clicked to close the Popup-Dialog. - */ -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)); -} - -/** - * @brief [Please consider using LtsMessageDialogNew] The LtsMessageDialog class provides a BLOCKING Popup-Dialog (based on LtsFramelessDialog) to show a message to the user with selectable buttons. This class is static and cannot be instantiated. - * To show a message to the user class 'LtsMessageDialog::showMessage(..)'. And to get the button that was selected by the user to close the dialog, consider the return type 'LtsWidgets::Button'. - * This Popup-Dialog closes automatically if any button on it is clicked. - * The return type is an enum class instance which contains the information about the clicked button. - * If no button was specified there is no chance to close the dialog if LtsWidgets::Behaviour::Modal was specified. - */ -namespace LtsMessageDialog // in reality this is the helper, but for "naming's sake", this is not called the helper -{ -/** - * @brief showMessage shows a Popup-Dialog to show a message to the user with selectable buttons - method is blocking (creates new EventLoop)! - * @param parent The widget to be covered. - * @param message The message to the user. - * @param behaviour How the Popup-Dialog should behave (see LtsFramelessDialog). - * @param buttons A list of Buttons to provide to the user. - * @param buttonTexts If set, the button texts are used from there instead the default ones - * @return The button that has been clicked by the user to close this Popup-Dialog. - */ -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); -} // namespace LtsMessageDialog - - - -/** - * @brief The LtsMessageDialog class provides a NON-BLOCKING Popup-Dialog (based on LtsFramelessDialog) to show a message to the user with selectable buttons. - * To show a message to the user use: connect(new LtsMessageDialogNew(this, title, message), &LtsMessageDialogNew::ok, this, &MyDialog::mySlot); - * This Popup-Dialog closes automatically if any button on it is clicked. - * Keep in mind that as this dialog does not block, following code gets executed right afetr creating this dialog. - */ -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 /* retain c++ conformity by this convenience bool */); - -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(); - -}; - -} // namespace LtsWidgets - -Q_DECLARE_OPERATORS_FOR_FLAGS(LtsWidgets::Buttons) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.cpp deleted file mode 100644 index 2a10e0126..000000000 --- a/qt6-after/qt6-migrated-legatus/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); // Message Dialogs are loose instances that must delete theirselves, because noone keeps a pointer to the message dialog - - QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog); - - QString text; - if (!title.isEmpty()) // title given? - { - text = QStringLiteral("%1

").arg(title); - } - - text += message; - - QLabel *messageLabel = new QLabel(text, m_dialog); - messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); // make text selectable by mouse - mainLayout->addWidget(messageLabel); - - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->setContentsMargins(0, 0, 0, 0); - - // TODO: FIXME: can this be called after the selection was made and the shadow just dies ? - connect(m_dialog, &LtsFramelessDialog::rejected, this, &LtsMessageDialogHelper::slotCancel); // respect that non-modal dialogs can be closed by clicking outside of it - - 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); // same as yes - - if (buttons.testFlag(Button::Discard)) - connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent), - &QPushButton::clicked, this, &LtsMessageDialogHelper::slotDiscard); // same as no - - 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(); - - // this class is called a dialog, but is not a QDialog, so there is no event loop to wait for a result to return, so we create our own event loop - QEventLoop loop; - connect(this, &LtsMessageDialogHelper::signalQuitEventLoop, &loop, &QEventLoop::quit, Qt::QueuedConnection); - loop.exec(); // now wait for new result value before returning - - 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) // ok and cancel should not be used together, so there is no check needed (as only one button can be the default button) - { - btn->setDefault(true); - } - - return btn; -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.h deleted file mode 100644 index e92ed91b5..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_message_dialog_helper.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_message_dialog.h" - -#include -#include -#include - - -class QPushButton; -class QHBoxLayout; - - -namespace LtsWidgets -{ - -/** - * @brief The LtsMessageDialogHelper class cannot be exported and only is a helper class which is actually instantiated, shows the LtsMessageDialog. - * To provide the blocking call to showMessage (call with return type), an event loop is created and exited when the user has selected a button to close the dialog. - * If the behaviour is non-modal, closing the Dialog by clicking outside of it will return Button::Cancel. - */ -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(); -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.cpp deleted file mode 100644 index b88fa9309..000000000 --- a/qt6-after/qt6-migrated-legatus/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); // ..UGH.. -} - -void LtsMSecDateTimeEdit::setTimezoneHoursOffset(int timezoneHoursOffset) -{ - qint64 b4 = msecsSinceEpoch(); - m_timezoneHoursOffset = timezoneHoursOffset; // set timezone AFTER reading the old value (otherwise timezone conversion would fail) - setMsecsSinceEpoch(b4); // apply timezone by re-setting -} - -void LtsMSecDateTimeEdit::setDateTime(const QDateTime &dateTime) -{ - // TODO: doesn't make sense, as toMSecsSinceEpoch returns the *UTC version* of the dt, so the timespec stuff does nothing - setMsecsSinceEpoch(QDateTime::fromMSecsSinceEpoch(dateTime.toMSecsSinceEpoch(), QTimeZone(m_timezoneHoursOffset * 3600)).toMSecsSinceEpoch()); -} - -void LtsMSecDateTimeEdit::setMsecsSinceEpoch(qint64 msecs) -{ - QDateTimeEdit::setDateTime(QDateTime::fromMSecsSinceEpoch(msecs, QTimeZone(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); -} - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_msec_date_time_edit.h deleted file mode 100644 index 7a8c5630d..000000000 --- a/qt6-after/qt6-migrated-legatus/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; -}; - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp deleted file mode 100644 index 94468a10b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @RDE_HEADER@ - * SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers DER LOSER, NLSE, 2018-05-07 - */ - -#include "lts_multi_range_spinbox.h" - -#include -#include -#include - -namespace LtsWidgets -{ - -LtsMultiRangeSpinBox::LtsMultiRangeSpinBox(QWidget *parent) - : QSpinBox(parent) -{ - m_curVal = minimum(); - -} - -/*! - * \brief LtsSpinBoxNew::validate - * \param input - * \param pos - * \brief only used if enter pressed - * \return - */ - -QValidator::State LtsMultiRangeSpinBox::validate(QString &input, int &pos) const -{ -// QIntValidator validator; - -// int minimumVal = minimum(); -// int maximumVal = maximum(); -// validator.setBottom(minimum()); -// validator.setTop(maximum()); -// QValidator::State state = validator.validate(input, pos); -// if (state == QValidator::Invalid || -// state == QValidator::Intermediate) -// { -// return state; -// } -// // Now we're sure, it's an int. Therefore toInt() will always be okay: -// int value = input.toInt(); -// value -= minimum(); -// return (value % singleStep() == 0) ? QValidator::Acceptable : QValidator::Intermediate; - Q_UNUSED(input); - Q_UNUSED(pos); - return QValidator::Acceptable; -} - -void LtsMultiRangeSpinBox::fixup(QString &str) const -{ - Q_UNUSED(str); - -// QSpinBox::fixup(str); - -// if (singleStep() == 1) -// { -// return; -// } -// bool bOk = false; -// int value = str.toInt(&bOk); -// if (!bOk) -// { -// return; -// } -// fixupValue(value); - -// str = QString::number(value); -} - -QValidator::State LtsMultiRangeSpinBox::validateAll(QString &input, int &pos) -{ - for(QPair range : std::as_const(m_rangesList)) - { - QIntValidator validator(range.first, range.second); - QValidator::State valState = validator.validate(input, pos); - if (valState == QValidator::Acceptable) - { - return valState; - } - } - return QValidator::Invalid; - - //return validate(input, pos); -} -/*! - * \brief LtsSpinBoxNew::setGaps - * \param gaps - * \author Niklas Schmidt - */ -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 : std::as_const(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) - { - /*! - Check if the new value is lower or higher the past value - Example: Gap between 50 and 100. Value changed from 100 to 99 requested - So the new value is lower so the SpinBox need to be corrected down to 49 - !*/ - - 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) - { - /*! - Check if the new value is lower or higher the past value - Example: Gap between 50 and 100. Value changed from 100 to 99 requested - So the new value is lower so the SpinBox need to be corrected down to 49 - !*/ - - - 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())); - } -} - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h deleted file mode 100644 index 4276bbdc2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_multi_range_spinbox.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @RDE_HEADER@ - * SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers, NLSE, 2018-05-07 - */ - -#pragma once - -#include "lts_widgets_export.h" -#include -#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); - //void buildRegex(); - int fixupValue(int value) const; - int fixupValue(int value, bool higher) const; -private: - QVector> m_rangesList; - - int m_curVal = minimum(); - QRegularExpression m_regex; -}; - - - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.cpp deleted file mode 100644 index 4085a5ce1..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.cpp +++ /dev/null @@ -1,197 +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; - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} -} // namespace LtsWidgets - - - - - - - - - - - - - - - - - - -// diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.h deleted file mode 100644 index 222e84ee4..000000000 --- a/qt6-after/qt6-migrated-legatus/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; -}; -} // namespace LtsWidgets - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_notification_box.ui deleted file mode 100755 index bb58cd5a8..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.cpp deleted file mode 100644 index db5f4c332..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @RDE_HEADER@ - * User Menu on active sessions (logout, etc.) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#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(); // swaps only possible if more than 1 screen is available - - 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); //Da startDetached static ist wird hier drauf nicht zugegriffen - 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")); -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.h deleted file mode 100644 index 700453197..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @RDE_HEADER@ - * User Menu on active sessions (logout, etc.) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-08-17 - */ - -#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(); -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_operator_menu.ui deleted file mode 100755 index aed0c23cc..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.cpp deleted file mode 100644 index db860dd1b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @RDE_HEADER@ - * An overlay for LtsDialog's setOverlay(..) which shows a 'loading bar' - * (a progress bar in a special state) and a default label "Loading ...", which - * can be altered - * $Id: $ - * $HeadURL: $ - * $Author: $ - * - * Created by Mike Lennart Wille, SLTE, 2022-07-07 - */ - -#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); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.h deleted file mode 100644 index 7a7b9dc8c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @RDE_HEADER@ - * An overlay for LtsDialog's setOverlay(..) which shows a 'loading bar' - * (a progress bar in a special state) and a default label "Loading ...", which - * can be altered - * $Id: $ - * $HeadURL: $ - * $Author: $ - * - * Created by Mike Lennart Wille, SLTE, 2022-07-07 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace Ui { class LtsOverlayLoading; } - - -namespace LtsWidgets -{ -/** - * @brief The LtsOverlayLoading class is an overlay for LtsDialog's - * setOverlay(..) which shows a 'loading bar' (a progress bar in a special - * state), a default label "Loading ..." - which can be altered with - * 'setText(..)' - and a cancel button (hidden by default) that emits the - * 'cancel' signal. - */ -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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_overlay_loading.ui deleted file mode 100755 index 2389e32b3..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.cpp deleted file mode 100644 index 491ee6710..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/** - * @RDE_HEADER@ - * Crop pictures - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Raphael Becker - */ - -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.h deleted file mode 100644 index e5b76fe99..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_picture_view.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @RDE_HEADER@ - * Crop pictures - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Raphael Becker - */ - -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.cpp deleted file mode 100644 index fe8ec97ce..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @RDE_HEADER@ - * A Button with an elliptic Pixmap and a menu icon when hovering. In the style of LtsStyle. - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-06-20 - */ - -#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) // enabled by default -{ - QSizePolicy qsp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - qsp.setHeightForWidth(true); - setSizePolicy(qsp); -} - - -void LtsProfileButton::setPixmap(const QPixmap &pixmap) -{ - if (pixmap.isNull()) // invalid / empty pixmap given, trying to replace it with one from lts_resources - m_pixmap = QPixmap(QStringLiteral("icon:no_pic.svg")); // when this fails, the pixmap is still invalid, so no checks needed - else - m_pixmap = pixmap; - - update(); -} - - -void LtsProfileButton::enterEvent(QEnterEvent *event) -{ - //qWarning() << "enter"; - m_isHovered = true; - update(); - QWidget::enterEvent(event); -} - - -void LtsProfileButton::leaveEvent(QEvent *event) -{ - //qWarning() << "leave"; - m_isHovered = false; - update(); - QWidget::leaveEvent(event); -} - - -bool LtsProfileButton::event(QEvent *event) -{ - if (event->type() == QEvent::MouseButtonPress && m_menuEnabled) - { - emit signalClicked(); - event->accept(); -// m_isHovered = false; // unhovered on click - update(); - return true; - } - - return QWidget::event(event); // otherwise.. -} - - -void LtsProfileButton::paintEvent(QPaintEvent* event) // TODO: colors from palette (style plugin) ? -{ - QPainter p(this); - p.save(); - - int minSize; - QPointF center; - minSize = height() > width() ? width() : height(); - center = QPointF(width() / 2.0, height() / 2.0); - - - // draw profile picture - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); -// QBrush brush = QColor(QStringLiteral("white")); // TODO: from palette - - QPixmap pixmap(minSize, minSize); - pixmap.fill(Qt::white); - - if (!m_pixmap.isNull()) - { - if (!(m_isHovered && m_menuEnabled)) // draw if NOT hovered or draw when hovered but menu is disabled - { - pixmap = m_pixmap.scaled(minSize, minSize, Qt::KeepAspectRatioByExpanding); - } - } - -// p.setBrush(Qt::white); -// p.setPen(Qt::NoPen); - 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); - - - - // draw frame - p.setPen(QPen(QColor("gray"), 2)); - p.drawEllipse(center, - minSize / 2.0 - 4, - minSize / 2.0 - 4); - - - // draw menu icon - if (m_isHovered && m_menuEnabled) - { - p.setPen(QPen(QColor(QStringLiteral("gray")), (minSize / 48 * 2))); // respect height, width is unnecessary - - 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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.h deleted file mode 100644 index ac13ac800..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_profile_button.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @RDE_HEADER@ - * A Button with an elliptic Pixmap and a menu icon when hovering. In the style of LtsStyle. - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-06-20 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include - - -namespace LtsWidgets -{ - -/** - * @brief The LtsProfileButton class provides a Button which has a round form, containing a picture. On hovering the picture turns into - * a "menu" icon to symbolize the clickability of the button. - * If no pixmap / picture is set, the picture-layer is button colored. - */ -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); - - /** - * @brief setPixmap sets the picture of this button. If no pixmap / picture is set, the picture-layer is button colored. - * @param pixmap The picture to draw on the button. - */ - void setPixmap(const QPixmap &pixmap); - - const QPixmap &pixmap() const { return m_pixmap; } - - /** - * @brief setMenuEnabled changes the hover & click behaviour. If the menu is enabled, the profile button indicates a menu on hovering, and by clicking it, the signal 'signalClicked' is emitted (menu is enabled by default) - * @param enabled true if menu is enabled, false if not - */ - void setMenuEnabled(bool enabled = true) { m_menuEnabled = enabled; } - bool menuEnabled() const { return m_menuEnabled; } - - -protected: - void paintEvent(QPaintEvent *event); - void enterEvent(QEnterEvent *event); - void leaveEvent(QEvent *event); - bool event(QEvent *event); - -private: - QPixmap m_pixmap; - bool m_isHovered; - bool m_menuEnabled; - -signals: - /** - * @brief signalClicked is emitted when the button has been clicked. - */ - void signalClicked(); -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp deleted file mode 100644 index faa7e9ca7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @RDE_HEADER@ - * Helper Class, which manages a progress Dialog with two states. - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-06-20 - */ - -#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) // save mem - , 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(); //delete m_waitDlg; // when there is already an instance - 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(); // TODO: needed when StyOnTop is active ? - - //QCoreApplication::processEvents(); // do not look glitched if dds hangs -} - - -void LtsProgressDialogHelper::slotStopWaitForReplyWindow(const ReplyResult &replyResult, const QString &replyString) -{ - if (m_waitDlg == nullptr) - { - // TODO: debug/warning that there is no wait dlg.. - return; - } - - //qDeleteAll(m_waitDlg->children()); // delete the layout and it's children - m_waitDlg->deleteLater(); // the easiest way - m_waitDlg = nullptr; - - // .. a new layout, telling the reply result - QString resultText = tr("Unknown result (%1)!").arg((int)replyResult); - - switch (replyResult) - { - case ReplyResult::Close: - { - return; // say nothing, just close - } - - 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); // TODO: or close? - - newLayout->addWidget(btn); - - m_waitDlg->setLayout(newLayout); - m_waitDlg->show(); - - //QCoreApplication::processEvents(); // do not look glitched if dds hangs -} - - -void LtsProgressDialogHelper::accept() -{ - m_waitDlg->accept(); - m_waitDlg->deleteLater(); - m_waitDlg = nullptr; - emit accepted(); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h deleted file mode 100644 index b172e6e67..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_progress_dialog_helper.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @RDE_HEADER@ - * Helper Class, which manages a progress Dialog with two states. - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-06-20 - */ - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include - -class QLabel; -class QWidget; - -namespace LtsWidgets -{ -class LtsFramelessDialog; - -/** - * @brief The LtsProgressDialogHelper class is a helper class, which can be used to easily - * show a Dialog with eather a wait state (indefinite progress bar), or a default message, - * adjusted to the result type (see ReplyResult) - */ -class LTSWIDGETS_EXPORT LtsProgressDialogHelper : public QObject -{ - Q_OBJECT - -public: - /** - * @brief The ReplyResult enum describes the behaviour of the slotSopWaitForReplyWindow method. - */ - enum class ReplyResult - { - Close = 0, // say nothing, just close automatically - Success, // dialog with success message, has to be receipted - Failure, // dialog with failure message plus reply string, has to be receipted - TimeOut, // dialog with timeout message, has to be receipted - }; - - explicit LtsProgressDialogHelper(QWidget *parent = nullptr); - ~LtsProgressDialogHelper(); - bool isShown() const { return !!m_waitDlg; } - void setCustomText(const QString &customText = QString()); - - void setParentWidget(QWidget *newParent); - -public slots: - /** - * @brief slotShowWaitForReplyWindow shows a centered wait dialog which does not allow the user to interact with the application and cannot be canceled. - */ - void slotShowWaitForReplyWindow(/*const QString &textToShow*/); - - /** - * @brief slotStopWaitForReplyWindow closes the wait dialog. Nothing happens when there was no show wait dialog called before. - * @param replyResult The wait dialog is closed on ReplyResult::Close. A new centered, modal dialog is shown on ReplyResult::Failure/Success/Timeout with an error message in it. - * @param replyString An explanatory string shown on ReplyResult::Failure to tell the user what went wrong on. - */ - void slotStopWaitForReplyWindow(const LtsWidgets::LtsProgressDialogHelper::ReplyResult &replyResult = ReplyResult::Close /* say nothing, just close */, - const QString &replyString = QString()); - -private: - QPointer m_waitDlg; - QWidget *m_parentWidget; - QPointer m_textLabel; - -signals: - /** - * @brief accepted is emitted when accept() was called (e.g. by clicking okay) - */ - void accepted(); - -private slots: - void accept(); -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.cpp deleted file mode 100644 index 1f5ac3109..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.cpp +++ /dev/null @@ -1,848 +0,0 @@ -/** - * @RDE_HEADER@ - * A slider that has a minimum and a maximum value, thus can define a range within a range. - * That could be a a snippet out of a video, a time range within an exercise... - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers, NLE, 2020-01-06 - */ - -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "lts_range_slider.h" - -// Qt includes -#include -#include -#include -#include -#include -#include -#include - -namespace LtsWidgets -{ - -class LtsRangeSliderPrivate -{ - Q_DECLARE_PUBLIC(LtsRangeSlider) -protected: - LtsRangeSlider *const q_ptr; -public: - /// Boolean indicates the selected handle - /// True for the minimum range handle, false for the maximum range handle - enum Handle - { - NoHandle = 0x0, - MinimumHandle = 0x1, - MaximumHandle = 0x2 - }; - Q_DECLARE_FLAGS(Handles, Handle) - - explicit LtsRangeSliderPrivate(LtsRangeSlider &object); - void init(); - - /// Return the handle at the given pos, or none if no handle is at the pos. - /// If a handle is selected, handleRect is set to the handle rect. - /// otherwise return NoHandle and handleRect is set to the combined rect of - /// the min and max handles - Handle handleAtPos(const QPoint &pos, QRect &handleRect) const; - - /// Copied verbatim from QSliderPrivate class (see QSlider.cpp) - int pixelPosToRangeValue(int pos) const; - int pixelPosFromRangeValue(int val) const; - - /// Draw the bottom and top sliders. - void drawMinimumSlider(QStylePainter *painter) const; - void drawMaximumSlider(QStylePainter *painter) const; - - /// End points of the range on the Model - int m_minimumValue = 0; - int m_maximumValue = 100; - - /// End points of the range on the GUI. This is synced with the model. - int m_minimumPosition = 0; - int m_maximumPosition = 100; - - /// Controls selected ? - QStyle::SubControl m_minimumSliderSelected = QStyle::SC_None; - QStyle::SubControl m_maximumSliderSelected = QStyle::SC_None; - - /// See QSliderPrivate::clickOffset. - /// Overrides this ivar - int m_subclassClickOffset = 0; - - /// See QSliderPrivate::position - /// Overrides this ivar. - int m_subclassPosition = 0; - - /// Original width between the 2 bounds before any moves - float m_subclassWidth = 0; - - LtsRangeSliderPrivate::Handles m_selectedHandles = {}; - - /// When symmetricMoves is true, moving a handle will move the other handle - /// symmetrically, otherwise the handles are independent. - 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); - - // The functinos hitTestComplexControl only know about 1 handle. As we have - // 2, we change the position of the handle and test if the pos correspond to - // any of the 2 positions. - - // Test the MinimumHandle - 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); - - // Test if the pos is under the Maximum handle - 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); - - // The pos is above both handles, select the closest handle - 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 //if (q->orientation() == Qt::Vertical) - { - 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; -} - -// -------------------------------------------------------------------------- -// Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp -// -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; -} - -//--------------------------------------------------------------------------- -// Draw slider at the bottom end of the range -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 - // On mac style, drawing just the handle actually draws also the groove. - QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, - QStyle::SC_SliderHandle, q); - painter->setClipRect(clip); -#endif - painter->drawComplexControl(QStyle::CC_Slider, option); -} - -//--------------------------------------------------------------------------- -// Draw slider at the top end of the range -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 - // On mac style, drawing just the handle actually draws also the groove. - 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); -} - -// -------------------------------------------------------------------------- -// Render -void LtsRangeSlider::paintEvent(QPaintEvent *) -{ - Q_D(LtsRangeSlider); - QStyleOptionSlider option; - initStyleOption(&option); - - QStylePainter painter(this); - - // ------------------------------------------------------------------------- - // Render complete range - // - 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; - // Move to minimum to not highlight the SliderGroove. - // On mac style, drawing just the slider groove also draws the handles, - // therefore we give a negative (outside of view) position. - 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()))); - } - - // ----------------------------- - // Render the range - // - QRect groove = style()->subControlRect(QStyle::CC_Slider, - &option, - QStyle::SC_SliderGroove, - this); - groove.adjust(0, 0, -1, 0); - - // Create default colors based on the transfer function. - // - 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()); - - // TODO: Set this based on the supplied transfer function - //QColor l = Qt::darkGray; - //QColor u = Qt::black; - - 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)); - - // ----------------------------------- - // Render the sliders - // - 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); - } -} - -// -------------------------------------------------------------------------- -// Standard Qt UI events -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.; - //qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition); - 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; - - // save the position of the mouse inside the handle for later - d->m_subclassClickOffset = mepos - (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top()); - - setSliderDown(true); - - if (d->m_selectedHandles != handle_) - { - d->m_selectedHandles = handle_; - update(handleRect); - } - // Accept the mouseEvent - ev->accept(); - return; - } - - // if we are here, no handles have been pressed - // Check if we pressed on the groove between the 2 handles - - 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) - { - // warning lost of precision it might be fatal - 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.; - //qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition); - setSliderDown(true); - if (!isMinimumSliderDown() || !isMaximumSliderDown()) - { - d->m_selectedHandles = QFlags(LtsRangeSliderPrivate::MinimumHandle) | - QFlags(LtsRangeSliderPrivate::MaximumHandle); - update(handleRect.united(sr)); - } - ev->accept(); - return; - } - ev->ignore(); -} - -// -------------------------------------------------------------------------- -// Standard Qt UI events -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); - - // Both handles are down (the user clicked in between the handles) - 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); - // prevent the range from becoming smaller - 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; - } - - // Only the lower/left slider is down - if (isMinimumSliderDown() && !isMaximumSliderDown()) - { - double newMinPos = qMin(newPosition, d->m_maximumPosition); - setPositions(newMinPos, d->m_maximumPosition + (d->m_symmetricMoves ? d->m_minimumPosition - newMinPos : 0)); - } - // Only the upper/right slider is down - 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(); -} - -// -------------------------------------------------------------------------- -// Standard Qt UI mouseEvents -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; - // Test the MinimumHandle - 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; - } - // Test the MaximumHandle - 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); -} - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.h deleted file mode 100644 index c3a1f4f6b..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_range_slider.h +++ /dev/null @@ -1,219 +0,0 @@ -/** - * @RDE_HEADER@ - * A slider that has a minimum and a maximum value, thus can define a range within a range. - * That could be a a snippet out of a video, a time range within an exercise... - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers, NLE, 2020-01-06 - */ - -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ -#pragma once - -#include "lts_widgets_export.h" - -// Qt includes -#include - -namespace LtsWidgets -{ - -class LtsRangeSliderPrivate; - -/// \ingroup Widgets -/// -/// A ctkRangeSlider is a slider that lets you input 2 values instead of one -/// (see QSlider). These values are typically a lower and upper bound. -/// Values are comprised between the range of the slider. See setRange(), -/// minimum() and maximum(). The upper bound can't be smaller than the -/// lower bound and vice-versa. -/// When setting new values (setMinimumValue(), setMaximumValue() or -/// setValues()), make sure they lie between the range (minimum(), maximum()) -/// of the slider, they would be forced otherwised. If it is not the behavior -/// you desire, you can set the range first (setRange(), setMinimum(), -/// setMaximum()) -/// TODO: support triggerAction(QAbstractSlider::SliderSingleStepSub) that -/// moves both values at a time. -/// \sa ctkDoubleRangeSlider, ctkDoubleSlider, ctkRangeWidget -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: - /// Constructor, builds a ctkRangeSlider that ranges from 0 to 100 and has - /// a lower and upper values of 0 and 100 respectively, other properties - /// are set the QSlider default properties. - explicit LtsRangeSlider(Qt::Orientation o, QWidget *parent = nullptr); - explicit LtsRangeSlider(QWidget *parent = nullptr) - : LtsRangeSlider(Qt::Vertical, parent) - {} - ~LtsRangeSlider(); - - /// - /// This property holds the slider's current minimum value. - /// The slider silently forces minimumValue to be within the legal range: - /// minimum() <= minimumValue() <= maximumValue() <= maximum(). - /// Changing the minimumValue also changes the minimumPosition. - int minimumValue() const; - - /// - /// This property holds the slider's current maximum value. - /// The slider forces the maximum value to be within the legal range: - /// The slider silently forces maximumValue to be within the legal range: - /// Changing the maximumValue also changes the maximumPosition. - int maximumValue() const; - - /// - /// This property holds the current slider minimum position. - /// If tracking is enabled (the default), this is identical to minimumValue. - int minimumPosition() const; - void setMinimumPosition(int min); - - /// - /// This property holds the current slider maximum position. - /// If tracking is enabled (the default), this is identical to maximumValue. - int maximumPosition() const; - void setMaximumPosition(int max); - - /// - /// Utility function that set the minimum position and - /// maximum position at once. - void setPositions(int min, int max); - - /// - /// When symmetricMoves is true, moving a handle will move the other handle - /// symmetrically, otherwise the handles are independent. False by default - bool symmetricMoves() const; - void setSymmetricMoves(bool symmetry); - - /// - /// Controls the text to display for the handle tooltip. It is in addition - /// to the widget tooltip. - /// "%1" is replaced by the current value of the slider. - /// Empty string (by default) means no tooltip. - QString handleToolTip() const; - void setHandleToolTip(const QString &toolTip); - - /// Returns true if the minimum value handle is down, false if it is up. - /// \sa isMaximumSliderDown() - bool isMinimumSliderDown() const; - /// Returns true if the maximum value handle is down, false if it is up. - /// \sa isMinimumSliderDown() - bool isMaximumSliderDown() const; - -Q_SIGNALS: - /// - /// This signal is emitted when the slider minimum value has changed, - /// with the new slider value as argument. - void minimumValueChanged(int min); - /// - /// This signal is emitted when the slider maximum value has changed, - /// with the new slider value as argument. - void maximumValueChanged(int max); - /// - /// Utility signal that is fired when minimum or maximum values have changed. - void valuesChanged(int min, int max); - - /// - /// This signal is emitted when sliderDown is true and the slider moves. - /// This usually happens when the user is dragging the minimum slider. - /// The value is the new slider minimum position. - /// This signal is emitted even when tracking is turned off. - void minimumPositionChanged(int min); - - /// - /// This signal is emitted when sliderDown is true and the slider moves. - /// This usually happens when the user is dragging the maximum slider. - /// The value is the new slider maximum position. - /// This signal is emitted even when tracking is turned off. - void maximumPositionChanged(int max); - - /// - /// Utility signal that is fired when minimum or maximum positions - /// have changed. - void positionsChanged(int min, int max); - -public Q_SLOTS: - /// - /// This property holds the slider's current minimum value. - /// The slider silently forces min to be within the legal range: - /// minimum() <= min <= maximumValue() <= maximum(). - /// Note: Changing the minimumValue also changes the minimumPosition. - /// \sa stMaximumValue, setValues, setMinimum, setMaximum, setRange - void setMinimumValue(int min); - - /// - /// This property holds the slider's current maximum value. - /// The slider silently forces max to be within the legal range: - /// minimum() <= minimumValue() <= max <= maximum(). - /// Note: Changing the maximumValue also changes the maximumPosition. - /// \sa stMinimumValue, setValues, setMinimum, setMaximum, setRange - void setMaximumValue(int max); - - /// - /// Utility function that set the minimum value and maximum value at once. - /// The slider silently forces min and max to be within the legal range: - /// minimum() <= min <= max <= maximum(). - /// Note: Changing the minimumValue and maximumValue also changes the - /// minimumPosition and maximumPosition. - /// \sa setMinimumValue, setMaximumValue, setMinimum, setMaximum, setRange - 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); - - // Description: - // Standard Qt UI events - void mousePressEvent(QMouseEvent *ev) override; - void mouseMoveEvent(QMouseEvent *ev) override; - void mouseReleaseEvent(QMouseEvent *ev) override; - - // Description: - // Rendering is done here. - void paintEvent(QPaintEvent *ev) override; - void initMinimumSliderStyleOption(QStyleOptionSlider *option) const; - void initMaximumSliderStyleOption(QStyleOptionSlider *option) const; - - // Description: - // Reimplemented for the tooltips - bool event(QEvent *event) override; - -protected: - QScopedPointer d_ptr; - -private: - Q_DECLARE_PRIVATE(LtsRangeSlider) - Q_DISABLE_COPY(LtsRangeSlider) -}; - -} // namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.cpp deleted file mode 100644 index 0ff6dddc8..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/** - * @RDE_HEADER@ - * ComboBox's popup, optimized for tablets (bigger dropdown/popup) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-03-09 - */ - -#include "lts_selection_popup.h" -#include "ui_lts_selection_popup.h" -#include "lts_base.h" - -#include -#include -#include -#include - -//#define NEVER -#ifdef NEVER -# include -#endif // NEVER - - -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) // TODO: morph .ui to QDialog (is QWidget) - , m_currentIndex(currentIndex) - , m_firstIndex(currentIndex) - , m_filter(new QSortFilterProxyModel(this)) - , m_valid(false) -{ - m_ui->setupUi(this); // i am sorry for this solution, but qlistview's are so unthankfully to work with - - 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 - // debugging : -// // highlighted -> UserInteraction -// connect(this, static_cast(&LtsSelectionPopup::highlighted), this, [](int idx){ -// qCritical() << " called: highlighted(int)"; -// }); - - // currentIndexChanged -> programmatically & UserInteraction - connect(this, static_cast(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){ - qCritical() << " LtsSelectionPopup: called: currentIndexChanged(int), idx=" << idx; - }); - - // activated -> UserInteraction - connect(this, static_cast(&LtsSelectionPopup::activated), this, [](int idx){ - qCritical() << " LtsSelectionPopup: called: activated(int), idx=" << idx; - }); - // : debugging - #endif // NEVER -} - - -LtsSelectionPopup::~LtsSelectionPopup() -{ - delete m_ui; -} - - -void LtsSelectionPopup::slotFilterChanged() -{ - m_filter->setFilterWildcard(m_ui->leSearch->text().size() ? m_ui->leSearch->text() : QStringLiteral("*") /* show everything if nothing was entered */); - refresh(); -} - - -void LtsSelectionPopup::refresh() -{ - m_valid = false; // invalidate, none selected as all deleted - qDeleteAll(m_ui->itemLayoutWidget->findChildren()); // delete all RBs - 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); // is always selected then.. - 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) // if there is only one item, then allow the user to click Select (or pressing enter) to select the only item available - 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) -{ - // order of signals (like Qt 5.9): 1st: currentIndexChanged 2nd: activated - - if (bCurrentIndexChanged) - emit currentIndexChanged(row); - - if (bActivated) - emit activated(row); - - m_ui->pbSelect->setEnabled(true); - - LtsFramelessDialog::accept(); // only accept if one item is selected -} - - -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; // currentIndex has changed? - bool bActivated = true; - selectItem(realRow, bCurrentIndexChanged, bActivated); // & closes dialog - return; // no need to go further - } - } - - // okay, nothing was selected, is there only one item available? if yes, select it and accept - if (count == 1) - { - const RadioButton *rb = items.at(0); - selectItem(rb->row(), true, true); // item was not selected before, so it must also be emitted as "activated" // & closes dialog - return; // no need to go further - } - // none selected and multiple choises? ignore.. -} - -} // namespace LtsWidgets - -#include "lts_selection_popup.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.h deleted file mode 100644 index 2e45022fa..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @RDE_HEADER@ - * ComboBox's popup, optimized for tablets (bigger dropdown/popup) - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Mike Wille, SLSE, 2016-03-09 - */ - -#pragma once - -#include "lts_frameless_dialog.h" -#include "lts_widgets_export.h" - - -namespace Ui -{ -class LtsSelectionPopup; -} - -class QAbstractItemModel; -class QSortFilterProxyModel; - -namespace LtsWidgets -{ - -/** - * @brief The LtsSelectionPopup is a popup window (LtsFramelessDialog) with a list of all QRadioButtons from a model with a search/filter edit. - * (former 'LtsComboBoxPopup' class WAS an internal Popup class for the LtsComboBox and is not meant for export) - * This is a custom Popup which is touch-friendly and screen-centered. - */ -class LTSWIDGETS_EXPORT LtsSelectionPopup : public LtsFramelessDialog -{ - Q_OBJECT - -public: - /** - * @brief LtsSelectionPopup creates a popup with all its items (from model) in it - * @param model the model which contains all items - * @param currentIndex which item is currently selected (to not select any item: a value smaller than 0) - * @param parent parent of this widget (not the LtsComboBox, the actual parent) - */ - LtsSelectionPopup(QAbstractItemModel *model, - int currentIndex, - QWidget *parent = nullptr); - ~LtsSelectionPopup(); - -protected: - /** - * @brief showEvent selects the search/filter widget on show - * @param event - */ - void showEvent(QShowEvent *event) override; - -private: - Ui::LtsSelectionPopup *m_ui; - int m_currentIndex; - int m_firstIndex; - QSortFilterProxyModel *m_filter; - bool m_valid; // selection is valid? (e.g. not hidden because it's filtered) - - /** - * @brief refresh deletes all Radio Buttons and creates them new (e.g. filtering) - */ - void refresh(); - - /** - * @brief selectItem emits signals that tell the connected slot which item was selected - * @param row the row number of the model - * @param text the text of the radio button - * @param activate if the item was activated (see QRadioButton doc) - by default false as it not not used everytime - */ - void selectItem(int row, bool bCurrentIndexChanged, bool bActivated); - -signals: - - // void highlighted(int index); // not supported yet - - // activated(row); // Qt::Doc: is only emitted when the change is caused by user interaction - - // currentIndexChanged(row); // Qt::Doc: is always emitted regardless if the change was done programmatically or by user interaction - // My note: If the uses selects the already selected item, only activated is emitted. - - - /** - * @brief activated is emitted when the selection has changed or is confirmded by the user - * Qt::Doc: is only emitted when the change is caused by user interaction - * Addon: If the uses selects the already selected item, only activated is emitted. - */ - void activated(int index); - - /** - * @brief currentIndexChanged is emitted when the current item changes (by user interaction or programmatically) - * Qt::Doc: is always emitted regardless if the change was done programmatically or by user interaction - * Addon: If the uses selects the already selected item, only activated is emitted. - */ - void currentIndexChanged(int index); - -public slots: - /** - * @brief accept is called when the selection ends successfully (user double-clicked an item or clicked the 'Select' button) and emits signalSelectItem - */ - void accept() override; - - /** - * @brief slotFilterChanged is called if the search / filter text changed and recreates all Radio Buttons after filtering - */ - void slotFilterChanged(); - -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_selection_popup.ui deleted file mode 100755 index 60f6d52aa..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.cpp deleted file mode 100644 index f9283b121..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * @RDE_HEADER@ - * SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers, NLSE, 2018-05-07 - */ - -#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; - } - - // Now we're sure, it's an int. Therefore toInt() will always be okay: - 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; - // In eigene Variable - auto newExclusions = exclusionRanges; - // Hinzufügen? - if (preserveCurrentValues) - newExclusions += m_exclusions; - // ggf. Min und Max tauschen: - for (auto &elem : newExclusions) - { - if (elem.first > elem.second) - { - int tmp = elem.first; - elem.first= elem.second; - elem.second = tmp; - } - } - // Sortieren und - 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; - } - } - // Falls doch - oder falls out of range - if (!retVal || newExclusions[0].first <= minimum() || newExclusions[newExclusions.size()-1].second >= maximum()) - { - // Falls nicht "Hinzufügen", Liste leeren. Sonst bleibt alles beim Alten. - if (!preserveCurrentValues) - m_exclusions.clear(); - return false; - } - // Alles in Butter: - m_exclusions = exclusionRanges; - - // Put that into validRanges... - 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()); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.h deleted file mode 100644 index 1ef11239c..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_spinbox.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @RDE_HEADER@ - * SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value - * $Id$ - * $HeadURL$ - * $Author$ - * - * Created by Malte Diers, NLSE, 2018-05-07 - */ - -#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; // Doubles the information. However, it saves calculation... - -}; - -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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.cpp deleted file mode 100644 index f78fd98ec..000000000 --- a/qt6-after/qt6-migrated-legatus/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); // after the splash has finished, wait dlgs are allowed -} - - -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; -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splash.h deleted file mode 100644 index b8cbbd0c7..000000000 --- a/qt6-after/qt6-migrated-legatus/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; -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.cpp deleted file mode 100644 index c0db93b94..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** -* @RDE_HEADER@ -* Splitter with sizes Proberty for Animation -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.h deleted file mode 100644 index 5e63e2c6a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter.h +++ /dev/null @@ -1,42 +0,0 @@ -/** -* @RDE_HEADER@ -* Splitter with sizes Proberty for Animation -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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); -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.cpp deleted file mode 100644 index b11a50abf..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/** -* @RDE_HEADER@ -* Splitter with sizes Proberty for Animation -* $Id$ -* $HeadURL$ -* $Author kemayou_s$ -*/ - -#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); - //gradient.setColorAt(1, m_endColor); - - 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(); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.h deleted file mode 100644 index 7811e1b70..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_splitter_handle.h +++ /dev/null @@ -1,44 +0,0 @@ -/** -* @RDE_HEADER@ -* Splitter with sizes Proberty for Animation -* $Id$ -* $HeadURL$ -* $Author kemayou_s$ -*/ - -#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; - -}; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.cpp deleted file mode 100644 index ab0b31044..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** -* @RDE_HEADER@ -* TabBar with special stylesheet settings -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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() // Do not use. -{ - 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) // as this does not happen automatically -{ - // tell all buttons if there are selected or not - if (newIndex == -1) - newIndex = currentIndex(); // TODO: is currentIndex already set when emitting currentChanged ?? - - 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; // no 'selected' on disabled! - - //qWarning() << "("<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); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.h deleted file mode 100644 index 21b563586..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_bar.h +++ /dev/null @@ -1,51 +0,0 @@ -/** -* @RDE_HEADER@ -* TabBar with special stylesheet settings -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#pragma once - -#include -#include "lts_widgets_export.h" - -namespace LtsWidgets -{ - -/** - * @brief The LtsTabBar class provides a QTabBar with the ability to be a master tab or a secondary tab inside a master tab. This allows tabs to be sorted - * into a group of a whole, which promotes better overview. - */ -class LTSWIDGETS_EXPORT LtsTabBar : public QTabBar -{ - Q_OBJECT - Q_PROPERTY(bool secondary MEMBER m_secondary) - -public: - /** - * @brief LtsTabBar creates a QTabBar - * @param isSecondary true if this is a secondary tab bar, false if this is a master tab bar - * @param width Debug value - do not use - * @param parent The parent. - */ - explicit LtsTabBar(bool isSecondary = false, QWidget *parent = nullptr); - - /** - * @brief Do not use. - */ - 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); // has to be called AFTER a new Close Button was added, to set his initial status (selected yes/no) - as this does not happen automatically - -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp deleted file mode 100644 index 5c205c0e4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @RDE_HEADER@ - * widget for creating a tab view of dds sequences - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Verena Eberlein , SPMO, 2023-08-03 - */ -#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); -} - - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h deleted file mode 100644 index 53ddc5365..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_sequence_widget.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @RDE_HEADER@ - * widget for creating a tab view of dds sequences - * $Id$ - * $HeadURL$ - * $Author$ - * Created by Verena Eberlein, SPMO, 2023-08-03 - */ -#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 // LTS_TAB_SEQUENCE_WIDGET_H diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.cpp deleted file mode 100644 index 3f5ac705a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.cpp +++ /dev/null @@ -1,907 +0,0 @@ -/** -* @RDE_HEADER@ -* TabWidget for new EXCON -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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 -#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); // also sets m_tabBar - - QFont currentFont = tabBar->font(); - - QFont::Capitalization c = QFont::SmallCaps; // Small Capitals make it look more Rheinmetall - 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); - - // Adding maximize button aside the tabbar - QSplitter *splitter = static_cast(parent); - if (splitter && - splitter->objectName() == QStringLiteral("mainSplitter") && - splitter->widget(1) == this && - !isSecondary) - { - int tabBarHeight = m_tabbar->height() * 1.33; // Luckily found this factor, no clue where that comes from - 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() -{ - /* no need to delete all widgets as they are parented to this TabWidget, so they get deleted anyways - BUT all tabs have to be removed, otherwise onRemoveTab (from the tabs) is going to be called on a dead 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; - } - - // the underlying tab widget might have a dialog open, which is automatically focused also - 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); // this is a sub tab widget? - 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; // is an LtsDialog, but is not closable, so cancel here - } - } - - 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; //The double delete might be suspended due to the delete later but safety first - default: - return; - } - } - else - { - //tab->deleteLater(); - closeRequestTabWidget(tab); - } - } - w->deleteLater(); // closing the widget would destroy the widget, which emits the signal 'destroyed' which removes the tab that widget was on - does not work here - //onRemoveTab(w); <= TODO: reactive if deleteLater active again - // NOTE: calling close on an LtsDialog removes it automatically from the tab widget -} - - -void LtsTabWidget::onCheckTabBarVisibility() // if this is called directly, the call comes from THIS TabWidget -{ - // The CALL is on the Main Tab when CALLER exists (Sub Tab calls) so, no need to check for hasSubTabs - - if (!m_isSecondary) // iam NOT the secondary, iam the FIRST - { - // i dont have any children anymore, hide me! - if (count() < 1) - setHidden(true); - } - else // IAM THE SECONDARY TAB WIDGET - { - if (count() < 1) - deleteLater(); // this will delete the secondary tab widget (me), but after this. my DESTROYED will call removeTab. after calling removeTab, this method is called on the main tab widget, which hides itself if empty - } - - /*/ - * also re-show close-buttons after they were hidden (only on main tab), - * but must now be visible again (such as there is no closable dialog anymore) - * but this case is very unlikely, because a non-closable tab can't be closed, but this CAN happen... so: impl! - /*/ - - - - // TODO: impl - - - - // if (m_hasSubTabs) - // { - // LtsTabBar *tabbar = m_tabbar; - - // bool showAgain = true; - // for (int i = 0; i < tabbar->count(); i++) - // { - // if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden()) // if something is still hidden, do not show again! - // { - // showAgain = false; - // break; - // } - // } - - // if (showAgain) - // m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->show(); - // } -} - - -void LtsTabWidget::setSubTabsVisible(bool visible) -{ - if (count()) - { - // TODO: warn the dev that this is not allowed after adding tabs! - return; - } - - m_hasSubTabs = visible; -} - - -void LtsTabWidget::addTab(const QString &menuGroup, const QString &identifier, QWidget *widget, - bool setClosable, bool focusNewTab) -{ - //qWarning() << "LtsTabWidget::addTab"; - setFocus(); - LtsTabWidget *tab = this; // keep this when m_hasSubTabs == false - QString fullName; - - // TODO: FIXME: rm! TESTING: - //widget->setParent(this); << after tests: interferes with deleted LtsTabWidget and to-be-deleted (and not delete-latered' by qt) - so onRemoveTab gets called on a dead LtsTabWidget - // END TESTING - - if (m_hasSubTabs) - { - fullName = identifier; // for the widget, not for us! - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); // get SUB tab, when existing.. wenn ein sub tab nicht existiert, existiert im main tab auch kein tab dafür.... also neuesn tab anlegen - - if (!tab) - { - //qWarning() << "LtsTabWidget::addTab: there is no sub tab - creating it!"; - tab = new LtsTabWidget(nullptr, true); // create new SUB tab..! - tab->setMovable(true); - addWidget(menuGroup, tab, true /* will be checked later! */, focusNewTab); - } - else - { - //qWarning() << "LtsTabWidget::addTab: there is already a tab!"; - tab->setHidden(false); // tab regests content, reshow (even when it's visible, that does not matter) - } - - if (focusNewTab) - setCurrentIndex(indexOf(tab)); - } - else // do not show & use sub tabs - { - fullName = menuGroup + QLatin1Char('.') + identifier; - // addWidget does that: m_tabMap.insert(fullName, widget); // by group + ident name - } - - setHidden(false); // as there will be content, show again! - - tab->addWidget(fullName, widget, setClosable, focusNewTab); // the 'tab' ptr is either THIS tab or ONE OF this tab's subtabs - - /*/ - * check if there are sub tabs that are not closable, - * but only if this tab is the main tab! (as sub tabs have no sub tabs, only the dialogs) - /*/ - if (m_hasSubTabs) - { - LtsTabBar *tabbar = tab->tabBar(); // sub tab - - for (int i = 0; i < tabbar->count(); i++) - { - // hide close btn of main tab if even ONE tab is not closable - otherwise the main tab could close a sub-tab which is not closable - if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden()) - { - /* main tab */ m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->hide(); - break; // one positive condition is enough to hide the main close-button - } - } - } -} - - -void LtsTabWidget::addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab) -{ - QString theName = fullName; - - if (fullName.isEmpty()) - { - // create random name....... - const QString possibleCharacters(QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); - const int randomStringLength = 24; - - QString randomString; - for (int i = 0; i < randomStringLength; i++) - { - int index = QRandomGenerator::global()->bounded(possibleCharacters.length()); - QChar nextChar = possibleCharacters.at(index); - randomString.append(nextChar); - } - - theName = randomString; - } - - widget->setObjectName(theName); // must match the string in the tabMap, so it can be deleted by the object's name - - LtsDialogHelper *helper = qobject_cast(widget); - if (helper) - helper->retranslate(); // properly set the window title - - QString title = widget->windowTitle(); - - if (title.isEmpty()) - { - if (helper) - title = QStringLiteral("retranslate() fehlt!"); // Da es sich heir um ein LtsDialog handelt, muss in der retranslate()-Methode des LtsDialog auch eine Uebersetzung des Titels stattfinden! - else - title = widget->objectName(); - } - - if (helper) - { - connect(helper->getDialog(), &LtsDialog::signalHide, this, static_cast(&LtsTabWidget::onRemoveTab), - static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); // hide and delete tab - //qWarning() << "addWidget: was a helper widget, added hide signal"; - } - - connect(widget, &QWidget::destroyed, this, static_cast(&LtsTabWidget::onRemoveTab), Qt::DirectConnection); // as it was deleted, there was DESTROYED called, so only a remove is needed - connect(widget, &QWidget::windowTitleChanged, this, &LtsTabWidget::slotWindowTitleChanged); - - if (helper) - { - m_tabMap.insert(theName, helper); - //qWarning() << "LtsTabWidget::addWidget: added" << theName << helper << "to tab map"; - } - else - { - m_tabMap.insert(theName, widget); // as this is a sub tab, save the pure widgets by their identifier - //qWarning() << "LtsTabWidget::addWidget: added" << theName << widget << "to tab map"; - } - - int newTabIndex = QTabWidget::addTab(widget, title); // finally add widget to a new tab on the sub tab widget - -#ifdef LTS_DEBUG - setTabToolTip(newTabIndex, tr("DEBUG: WindowName: %1").arg(theName)); -#endif - - if (focusNewTab) - setCurrentIndex(newTabIndex); // .. and focus it also (if desired)! - - LtsTabBar *tabbar = qobject_cast(tabBar()); - - if (tabbar) - { - // create close btn - CloseButton *closeBtn = new CloseButton(); - connect(closeBtn, &QPushButton::clicked, tabbar, &LtsTabBar::_internal_closeTab); // because the button does not know its index ... - 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(); - - // format: remove "(",")", prepend "Locked by: " - 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))); // svg -> icon -> pixmap -> scale - - tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, readOnly); - } else { - QWidget *readOnly = tabBar->tabButton(index, QTabBar::ButtonPosition::LeftSide); - if (readOnly) - { - // Qt docu: - // The tab bar will take ownership of the widget and so all widgets set - // here will be deleted by the tab bar when it is destroyed unless you - // separately reparent the widget *after* setting some other widget (or nullptr). - tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, nullptr); - readOnly->setParent(this); - delete readOnly; - } - } -} - - -QWidget *LtsTabWidget::getWidget(const QString &menuGroup, const QString &identifier) -{ - LtsTabWidget *tab = this; // keep this when m_hasSubTabs == false - QString fullName; - - if (m_hasSubTabs) - { - fullName = identifier; - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); - - if (tab == nullptr) - return nullptr; // not found! - } - 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; // keep this when m_hasSubTabs == false - QString fullName; - - if (m_hasSubTabs) - { - fullName = identifier; - tab = qobject_cast(m_tabMap.value(menuGroup, nullptr)); - - if (!tab) - { - //qWarning() << "LtsTabWidget::focusTab: tab for" << menuGroup << "not found! Cannot focus."; - return false; - } - setCurrentIndex(indexOf(tab)); // set focus on main tab (e.g. detail tab widget) - } - else // no sub tabs - fullName = menuGroup + QLatin1Char('.') + identifier; - - if (!tab) - return false; - - // start animation if splitter is collapsed - LtsSplitter *splitter = static_cast(parent()); - - // Norbert Thoden, 06.11.2024 - // lambda are sometimes ugly, like here: - // opApp crashes if dialog is closed/destroyed WHILE the 500msec. - // fix: use QPointer for tab - QTimer::singleShot(500, this, [splitter, tab] (){ - // Norbert Thoden, 06.11.2024 - // What does following if mean? really hard to maintain: - 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)); // set focus on sub tab (e.g. Exm => Edit) - 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; -} // LtsTabWidget::removeWidget(QWidget * widget) - - -void LtsTabWidget::onRemoveTab(QObject *object) -{ - if (object == nullptr) - { - qWarning() << "onRemoveTab: obj == nullptr"; - return; - } - - QWidget *w = static_cast(object); // destroyed widgets cannot be dynamically casted, but this slot is only called by widgets ! - - bool rmd = false; - for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i) // try to remove the tab that belongs to the widget - { - 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); // NOTE: calling indexOf with a dead widget - I might look into the source if it tries to afect the widget - if (index < 0) - { - // ignoring this message, as clearAll() is evil - qWarning() << "LtsTabWidget::onRemoveTab: cannot remove widget, as it was not found on the tab widget" << (void *)w; - } - else - { - removeTab(index); - } - - onCheckTabBarVisibility(); // check if this was the last tab and hide / delete me -} - - -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(); - - // Small Capitals make it look more Rheinmetall - 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() -{ - // FIXME: there is no other way to get all widgets in a tab widget (?), so this code must be used. But removing a tab, then deleting it, causes the widget to call "onRemoveTab" on signal DESTROYED, which then says that the widget was not found (as it was removed here before..) - - for (int i = 0; i < count(); i++) - { - QWidget *w = widget(i); - - if (w == nullptr) - continue; - - disconnect(w, &QWidget::destroyed, this, static_cast(&LtsTabWidget::onRemoveTab)); // otherwise call to already deleted LtsTabWidget - - w->deleteLater(); // queue deletion - } - - clear(); // remove (but don't delete) all tabs now. If not, destroyed signal is queued for a dead LtsTabWidget -} - - -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) - { - //qWarning() << "CHECKING DLG '"<windowTitle()<<"' / '"<objectName()<<"'"; - - if (dlg->hasUnsavedChanges()) - { - int dlgIndex = tabWidget->indexOf(helper); - if (dlgIndex != -1) - tabWidget->setCurrentIndex(dlgIndex); - - //dlg->closeRequest(); // tell the operator .. - return true; // .. and stop here! - } - } - } - - LtsTabWidget *tab = qobject_cast(w); // this is a sub tab widget? - - if (tab) - { - //qWarning() << "CHECKING NEXT TAB..."; - - bool _hasUnsavedChanges = hasUnsavedChanges(tab); // recursive - if (_hasUnsavedChanges) - { - int tabIndex = tabWidget->indexOf(tab); - if (tabIndex != -1) - tabWidget->setCurrentIndex(tabIndex); - - return true; // only abort if all tested and one of them has changes - } - } - - /*if (!dlg && !tab) - { - qWarning() << "This thing was a "<metaObject()->className()<<".. '"<objectName()<<"' / '"<windowTitle()<<"'"; - }*/ - } - - return false; // has no changes if it comes until here -} - - - - - -// Close Button: -void CloseButton::paintEvent(QPaintEvent *) -{ - QPainter p(this); - - p.save(); - p.setPen(QPen(QColor(m_fontColor), 1.5)); // line - p.setRenderHint(QPainter::Antialiasing); - - // V-Line - p.drawLine(line()); - - // X / cross - p.setPen(QPen(QColor(m_fontColor), 2.5)); // bold cross - - 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; // starts at the first half of the margin (see sizeHint) - 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; // add a quarter more space, for looks! -#ifdef TABLET_MODE - finalSize = finalSize*1.5; -#endif - return QSize(finalSize/* + fontHeightQuarter*/, finalSize); -} - - -void CloseButton::mousePressEvent(QMouseEvent *e) -{ - e->ignore(); - m_isPressed = true; // dunno if qt does this automatically -} - - -void CloseButton::mouseReleaseEvent(QMouseEvent *e) // only accept a click directly on the X / cross -{ - 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 /* hack */))); // be more tolerant - - if (!adjusted.contains(e->position().toPoint())) - { - //qWarning() << "NO! release - event:" << e->position() << "cross:" << cross(); - e->ignore(); - return; - } - - //qWarning() << "YES! release - event:" << e->position() << "cross:" << cross(); - e->accept(); - emit clicked(); -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.h deleted file mode 100644 index 3c378d917..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tab_widget.h +++ /dev/null @@ -1,244 +0,0 @@ -/** -* @RDE_HEADER@ -* TabWidget for new EXCON -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#pragma once - -#include "lts_base.h" -#include "lts_widgets_export.h" - -#include -#include -#include - - -namespace LtsWidgets -{ -class LtsDialogHelper; -class LtsTabBar; - -/** - * @brief The LtsTabWidget class provides a QTabWidget with enhanced functionality for the Lts Layout System. - * This TabWidget can either be a..: - * - * > Master Tab Widget: is the MenuGroup TabWidget (sums up all LtsDialogs which are in the same MenuGroup), contains Tabs to Secondary Identifier TabWidgets - * ..or.. - * > Secondary Tab Widget: is the Identifier TabWidget, contains all LtsDialogs - * - */ -class LTSWIDGETS_EXPORT LtsTabWidget : public QTabWidget -{ - Q_OBJECT -public: - /** - * @brief LtsTabWidget - * @param parent - * @param isSecondary true if this is a Secondary TabWidget, false if this is a Master TabWidget - */ - explicit LtsTabWidget(QWidget *parent = nullptr, bool isSecondary = false); // isSecondary var = last => easy for usage in ui file - ~LtsTabWidget(); - - /** - * @brief setActuatorTabStyle decorates the TabBar in a different way (once enabled, it cannot be disabled anymore). - * @param enable true to change TabBar decoration. false does nothing. - */ - void setActuatorTabStyle(bool enable); - - /** - * @brief clearAll removes all Widgets (usually LtsDialogs) from this TabWidget and deletes them. - */ - void clearAll(); - - - void aboutToClose(); - - /** - * @brief tabBar returns the used LtsTabBar - * @return - */ - LtsTabBar *tabBar(); - - /** - * @brief setTabBar sets the TabBar to an LtsTabBar - * @param tabbar the LtsTabBar to replace the current TabBar with. - */ - void setTabBar(LtsTabBar *tabbar); - - /** - * @brief focusTab focuses the LtsDialog, identified by the Menu and its Identifier, by setting the current Index to the requested LtsDialog's index. - * @param menuGroup the menu group of the widget - * @param identifier the identifier of the widget - * @return true if found & focused - false if not found (so not focused) - */ - bool focusTab(const QString &menuGroup, const QString &identifier); - - /** - * @brief setSubTabsVisible allows this TabWidget to have SubTabs, so that this TabWidget contains all Tabs refered as the MenuGroups, and the SubTabs - * refer to the Identifiers of the LtsDialogs. - * DO NOT call this AFTER adding tabs !! - * @param visible true if this TabWidget has SubTabs - false if not. - */ - void setSubTabsVisible(bool visible); - - /** - * @brief getWidget returns the Widget (LtsDialogHelper !!) refered by its MenuGroup and its Identifier from this TabWidget. - * @param menuGroup the menu group of the Widget. - * @param identifier the identifier of the Widget. - * @return the Widget if found, nullptr if not found. (LtsDialogHelper) - To get the dialog: ->getDialog() - */ - QWidget *getWidget(const QString &menuGroup, const QString &identifier); - - /** - * @brief hasUnsavedChanges checks if there are open Dialogs which have unsaved changes and tells the operator if there are - * unsaved changes by the "There are unsaved changes"-Dialog. This method only checks until one unsaved dialog was found. - * @param tabWidget The Tab Widget to check if it contains unsaved Dialogs. - * @return true if there are unsaved changes, false if there are no unsaved Dialogs. - */ - static bool hasUnsavedChanges(LtsTabWidget *tabWidget); - - - -signals: - /** - * @brief TODO: DOC ! ! signalCheckTabBarVisibility is emitted when it is necessary to check if this TabWidget can be hidden (e.g. when the last Widget - * on this TabWidget is closed, which makes this TabWidget useless and can be hidden). - */ - void signalCheckTabBarVisibility(); - - -public slots: - /** - Do not use. - - - * @brief addTab adds a Tab to this TabWidget (is called by the 'addTab'-signal of your LtsDialog) - * @param menuGroup the menu group in which this widget should be sorted into. - * @param identifier the identifier to check if 1. there already is a widget with this Identifier in this group (so this widget - * will not be added (because duplicate)), 2. to make this widget findable when calling e.g. getWidget. - * @param dialog helper (which contains the LtsDialog) OLD: the LtsDialog (LtsDialog, as its addTab-signal is automatically connected to this addTab-slot in this method) to - * be added to this TabWidget - * @param setClosable true if this new Tab / Widget should be able to be closed by the user via the Close-Button on the Tab itself ONLY! (does not - * prevent this new Tab to be closed by an Close-Button located on this Widget. - */ - void addTab(const QString &menuGroup, const QString &identifier, QWidget *widget, - bool setClosable = true, bool focusNewTab = true); - - /** - * @brief removeWidget removes a Tab from this TabWidget if it's under control of this TabWidget. - * @param widget Tab to remove from TabWidget - * @return In case of success true, in other case false - */ - bool removeWidget(QWidget * widget); - -private slots: - /** - * @brief onDeleteTab TODO! rm & free - * @param index - */ - //void onDeleteTab(int index); - - /** - TODO: update - * @brief onCloseTab removes the Widget (usually LtsDialogs), identified by its index, from this TabWidget and deletes them. - * @param index the index to identify the Widget to be removed - */ - //void onRemoveTab(int index); - - /** - TODO: update - * @brief onCloseTab removes the Widget (usually LtsDialogs), identified by its Pointer, from this TabWidget and deletes them. - * @param object the pointer to identify the Widget to be removed - */ - void onRemoveTab(QObject *object); - - /** - * @brief onCheckTabBarVisibility checks if this TabWidget can be hidden (e.g. when the last Widget - * on this TabWidget is closed, which makes this TabWidget useless and can be hidden). - */ - void onCheckTabBarVisibility(); - - /** - * @brief slotTabCloseRequested makes a close request of an LtsDialog, identified by its index, by calling the - * reject-method (see LtsDialog::reject). If the Widget on this index is not an LtsDialog, the close-method of the QWidget-class - * is called. - * @param index the index to identify the Widget to be closed - */ - void slotTabCloseRequested(int index); - - void slotCurrentChanged(int newIdx); - - void slotMaximizeButtonClicked(); - -private: - /** - * @brief addWidget adds a widget to this TabWidget (no matter if this is an Master-/SubTab) - is called by the addTab-method. ONLY ADD LTSDIALOG !! - * @param fullName MenuGroup + Identifier, as there is no need to differ between Master-/SubTab - * @param widget the widget to add - * @param setClosable true if this new Tab / Widget should be able to be closed by the user via the Close-Button on the Tab itself ONLY! (does not - * prevent this new Tab to be closed by an Close-Button located on this Widget. - */ - void addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab = true); // only called by addTab (means that the called class is a sub tab widget) - - /** - * @brief getWidget same as getWidget(MenuGroup, Identifier), but only for MasterTabs, as the Tabs are saved with the full name in the Master Tab (MenuGroup + Ident) - * @param fullName MenuGroup + Ident - * @return widget if found, nullptr if not - */ - QWidget *getWidget(const QString &fullName); - - /** - * @brief focusWidget only called by focusTab (means that the called class is a sub tab widget) - * @param fullName Menu + Ident - * @return true = found & focused, false = not found, not focused - */ - bool focusWidget(const QString &fullName); - - /** - * @brief slotWindowTitleChanged is called by an LtsDialog(Helper) to sync the Dialog's Title with the Title in the TabBar - * @param title the title which was set and has to be set in the TabBar - */ - void slotWindowTitleChanged(const QString &title); - - void closeRequestTabWidget(LtsTabWidget *tabWidget); - - QPoint startPos; - LtsTabBar *m_tabbar = nullptr; - QPalette m_palette; - - // when this tab contains a second tab.. - /* TODO: use LtsTranslatableIdentifier instead of QString */ QMap m_tabMap; // QWidget because a TabWidget can contain more TabWidgets (Main Tab contains Sub Tabs) and a TabWidget can contain LtsDialogs (Sub Tab contains Dialogs) - - // FIXME: difference ?????? - 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; -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp deleted file mode 100644 index 2bcb5f7d3..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/** -* @RDE_HEADER@ -* LtsTextEditDialog -* $Id$ -* $HeadURL$ -* $Author$ -* Created by Norbert Thoden, SLSE, 2023-06-14 -*/ - -#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 -} - -/////////////////////////////////////////////////////////////////////////////// -// PUBLIC - -void LtsTextEditDialog::retranslate() -{ - // in correct translation context - 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) -{ - // QTextEdit::textChanged is add via LtsDialog::addChangesMadeSignal() - // so setHtml will in turn call slotChangesMade() -#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); -} - -} // namespace LtsWidgets - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.h deleted file mode 100644 index b1fb9a232..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_edit_dialog.h +++ /dev/null @@ -1,61 +0,0 @@ -/** -* @RDE_HEADER@ -* LtsTextEditDialog -* $Id$ -* $HeadURL$ -* $Author$ -* Created by Norbert Thoden, SLSE, 2023-06-14 -*/ -#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(); - -}; - -} // namespace FileManagement - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.cpp deleted file mode 100644 index c34155fa2..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.cpp +++ /dev/null @@ -1,389 +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.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.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.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(int index) -{ - Q_UNUSED(index); - 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); - } - const QString f = m_ui->cbFonts->currentText(); - fmt.setFontFamilies({f}); - slotMergeFormatOnWordOrSelection(fmt); -} - - -void LtsTextEditor::slotTextSize(int index) -{ - Q_UNUSED(index); - const QString p = m_ui->cbFontSize->currentText(); - 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.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.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); - //[GUEZE-3151] Voreinstellung vom Text werden wenn text editor leer ist nicht greifen - // Mögliche Grund: je nach position des cursors auf dem Text wird die tools dynamisch eingestellt. - 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()); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.h deleted file mode 100644 index 3fb87d2f1..000000000 --- a/qt6-after/qt6-migrated-legatus/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(int index); - void slotTextSizeEditingFinished(); - void slotTextFamily(int index); - void slotTextItalic(); - void slotTextUnderline(); - void slotTextBold(); - //void slotTextStyle(int styleIndex); - void slotTextAlign(); -}; - -} // namespace LtsWidgets - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor.ui deleted file mode 100755 index dccc94a58..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.cpp deleted file mode 100644 index 9578a01f9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.cpp +++ /dev/null @@ -1,926 +0,0 @@ -/** -* @RDE_HEADER@ -* Widget to create Text Editor -* $Id$ -* $HeadURL$ -* $Author$ -*/ - - -#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 /*Qt::WindowStaysOnTopHint */); - 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() -{ - //delete m_ui; -} - -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); -} - -/*! - * \brief LtsTextEditor2::slotBackgroundColorChanged - * \param bgColor - */ -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(QKeyCombination(Qt::CTRL, Qt::Key_D)); - tb->addAction(actionExport); - - menu->addSeparator(); -#endif - - m_actionQuit = menu->addAction(tr("&Quit"), this, &QWidget::close); - m_actionQuit->setShortcut(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(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(QKeyCombination(Qt::CTRL, Qt::Key_BracketLeft)); - m_actionIndentLess->setPriority(QAction::LowPriority); - - // Make sure the alignLeft is always left of the alignRight - 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(QKeyCombination(Qt::CTRL, Qt::Key_K)); - m_actionToggleCheckState->setCheckable(true); - m_actionToggleCheckState->setPriority(QAction::LowPriority); - tb->addAction(m_actionToggleCheckState); - - // tb = addToolBar(tr("Format Actions")); - // tb->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); - // addToolBarBreak(Qt::TopToolBarArea); - // addToolBar(tb); - - 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(); - QString str = QString::fromUtf8(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) - //! [0] - 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))); - //! [0] -#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.setFontFamilies({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; // H1 to H6, or Standard - blockFmt.setHeadingLevel(headingLevel); - cursor.setBlockFormat(blockFmt); - - int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; // H1 to H6: +3 to -2 - 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(); - // See whether the line above is the list we want to move this item into, - // or whether we need a new list. - 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); -} - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.h deleted file mode 100644 index 510f3f2c7..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_text_editor2.h +++ /dev/null @@ -1,140 +0,0 @@ -/** -* @RDE_HEADER@ -* Widget to create Text Editor -* $Id$ -* $HeadURL$ -* $Author$ -*/ -#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; -}; - - } // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.cpp deleted file mode 100644 index 64bf21a9e..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/** -* @RDE_HEADER@ -* ToolBar wiht autohide on leave -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#include "lts_tool_bar.h" - -using namespace LtsWidgets; - -LtsToolBar::LtsToolBar(QWidget *parent) - : QToolBar(parent) -{ - setMaximumHeight(1); - setMinimumHeight(1); -} - -LtsToolBar::~LtsToolBar() -{ -} - -void LtsToolBar::enterEvent(QEnterEvent *e) -{ - setMaximumHeight(50); - setMinimumHeight(50); - QToolBar::enterEvent(e); -} - -void LtsToolBar::leaveEvent(QEvent *e) -{ - setMaximumHeight(1); - setMinimumHeight(1); - QToolBar::leaveEvent(e); -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.h deleted file mode 100644 index 5d785b021..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar.h +++ /dev/null @@ -1,32 +0,0 @@ -/** -* @RDE_HEADER@ -* ToolBar wiht autohide on leave -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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(QEnterEvent *e) override; - void leaveEvent(QEvent *e) override; - - -}; - -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp deleted file mode 100644 index f15ca7d7d..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/** -* @RDE_HEADER@ -* -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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 -#include - - -namespace LtsWidgets -{ -// -// ShortCut -// -void ShortCut::clear() -{ - name.clear(); - identifier.clear(); - icon = nullptr; - shortcut->disconnect(icon); -} - -// -// LtsToolBarArea -// -LtsToolBarArea::LtsToolBarArea(QWidget *parent) - : QWidget(parent) - , m_dummyLabel(nullptr) - , m_ui(new Ui::LtsToolBarArea) -{ - m_ui->setupUi(this); - - setAcceptDrops(true); - clearToolBarArea(); - - // Initialize shortcuts - 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; - // already added? -> ignore - 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; - // internal move but no idea where to move to - if (after == nullptr) - return; - m_buttonMap.removeAt(i); - foreignDragIcon = m_foreignButtonMap.at(i); - m_foreignButtonMap.removeAt(i); - m_ui->layToolbar->removeWidget(entry); - break; - } - } - - // find place where to insert the entry - 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; - } - } - - // create new LtsDragIcon if it's a new item, otherwise use entry - 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); - - // first is a spacer -> +1 - 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; - //create dummyLabel to help the operator - 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; -} - - -//protected - -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()); - // does not work when not dragged on a LtsDragIcon ... should be ok for now - LtsDragIcon *after = nullptr; - QWidget *w = childAt(event->position().toPoint()); - 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].toCombined()); - 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; - - // Clear Shortcut if it is on another key - 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) -{ - // Key in range? - if (fkey < Qt::Key_F5 || fkey > Qt::Key_F12) - return false; - - // Icon available? - LtsDragIcon *icon = nullptr; - for (LtsDragIcon *ic : std::as_const(m_buttonMap)) - { - if (ic->identifier() == action) - { - icon = ic; - break; - } - } - - if (!icon) - return false; - - // Take shortcut for key - ShortCut &shorty = m_functionKeys[fkey]; - - // Remove key symbol from any icon - for (LtsDragIcon *ic : std::as_const(m_buttonMap)) - if (ic->identifier() == shorty.identifier) - ic->removeModification(); - - // Clear Shortcut if it is on another key - for(auto val = m_functionKeys.begin(); val != m_functionKeys.end(); ++val) - { - ShortCut &itShortcut = val.value(); - if (itShortcut.identifier == action) - { - itShortcut.clear(); - break; - } - } - - // Apply new Shortcut - 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) -{ - // Clear Shortcut - 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 : std::as_const(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); - - // update m_currentIdentifierForContextMenue - m_currentIdentifierForContextMenue = icon->identifier(); - connect(contextMenu, &QObject::destroyed, this, [this](){ - // qCritical() << " contextMenu destroyed"; - m_currentIdentifierForContextMenue.clear(); - }); - - // 1: Remove - QAction *delAction = contextMenu->addAction(tr("Remove")); // QtDocu: addAction: QMenu takes ownership - connect(delAction, &QAction::triggered, this, [this, icon] () { - if (icon) - removeMenuAction(icon); - }); - - // 2: FKeys - QMenu *fkeyMenu = functionKeyMenu(contextMenu); - contextMenu->addMenu(fkeyMenu); // QtDocu: addMenu: QMenu does NOT take ownership - - contextMenu->popup(globalPos); -} - - -void LtsToolBarArea::saveSettings() -{ -// LtsUtils::OperatorSettings *settings = LtsUtils::OperatorSettings::getInstance(); - -// settings->setValue(QStringLiteral("FunctionBars"), QStringLiteral("Functions"), menuEntries()); - -// QMap fKeys = functionKeys(); -// QMap variantMap; -// for(auto it = fKeys.cbegin(); it != fKeys.cend(); ++it) -// variantMap[QString::number(it.key())] = it.value(); -// settings->setValue(QStringLiteral("FunctionKeys"), QStringLiteral("Functions"), variantMap); -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.h deleted file mode 100644 index 74a0c4beb..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.h +++ /dev/null @@ -1,120 +0,0 @@ -/** -* @RDE_HEADER@ -* ToolBarArea that accepts LtsDragIcons -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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(); -}; - -} //namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.ui b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_bar_area.ui deleted file mode 100755 index 4f0e14e84..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.cpp deleted file mode 100644 index 87f9acad4..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** -* @RDE_HEADER@ -* ToolBox for Menus -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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) -{ - - // for most widgets - QPoint globalPos = this->mapToGlobal(pos); - // for QAbstractScrollArea and derived classes you would use: - // QPoint globalPos = myWidget->viewport()->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); // TODO: use popup, use signals & slots - 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 - { - // nothing was chosen - } -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.h deleted file mode 100644 index fa8f73184..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_tool_box.h +++ /dev/null @@ -1,36 +0,0 @@ -/** -* @RDE_HEADER@ -* ToolBox for Menus -* $Id$ -* $HeadURL$ -* $Author$ -*/ - -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.cpp deleted file mode 100644 index cb5e3efa3..000000000 --- a/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_wait_dialog.h deleted file mode 100644 index 2572ca3a4..000000000 --- a/qt6-after/qt6-migrated-legatus/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; - -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_widgets_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_widgets_export.h deleted file mode 100644 index 42df63dd9..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/lts_widgets_export.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Export definitions - * $Id$ - * $HeadURL$ - * $Author$ - */ -#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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgets_export.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgets_export.h deleted file mode 100644 index b4938ef6a..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgets_export.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef LTSWIDGETS_EXPORT_H -#define LTSWIDGETS_EXPORT_H - -// Stub export header for BUILD_RTI=OFF case -// When BUILD_RTI=ON, this file is generated by CMake's GENERATE_EXPORT_HEADER - -#define LTSWIDGETS_EXPORT -#define LTSWIDGETS_NO_EXPORT - -#endif // LTSWIDGETS_EXPORT_H \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp deleted file mode 100644 index 1c1ce7053..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.cpp +++ /dev/null @@ -1,1201 +0,0 @@ -/** - * @RDE_HEADER@ - * Das Designer-Plugin für nglwidgets - * $Id$ - * $HeadURL$ - * $Author$ - */ - -#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")); // some dummy icon... - } - 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)); -} - -} // namespace -#include "ltswidgetsplugin.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.json b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.json deleted file mode 100755 index a2697da42..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/ltswidgetsplugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name" : "Lts Widgets", - "version" : "1.0.0", - "dependencies" : [] -} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.cpp deleted file mode 100644 index 7419a0905..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.cpp +++ /dev/null @@ -1,142 +0,0 @@ - - -#include "requirement_widget.h" - -#include -#include -#include - -namespace LtsWidgets -{ -UiRequirement::UiRequirement(QLabel* descriptionLabel, const std::function& requirementCheck, - const QString& userWarningMessage) - : m_descriptionLabel(descriptionLabel), m_requirementCheck(requirementCheck), m_lastResult(true), - m_userWarningMessage(userWarningMessage) -{ - retranslate(); -} - -void UiRequirement::reset() -{ - if (!isValid()) - return; - m_descriptionLabel->setText(m_originalText); - m_descriptionLabel->setToolTip(m_originalTooltip); -} - -void UiRequirement::setTexts() -{ - if (!isValid()) - return; - if (!m_lastResult) - { - m_descriptionLabel->setText( - QStringLiteral("\u2022 %1").arg(m_originalText)); - - if (!m_userWarningMessage.isEmpty()) - { - m_descriptionLabel->setCursor(Qt::WhatsThisCursor); - m_descriptionLabel->setToolTip(m_userWarningMessage); - } - } - else - { - if (m_originalText.startsWith(QStringLiteral("\u2022 "))) - return; - - m_descriptionLabel->setText(QStringLiteral("\u2022 %1").arg(m_originalText)); - m_descriptionLabel->setCursor(Qt::ArrowCursor); - m_descriptionLabel->setToolTip(m_originalTooltip); - } -} - -void UiRequirement::retranslate() -{ - if (!isValid()) - return; - - if (m_originalText.startsWith(QStringLiteral("\u2022 "))) - return; - - auto x = m_descriptionLabel->text(); - if (x.startsWith(QStringLiteral("\u2022 "))) - return; - - m_originalText = x; - - m_originalTooltip = m_descriptionLabel->toolTip(); - setTexts(); -} - -bool UiRequirement::checkRequirement() -{ - if (!isValid()) - return m_lastResult; - - m_lastResult = m_requirementCheck(); - - if (!m_descriptionLabel->isVisible() && !m_descriptionLabel->property("checkMe").toBool()) - { - return true; - } - - setTexts(); - - return m_lastResult; -} - -bool UiRequirementList::checkRequirements(QWidget* messageOnThisParent) -{ - bool result = true; - for (auto i = m_requirementList.begin(); i != m_requirementList.end(); ++i) - { - UiRequirement& r = (*i); - - if (!r.isValid()) - continue; - - if (!r.checkRequirement()) - { - result = false; - } - } - - if (!result && messageOnThisParent) - { - LtsWidgets::LtsMessageDialog::showMessage( - messageOnThisParent, - QApplication::translate("LtsWidgets::LtsDialog", - "There are elements that have invalid or no value.
" - "Please change values in the elements which are marked with red text."), - QApplication::translate("LtsWidgets::LtsDialog", "Operation not complete")); - } - - return result; -} - -void UiRequirementList::addRequirement(const UiRequirement& uiRequirement) -{ - m_requirementList.append(uiRequirement); -} - -void UiRequirementList::clear() -{ - for (auto i = m_requirementList.begin(); i != m_requirementList.end(); ++i) - { - UiRequirement& uir = *i; - uir.reset(); - } - - m_requirementList.clear(); -} - -void UiRequirementList::retranslate() -{ - for (auto i = m_requirementList.begin(); i != m_requirementList.end(); ++i) - { - UiRequirement& r = (*i); - - r.retranslate(); - } -} -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.h deleted file mode 100644 index 26259bf3f..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/requirement_widget.h +++ /dev/null @@ -1,62 +0,0 @@ - - -#pragma once - -#include "lts_widgets_export.h" - -#include -#include -#include - -class QLabel; - -namespace LtsWidgets -{ - -class LTSWIDGETS_EXPORT UiRequirement -{ - public: - explicit UiRequirement() = default; - - UiRequirement(QLabel* descriptionLabel, const std::function& requirementCheck, - const QString& userWarningMessage = QString()); - - void reset(); - - bool checkRequirement(); - - bool isValid() const - { - return m_descriptionLabel != nullptr; - } - - void retranslate(); - - private: - void setTexts(); - - QLabel* m_descriptionLabel; - std::function m_requirementCheck; - QString m_originalText; - QString m_originalTooltip; - bool m_lastResult; - QString m_userWarningMessage; -}; - -class LTSWIDGETS_EXPORT UiRequirementList -{ - public: - UiRequirementList() = default; - - bool checkRequirements(QWidget* messageOnThisParent = nullptr); - - void addRequirement(const UiRequirement& uiRequirement); - - void clear(); - - void retranslate(); - - private: - QVector m_requirementList; -}; -} // namespace LtsWidgets diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/DMF.wav b/qt6-after/qt6-migrated-legatus/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 z)*h}rw5Q3Lb|qQ1&&ySjhDuSSq;k#PCa<;|$<2{t@&dH8G9Q{pZeU%bQ?Q%CGT19$ zdF+gL8}{1U8gJxlKs@zrAvgK+(GU$l@Aki=xB7C^pFIY7JZChKn)MCK$}E9(%$$Yo z%3$%-^p^O7^cnb#bbznO_)Toc)JP$_EWO6=Z-{?k^OLrJ5r#d4XoXzI zcOiRW;^rpN0Dlh2U^&b_;xFzC-jNrHe8NyNkLaOxh|QRyVilGa*0G6vG0x&{^80v? z_=>OW?7|m|Gq{QIWqBc?A74MA4BsN*Z%#~@!Oo6<%3O+j&HU@C#a3~iV%thiE>0}N z`Gf~-qUd5vh)tLh;vjmlIEWe{%^)W`e8j&FnW*G2h(u`!!HebbUczZKuYe+c{uG$Y zueJ7Yqs)=)OUTwL2aR%*EejIQ{)GFio@HTRJ}#e3MWu!Kn#2h;n- z+|+Wh2l=14k9;d$CU=NI9DW!2LtKNbk;bL^qf`h^vXef6e@!Wg#6x))x%l@PY zZUZxtJIT6ui9gFv79R5_g|d7peiXNh8OZg6%lS6sRK5qpJ$8I-926^?>;9L^fZ;;cq@x@ z{rQCye=;}N|2JF3--W&C-_Mo}JY^pQqU^QcZT49xl_?f3&zuZxVD<#du%iR#m~Q^f z^c7!}y6?-TCi!PmLNG?u4rBO&2!pkZ%*M>{9NZDjz|&&m@#FGM43kS^d1LV2EY{0j zF7Jg~yWM88Q54cQ4P!L8W3C5F!Cy8C^GmIf47l59N%ck?>{;XsFQNm)%IHf0 z?mA>1cC|EruGzLwe7ytT4=c#md|@m9-R<$aJ5_BPKb?BAYVE3hGbXW&xC z*+9drGl975m4TeB$ALiRgW!XVP-s@hrtp!>ap7{=V)%3R`OvbQQlVm=7s12cHla&i zZ@90gcI;Tr-^z?^TDzA0RGaSpLoezx^^8D0b6#k1cV>UWHpUTm^r!e7Ed}$dFVHwG zKXOAW2#Ix3v!qedOxF#)r(Qr$)bDFi=mIFBjnYOav$WLM-;h#SMjI2em3gspv8S=u z!Rhi2?|Nm7J4e0Yo}-WU);4GR23v`~%GM*#a`Ujen%N=ev++k(expssI(N3ha;(&ZRI6?HzRio4?J{BgzUw78E{t@tKX+xQ9O%=m-Ew0MN57~c^0#>HWi z;~pXfT~35_e6ZgOBkT}23#?^VTkjZ;*^SZ7_w);EJWbj+s7H1e@{;`$Z(u7J3G>YP zY?7F7pMd-R6~sKG6CMxKHVT|aj)0yilSs7E$W7)ja*GPucZf&kHY1tz2PK!cvxWCgl{s_ z!*T4R@M^YZq%G%(eBq8n6Zx019=u<<%)ix5ez66w9&C+oi4@}BAQ|jdbQg05Yfcv? zYEhYRZoZekMxJ4clO@>W#0jo{9>+=ylRycAcXeSJnZTff7kSlHh#L*?<`p*N7Vzp90@MOK@EhZ?QddZQ#b` zDa?oSqWqhD-T2}8dkd%Xmk{^ozbH=4Uq=%1ualfwH@PKO~qEudwc;$b-tdX0bkQuif`b2!tHe2WT!d~&?O!F$*R&af|Fk3=NwP5 zWY-?_mdk1wh#Ps!w8#}c?Jk*X{euFIAQ?PXn|4(!oT z0=qWUlYJ3LVV?L7(*Jl@P-VQ;NZ?7u-+9*I^*p!n)9y?h^}NOxd+Om2JYBFh-c0nG zuRRtYxQ<2q|DpAK1MFFzu~v?|tJT<(7X&>#*zX-?b@UathWcArnSps07TRcS4K=mO zg?E^9!h_5}D9t<<++e*Atg|Wy`&bV{bFF^S7gmb$0t`2fL-)l^^a*%?j)a!2j*udd zjG}l^xMQeHenSQ_#gLgig7^fV{f~ISe(ZpJ>$nSGZbCtDF5!?hBB6{mKB0nDI&qiv zIZ*(86B~ox32-u(Fa(TB*aW1+$6!U`Nw6xB0@1`nmM>|L)i~E+^Lo-feQP4Cor|ZG z#V)9)aHdCcJFZ05NoOLq7#A5O6bR2}lS8+uJmLQ2#_(ifWq2iKh6dW%!Aj5q&joS^fB3V4o&D!S*}kS>hp$w4y=PvifIAXAnNucI(j6DZ zJh#IiJyImg`)B00Z+oPipNRhBYZN1V3CbAnOm(EUlIHgQrS0);*V_4O>!19(;r3qv zNMI`ZPoN9_cd$G0D|nD-39r7!gkiorGLQs_Bg4pj>rg=~}DFe~?pcm@s=|JZl%gGh7OeY%29 zMkivU(Rdud4C)GG96A6tsCOfiVWMY(6^FSYi72o61B)}>;Td`t;-E1ZhvZ3Yj9C*6 znQPG8)^6;#@dv(0Q?bRefSrkqL7T$~Qd;;U=8F!)BQlBeDh=rqE_eEBw|j8oG$+fyGEh;Jy7UxY)iLx@8v)zp)F42ipt7arT?YHoHb_ zC-OOV0R2O*i%nMQVQsa?XkqBc91dH96|Kj}UaJ)ngL}-UaA#T68VgyDkBot^U9`ts z13BLLtjQp+bqD-sUIkChrjQ?$%Q|SSGK+wLum{@|V4x(JW{rb%p}f{@^Ru-YlEvqm zNodr3j544*JbO3bb09hHoV|?v40FGq>|rohUy~XG`q8tj;!KVS=zC@=b;}$;Zh`5w zm&R`VfUe?M+8)B8uO_7{)$U_F zMMK(A?3ub4d#nl|eQ*LB0laGPdG0Q3!dueTno&}#G)e!;COD2Y&U^BuC@b}>r zxFga9uO4ZPkB?l!mqrE?t0FRSH&T{d7imqN4%1|z@FwC^cpp(B+LhcA6RCA_J}S2| zo4l@M5NA}HtPCC2Ta2z`ZSxf|6B4L`=6;yYJW2EiMPYh#Hnh}YOn%}$Q;;&5we-JC zZDu1go_Wpi>9$135oLlHt!3u%;8&ZCs6*`$2p$E2Mi zpUaTATzi}wl9sx5B`$R(CLD3Cj4K|u%T+PX;i6oOD%4}?~3mq*FCPVtDvidv#v9jV}oO=xDn3P`%8KF6^=#RQs*ePwlhROcjTwHIy0!> zuELb*`awK)9>O`t9_(LnC)!`ChBS9J1SMQw%_+{}=6*+gqpc%Fd*)c9j(5&iG)In{ z$MHhGETzl4q>1uiNsU&NYDQ*?8KDkBrO;%4UuZ9%5LqQ`i?$Z0#WIB-vDQ2(Z)S_g z7+Xkgzy@PWm`$+(RIk_{#O&B>T#KD02Ftz3@p2Kelstvl8QYI{jd6HsIT^dJyg(PL z1CiNkXS2+7oVzcA2lL?GmBo(~(=l9VgX#(sd<8{36ek zy2|Gq3G#PmC3(B+r~KTtLD}fKt(DPx_*)NJXOI$Wx)RhK@g|B202PQ0uP z=l$|$=0>+S15;*Ik^@wD`R$)N+qvX=((W^D<4$vhWWpG5^r z=WGkka=!~wo;C0>CV0ZVEOQ8+i z^$Py`#zKFxDF(V(EdxKTPJwd34ip4pXg7Egs)kGt_rdN&dJ$V<56SAvIx0c^Mb%Zm zQ)AVQ)O_t8`Po=WECiSEiO3VY8@ht1kDeqWFwfZ=6R~O(Ud+9J#Qz?g={yg05iwx zPmZ?!z~@@cux60XLz;Qu8B-H+smJZJaw}wAY!uo#_7<%pS44*?P9$0R4czd)Fe{d2 z){k|xPDW3Fj!_nw6ctb->coD;e|r0Nlo6)*yU|wI0uH zt${?>Dfn^=whpb<_+RjImra})YTkhqzWwAw(3$Fkz!e>8XkZl%WN{}6xIO-(y z{A{FC>9cfqW+*+BA?WkWEb23RnY_JEu1H*}LGg#^W6=X_q6`BzojCT!hCb~v%62;~I z#7HF*µ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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 zmf}^%ULnyh*?-+V|0M$x*!jFU`9A!QhdGwC|C2V{M^5fm?xjipqM4)lg8!ynqv48m zzvO`W$^Go*nm+vXzGBICY)liS4CZX%t8I}5*vvw0BC#18tmFr-MO?&*g?nS5!;TyF^WG2VZ~ANY)Z&BXh~C{^j6Ns>O=Xi3Uui@Vt_>-DGMyaW4pKpH&U7p9v z%upgDpSxx%UdOse95{(mN?&J zsi)5RP`|Ypp|xv!LF&HvIEb}5z!Qez>M*FkpF8@vqbsg9v)u!7$Me_^It<6%!|29H z5_JG=AL{zO*w4L0{CP9EyQg#`5#0(2X|4Xe!F-^)4)@Fv@P9B1)`JW-QFE+Ab8T5d$q$Hv|fFIbOC)5*{ zI;po4+t9%?I*_4uY(&YY&ALd-?JvdM`Q+?dvN*f&3D&_WUjQaM3t|5X=hw3R8*$%v zm~pojE!n{{*`{pr|3+H8(%)Kgyppf5p}1!WiCLiyE6D0{vYI^0JQi{`&7Z1#!Kau@ z`n_4Awjf{fD+^)vVo#a}EAkHbH{@-$9%KgQSCH*9y|Wsmp5@J~1N3Gfs_abzeE*Kz z%`EWQTRCL&D;%FpE}@GHm-*9qA7-2F1|^7`@3B zwCMo)f3R{4jXjE99SaBYC8LqB;0QW=9B=Ft+I6;ap8YfF)amwfEN|SODGzYA2>*Qk zU*;#C?)NAN@jd5{Rp+subV5!VE>i{cd&{HsCjx~LKj%J1!Ld`2=WN#$~$%~Gvc%EqoC$(i39J~Pj<7SoqxVVWAg#L3wt&}?(kl=;Tf$=xTgJtM8j;LjvM-xg&B&6~?Ro58|N zC5yrTSvY{AOH;!M!4jQCH+VXIBjt9tfp)ZTzT<-0}gNkM0l*G`MGns-yZ%h#_O zLfZQ9u!a3L`q)j+lMUa~HGAvtUV6~qZ(sFxJTc%-UTh=WuUBNx2fKbB`k9f6Sh@bn zK+nv3jYDCL|$(Y(=$= z`nFo1R^!78zt^MeI^2n`xXtffcvIUQc|)uM|LPW3SK{LeJzPV+SF(O9^gQ~v5)vd^ zxq?K@M3v9=>`gs=9S>iE$1n39UM;XXtEP>B-6uer;~~mvuKAt_(Yz$~A!|~Mh2eQ? z>m`zwoc_anx%)}zqwK|4xt;f2`>8C|R9T0wcpp>vU0GLpvg4CPu%C)Rvzp5nY|j)K zs~N7D+PAr|g@>W!Au#74&)J8a=&!wdz@i{z3w~ZR zb+y#T*2Q*)KF`$m={P+dzNbu6Z`KB$j}}Xv%PgGrWOu8w#eN++RgNRgZJV;At=Y{E zIMV{XTNhVVlckNGwi9w>c3}8;M)eSokxD=l9O-LKSvK1klSRQ))pF=bs|hmTT6?AS#|Lw+-zD{g)-<^2mU2Xmw0$5>EA^sx3T!uZ0I)h-iaUKUoBW$3zk;E z{NSlIlldfWeDaT7VW+I%qILQrq~u>IMT z1KF0r{JTM}9bns!G_fPKi^-h8?J_;!O_Ilx{#%x-G!Y}+k$tdXsrc16?zO6z6+pd(n%G8TNDUK)<7#2k_m8kkUjH83E~R+l;hFTI)Jj8}3x& z?{;MiN!khHYr&t$N^O!~rp_(SZXz)&`R~iw`*>xQ%G%=Sc1JTur~-Zl1KXo}OEqW4 za^^-1@T9&_Z=l-x=!w;T(Xz(5owR>zf%O%RH&9}k5@}?Wr;PsQO)YDAy0!`>Hba8a zsvK=?eY=Wo=W7uwT)T!G1vQuJVZN1}*)U7pw_1BvI<_A2tamIppSXOBW9FG_Lo1~L zxoM*0y_V+M*9@hDEY0w|G5+WKpLrv!hW6IbuNvM>tpexQq0~zIi)mRhqFIMzWl^$v zc4N7-~c$1Cz`n7qk`Q|`NUT5O#c%VHU>u$RzJJClO;Qn5`-F|FF zFNoA1CMUbuzla4tci(5qr#SNkoAjl+=8%^~C=~2XhJ1(TZdLOB#9DPMgOstb)9g>t z#?RcBEdB?snWTO1s^bgyeTG}#DznwUlx1D)o~2}VnX(iDXEymJ?bxA@rIJ-6n|V$X z+a^$;B_6aSCmD@x#z$&FmKxKv7IZ0ZKX)v&Z7*jALXtr=YB0^qY}g_0H~=3HbnPMT zIY5cuHc;8iz5VH9H(X9s*H|l>>S?ki$!awDAC@&KsE{?**5Sw|T-<^?)i_v#b9G2y z#!1U;tLRV}NvvH+L*7Bk%46$E!A8=OIA>#VZPr^~4%3sh${5{T|IbrL@_S#}&s>+h z_d7#<(`nos5;0Hh$^R@-mU^mhzLKp*+T8?K>Z4qIki5^h)^nEGuF%evYFO#|jEZLd z&?<74HGGz7N#=qiQ@o+rRu*R$+n=v2cQje9HSUgdu2o_WHrS>eJC!F%PtbcNHR&sXDV3IQoBE<&pM}G&w=|M2yK>WyP%dzzE z1bR4%9-aUfkG37@_aS&T3@`Vy-4Ac~!N)=N_jdgN5)!%SV?P+%o%Hl6Bx;jwe7WT0 z%JF*_4T(<|ov%<^L!}-msUSUd$wJQWDDbB>DNW|9l|D4Z)uynmu45Hwov5Wb9<+xi zo$;eP`RGcTyO4xr#}iF;@w{GerHf~D(!Tg8DGi;AWLLPqLOUy5Q`h;r`ch9{)3OHo zT}F=cF6#QiQ%xqZhyCQ^gK&LNuX~|S8ReK}f2!-IY0(_5pQF|DpwN6WxC955;l%1f zPHTC3EiK+nma@N{B=07*HF#QE3+ie^=A2~wwI0h=TWdtV8aStV<0VN-L zv!~W5^<5kEjNR(!-rhh0&6Xx<=K57GMrwBbOtB{E9PG7x2ZDqUef zB7nS$+X^LHx~BmyWd+0yJerjxJ>UIIWE*K;3|SXDp7$!3>hUTiHewZ7%-b-lZP$_0 z4Unn|8g0k7ZQ8#}*`iOIly#n*xMB_JZqUC?uGy>=rFmmbJ-s1GNvSPjtF32g=T_9H zRLfd5WOek#erM*vVoxbqq6k}yf)9&HQTUR4#w;B8Mwx;a$^3t)XOs2p zUE2@*en+p~L5H`|BU!Uao|CNc*YI;1$@V33Jnzfa_f=m98rcQ!TNWBRU;Xo8R@T^A zK5@xtiJ^cY=bpx?I-51bmw9;UW(oeY?rY6^PHXQN^hz| zseI3>nr%zAps~KTMD4a@wFBD3+GfnN8A{i-t%IUF+*PCI9qQhp#;u;SLkp@rce|RZ zT$yq3()ea;^v@e7;a7aZK57_*@`F8P7|I7jGE;l6f_KXc9(Euf?eMUXYlD#$^sT<9 z*TeVN^vvbiiq<<6E023-O0{#loXa-XB);D1nqYS|4BW1rzDw#@{^HXn(~+@(ZO&{h z+A>Z^G&98sI6j&~@w8ABVS^r!3n9PbSSyZXN; zG>?_<KDWvD%vW>29C@5CSFl8=X-t6C^Pl-u( z>3xaviPSUKF7tewqH1%$o2e@l&$wiJ65SRxJE3mYzw1b&^DbB`ve(G<@gb4}h__T% zn;L6Ru%nG>!uro>j-Q!rgjmZm3;NJgqS+lXf;F zUDJJbxPvld)3V|BE!LabDaL?ky~ zhkV51SFr94ZJV&T&75mNmYVBDtKzSNezfw-cT~w>6K9*)v%;^xX6{essgY*{DH@Go0+eTwb zQOXiXwvYxbvQ0j8F{zqMqB3^2wAi*Pq-;6Qsgwt4iG#jwK!U1Vvk4-ubI)4Ojx@&3 z%y)c2feFoNUQ=zT>s;o>lxc6?)=z298*9XFG}8WzG?%+~7x^jS+bU9)9PcXfwzBX` zza@3?Pv?=OSb!xkJL8{uLoadRT7N;eO$E-yJFfD#(KaQryWN?5iz3odx1e&pg5s@F zJ$_ev{BBJmI^tx!o;_UC#kDdL!W8~@k#c(rN$8m-@~#E;mi*ISByY|!^D&c-vYvcJ}S`5ycl*CmUx zoK!DT=X`a~^*gIUWgK`Gi}tnO)@D?;(!(m(#L{lIt@ezJqU}^$YOnrZeVZMRb&1v3 ztcG}m!Q!AzJdfmWvu?>kC^OUVS@@gLiOdvTP@r$9(Gx0_yw2?qD*~guIakQ73dblsMO1inXr{CS|cczs+=wCFnhq^iz#TQ(PGaa>| zle^lep^5+FPu3~6t<_PQ&6N?roVQ+&dotsqxhH2NHsfclv@|n?GGfrNxWB326@>@c z0aaQSc)k@?lShbuu!%M#FSQLq#V=2ZpBdx{62wkyf(r2?lEbQ2g4G#?&RW8CooP_i z5ER|1W-AKWCd-wX!}UD1!m%2%lmEBlOCrMPMP}P(8xLc%<7-{B-gbrUD*Vct->XSR z_E&piWHmErmaBE8wk^^A#kLu(O~zop-_ibsbR>Eb1c^tm+Ws=<;u9|*C7Fw`g#B7b zswyEzM)`C7I_EMQcpaL=&rU`t-dg6=h7Majw?vy-w()_|haJxEB*mH4Q`@sjGt!b1 zU+rwZOBt==t5)@z|%#%!}QsP;zDLWo$ye z)0EkK_w4jLavra=G-{if zTkCNxGcz)h>l=e8n>e;U9jm~rM1iGvHXd5NqQqk;p?}FcDx)8}-I1}zdd@ZUmv}RE z)W!F__fXsabx@$5x*K^`Mz12DiDp}p>*OCAk>$)vDOYM>Z8hB8#6AUkHx&NTVw$y3 zS)n99?dLg_D6vLarSI|AR+9I%g^cg? zlzdY%zFcHIa$ly@XhYhwmEGJWGrv_1X1i^*XIMqIuq7MRm6;ox{KdW`BeM!lFSgCf zK$SEmW30&^u2pN+s!HZ@tEX(z>&%f(p1&mj@mCV9WXvf!OCRfWrVhGA(ld%!!mx04 z6B?FctoVYZs4QO#*o~gisB*|$AD%XZnN97tz@^Mj$Tr#R>}8u-3-QEbF~Yw@$oXqj z@HEkS_H(wev!!e675fcvx4!2@zvJCy#fw~o)yQ+}qs@1SQ5vNVw(O=!Wv;E`d?^o- ztAj#yi@W0KW^BBXZB_(pX4?#fgG?<@IB%FGvlCx6-gHyP8>_4I21$jz^fr0#+J#(a zoNz1phoc*9OFXHorW`BB$>?0hc!RM!$w}VMNL-f8O`_;Jo)+IK_?dB**q3l$?C>O{G|*6hp{fya5OmcPofctvw~iUeY1_V z59LapK&adZ1w*L@TA$b@+LTt;cP^enY)ICWD`oX^E>UBm%S4$~O3ugAC>@KR5UOPU zY{|}4728lP_9o|=7T0IYb7OH|!$M;dStfFe$CmaaSCCPLcyXBp9z4jg9r_lV7hb0y zk%HK|Wcf1c5$j!}j@ZJ|x}muy+8F9*^gUV~hZrd&z1gme228k!|uynFCd7PkJ25 z4Q0~fP$||o(wClB<6dw*+q5>@+!Kr_aXkC+(Ms25o@jp;$On+cwuFlU%wZd!@g~N%l(eQo_{8 zR;~`-mb|^_b?%A$sL}cL}GnE#)lJ&qzTsb-5>!A6}-trQYXk?y7by^_J?& z*+_SuooD8*?{Kr^Zv{otlF%`2%iW=O&?4GaLXFg%{nGgo?Mw3EBG;$KX?c2`ZRuR@ zNShNml%5lL3E%RR(l+f2MbhFD|0DI`PN_$sNXf$46#w$PQeV>I^fNVuPNjR&i|ABo zTSAZYJQ7{1E7ymDB@U)H`4t|OWGMYlT_ui{+LiW|s91VZnv!;ubRfr4XX?)rbN0J! z3CB`@srMzs`|g<~J^L;iN}aj)JFeze$)gMHgX3vc`cdjv>FK5a(}En$Hg%+y@Zr05 imX7A`v?LS`&V+NZAmMn4_y2z#X-BCwPWo3~_WuCk@n;kO diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/European-Union-Flag.png b/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/US-Flag.png b/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack.svg b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack.svg deleted file mode 100755 index 79b40b4ac..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack_dark.svg b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack_dark.svg deleted file mode 100755 index 8739fc398..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/ack_dark.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/beep.wav b/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close.svg b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close.svg deleted file mode 100755 index 71d8a3065..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close_dark.svg b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close_dark.svg deleted file mode 100755 index cc00a5706..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/close_dark.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/effectarea-enter.wav b/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/entryNew.wav b/qt6-after/qt6-migrated-legatus/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/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/info.svg b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/info.svg deleted file mode 100755 index 4657b2788..000000000 --- a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/info.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js b/qt6-after/qt6-migrated-legatus/Lts/Common/LtsWidgets/resources/jquery-3.1.0.min.js deleted file mode 100755 index f6a6a99e6..000000000 --- a/qt6-after/qt6-migrated-legatus/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("