Compare commits
5 Commits
60706e5e52
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 911f169b5e | |||
| 884e8e903e | |||
|
|
0b54793b08 | ||
|
|
b05180f575 | ||
|
|
c3e5092845 |
@@ -1,6 +1,5 @@
|
|||||||
QT += core gui svg widgets
|
QT += core gui svg widgets
|
||||||
|
|
||||||
|
|
||||||
CONFIG += c++23
|
CONFIG += c++23
|
||||||
|
|
||||||
# You can make your code fail to compile if it uses deprecated APIs.
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
@@ -9,34 +8,6 @@ CONFIG += c++23
|
|||||||
|
|
||||||
INCLUDEPATH += . libwin
|
INCLUDEPATH += . libwin
|
||||||
|
|
||||||
linux:contains(QT_ARCH, arm.*)
|
|
||||||
{
|
|
||||||
message("Konfiguration für Raspberry Pi (ARM) erkannt.")
|
|
||||||
|
|
||||||
# 1. Header-Dateien (z.B. für bcm2835.h oder eigene Treiber)
|
|
||||||
#INCLUDEPATH += /usr/local/include \
|
|
||||||
# /home/pi/my_custom_drivers/include
|
|
||||||
|
|
||||||
# not used at the moment
|
|
||||||
# 2. Bibliotheken linken
|
|
||||||
# -L sagt dem Linker WO er suchen soll
|
|
||||||
# -l sagt dem Linker WAS er nehmen soll (z.B. libwiringPi.so -> -lwiringPi)
|
|
||||||
#LIBS += -L/usr/lib \
|
|
||||||
# -lmhstcan
|
|
||||||
|
|
||||||
|
|
||||||
# Optional: Spezielle Compiler-Flags für den Pi (Optimierung)
|
|
||||||
#QMAKE_CXXFLAGS += -O3
|
|
||||||
}
|
|
||||||
|
|
||||||
li
|
|
||||||
|
|
||||||
windows
|
|
||||||
{
|
|
||||||
#LIBS += -L$$PWD/can_api -lmhstcan -lAdvapi32
|
|
||||||
message("Konfiguration für Windows.")
|
|
||||||
}
|
|
||||||
|
|
||||||
# You can make your code fail to compile if it uses deprecated APIs.
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
# In order to do so, uncomment the following line.
|
# In order to do so, uncomment the following line.
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
@@ -48,7 +19,7 @@ SOURCES += \
|
|||||||
bcdriver.cpp \
|
bcdriver.cpp \
|
||||||
bcdriverstatewidget.cpp \
|
bcdriverstatewidget.cpp \
|
||||||
bcdrivertinycan.cpp \
|
bcdrivertinycan.cpp \
|
||||||
bcthemeswitchbutton.cpp \
|
bcthemebutton.cpp \
|
||||||
bctoggleswitch.cpp \
|
bctoggleswitch.cpp \
|
||||||
bctransmitter.cpp \
|
bctransmitter.cpp \
|
||||||
bcvalue.cpp \
|
bcvalue.cpp \
|
||||||
@@ -69,7 +40,7 @@ HEADERS += \
|
|||||||
bcdriverstatewidget.h \
|
bcdriverstatewidget.h \
|
||||||
bcdrivertinycan.h \
|
bcdrivertinycan.h \
|
||||||
bcmainwindow.h \
|
bcmainwindow.h \
|
||||||
bcthemeswitchbutton.h \
|
bcthemebutton.h \
|
||||||
bctoggleswitch.h \
|
bctoggleswitch.h \
|
||||||
bctransmitter.h \
|
bctransmitter.h \
|
||||||
bcvalue.h \
|
bcvalue.h \
|
||||||
|
|||||||
227
CMakeLists.txt
227
CMakeLists.txt
@@ -1,138 +1,137 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(BionxControl VERSION 1.0 LANGUAGES C CXX)
|
||||||
# Projektname und unterstützte Sprachen (C++ für Qt, C für die Treiber)
|
# --- C++23 aktivieren ---
|
||||||
project(BionxControl LANGUAGES CXX C)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 1. C++ Standard & Projekt-Einstellungen
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Du hast C++23 angefordert. Hinweis: Der Compiler muss sehr aktuell sein (GCC 13+ / MSVC 2022)
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
# set(CMAKE_CXX_EXTENSIONS OFF) # Optional: Auf OFF setzen, wenn du reines C++23 statt GNU++23 erzwingen willst
|
||||||
|
|
||||||
# Automatische Erstellung von MOC, UIC und RCC aktivieren
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
find_package(Qt6 REQUIRED COMPONENTS Gui Svg Widgets)
|
||||||
# 2. Qt-Pakete laden
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Äquivalent zu: QT += core gui widgets svg
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Svg)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
qt_standard_project_setup()
|
||||||
# 3. Quellcode definieren
|
|
||||||
# ------------------------------------------------------------------------------
|
qt_add_executable(BionxControl WIN32 MACOSX_BUNDLE
|
||||||
set(PROJECT_SOURCES
|
bc.cpp bc.h
|
||||||
|
bcdelightpmwidget.cpp bcdelightpmwidget.h
|
||||||
|
bcdeviceview.cpp bcdeviceview.h
|
||||||
|
bcdriver.cpp bcdriver.h
|
||||||
|
bcdriverstatewidget.cpp bcdriverstatewidget.h
|
||||||
|
bcdrivertinycan.cpp bcdrivertinycan.h
|
||||||
|
bcmainwindow.cpp bcmainwindow.h bcmainwindow.ui
|
||||||
|
bcthemebutton.cpp bcthemebutton.h
|
||||||
|
bctoggleswitch.cpp bctoggleswitch.h
|
||||||
|
bctransmitter.cpp bctransmitter.h
|
||||||
|
bcvalue.cpp bcvalue.h
|
||||||
|
bcvaluedelegate.cpp bcvaluedelegate.h
|
||||||
|
bcvaluemodel.cpp bcvaluemodel.h
|
||||||
|
bcvalueslider.cpp bcvalueslider.h bcvalueslider.ui
|
||||||
|
bcxmlloader.cpp bcxmlloader.h
|
||||||
main.cpp
|
main.cpp
|
||||||
bcmainwindow.cpp
|
|
||||||
bc.cpp
|
|
||||||
bcdelightpmwidget.cpp
|
|
||||||
bcdeviceview.cpp
|
|
||||||
bcdriver.cpp
|
|
||||||
bcdriverstatewidget.cpp
|
|
||||||
bcdrivertinycan.cpp
|
|
||||||
bcthemeswitchbutton.cpp
|
|
||||||
bctoggleswitch.cpp
|
|
||||||
bctransmitter.cpp
|
|
||||||
bcvalue.cpp
|
|
||||||
bcvaluedelegate.cpp
|
|
||||||
bcvaluemodel.cpp
|
|
||||||
bcvalueslider.cpp
|
|
||||||
bcvaluesliderstyle.cpp
|
|
||||||
bcxmlloader.cpp
|
|
||||||
# Deine Header (wichtig, damit sie in der IDE sichtbar sind)
|
|
||||||
bcmainwindow.h
|
|
||||||
bc.h
|
|
||||||
bcdelightpmwidget.h
|
|
||||||
bcdeviceview.h
|
|
||||||
bcdriver.h
|
|
||||||
bcdriverstatewidget.h
|
|
||||||
bcdrivertinycan.h
|
|
||||||
bcthemeswitchbutton.h
|
|
||||||
bctoggleswitch.h
|
|
||||||
bctransmitter.h
|
|
||||||
bcvalue.h
|
|
||||||
bcvaluedelegate.h
|
|
||||||
bcvaluemodel.h
|
|
||||||
bcvalueslider.h
|
|
||||||
BCValueSliderStyle.h
|
|
||||||
bcxmlloader.h
|
|
||||||
# UI Forms
|
|
||||||
bcmainwindow.ui
|
|
||||||
bcvalueslider.ui
|
|
||||||
# Resources
|
|
||||||
bionxcontrol.qrc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# Plattformspezifische CAN-Treiber einbinden
|
||||||
# 4. Plattform-Spezifika (Architektur-Entscheidung)
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Windows-Spezifische Quellen und Libs
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
message(STATUS "Konfiguration für Windows.")
|
target_sources(BionxControl PRIVATE libwin/can_drv_win.c)
|
||||||
list(APPEND PROJECT_SOURCES
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
libwin/can_drv_win.c
|
target_sources(BionxControl PRIVATE libwin/mhs_can_drv.c)
|
||||||
libwin/mhs_can_drv.c
|
|
||||||
)
|
|
||||||
# Falls du die Libs später aktivieren willst (wie im .pro auskommentiert):
|
|
||||||
# target_link_libraries(BionxControl PRIVATE Advapi32)
|
|
||||||
# target_link_directories(BionxControl PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/can_api")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Linux / Raspberry Pi (ARM) Logik
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
# Check ob wir auf ARM kompilieren (für RPi)
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
|
||||||
message(STATUS "Konfiguration für Raspberry Pi (ARM) erkannt.")
|
|
||||||
# Optimierungs-Flag aus deiner .pro
|
|
||||||
add_compile_options(-O3)
|
|
||||||
|
|
||||||
# Falls du wiringPi oder can Treiber brauchst:
|
|
||||||
# target_link_libraries(BionxControl PRIVATE wiringPi mhstcan)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Hinweis: Im original .pro waren die .c Files auch unter Linux aktiv.
|
|
||||||
# Falls die C-Treiber auch unter Linux laufen sollen, verschiebe sie
|
|
||||||
# aus dem if(WIN32) Block nach oben in PROJECT_SOURCES.
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 5. Executable erstellen
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
qt_add_executable(BionxControl
|
|
||||||
MANUAL_FINALIZATION
|
|
||||||
${PROJECT_SOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Include-Pfade (Äquivalent zu INCLUDEPATH += . libwin)
|
|
||||||
target_include_directories(BionxControl PRIVATE
|
target_include_directories(BionxControl PRIVATE
|
||||||
.
|
.
|
||||||
libwin
|
libwin
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 6. Bibliotheken linken
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
target_link_libraries(BionxControl PRIVATE
|
target_link_libraries(BionxControl PRIVATE
|
||||||
Qt6::Core
|
Qt::Core
|
||||||
Qt6::Gui
|
Qt::Gui
|
||||||
Qt6::Widgets
|
Qt::Svg
|
||||||
Qt6::Svg
|
Qt::Widgets
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Resources:
|
||||||
|
set_source_files_properties("resources/bc_dark.qss"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bc_dark.qss"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/bc_light.qss"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bc_light.qss"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/bikeinfo.xml"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bikeinfo.xml"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/bionx_akku.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bionx_akku.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/bionx_console.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bionx_console.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/bionx_motor.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "bionx_motor.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/connect.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "connect.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/exit.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "exit.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/exit_red.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "exit_red.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/sync.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "sync.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/sync_green.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "sync_green.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/sync_yellow.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "sync_yellow.png"
|
||||||
|
)
|
||||||
|
set_source_files_properties("resources/update.png"
|
||||||
|
PROPERTIES QT_RESOURCE_ALIAS "update.png"
|
||||||
|
)
|
||||||
|
set(bionxcontrol_resource_files
|
||||||
|
"resources/bc_dark.qss"
|
||||||
|
"resources/bc_light.qss"
|
||||||
|
"resources/bikeinfo.xml"
|
||||||
|
"resources/bionx_akku.png"
|
||||||
|
"resources/bionx_console.png"
|
||||||
|
"resources/bionx_motor.png"
|
||||||
|
"resources/connect.png"
|
||||||
|
"resources/exit.png"
|
||||||
|
"resources/exit_red.png"
|
||||||
|
"resources/smile/face-angel.png"
|
||||||
|
"resources/smile/face-angry.png"
|
||||||
|
"resources/smile/face-cool.png"
|
||||||
|
"resources/smile/face-crying.png"
|
||||||
|
"resources/smile/face-embarrassed.png"
|
||||||
|
"resources/smile/face-glasses.png"
|
||||||
|
"resources/smile/face-kiss.png"
|
||||||
|
"resources/smile/face-laugh.png"
|
||||||
|
"resources/smile/face-monkey.png"
|
||||||
|
"resources/smile/face-plain.png"
|
||||||
|
"resources/smile/face-raspberry.png"
|
||||||
|
"resources/smile/face-sad.png"
|
||||||
|
"resources/smile/face-sick.png"
|
||||||
|
"resources/smile/face-smile-big.png"
|
||||||
|
"resources/smile/face-smile.png"
|
||||||
|
"resources/smile/face-smirk.png"
|
||||||
|
"resources/smile/face-surprise.png"
|
||||||
|
"resources/sync.png"
|
||||||
|
"resources/sync_green.png"
|
||||||
|
"resources/sync_yellow.png"
|
||||||
|
"resources/update.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_resources(BionxControl "bionxcontrol"
|
||||||
|
PREFIX
|
||||||
|
"/"
|
||||||
|
FILES
|
||||||
|
${bionxcontrol_resource_files}
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 7. Deployment / Installation
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Äquivalent zu deinem "target.path" Block
|
|
||||||
install(TARGETS BionxControl
|
install(TARGETS BionxControl
|
||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unter Windows: Konsolenfenster unterdrücken (außer im Debug-Mode wenn gewünscht)
|
|
||||||
if(WIN32)
|
|
||||||
set_target_properties(BionxControl PROPERTIES WIN32_EXECUTABLE ON)
|
|
||||||
endif()
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"configurePresets": [
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "Qt",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_PREFIX_PATH": "$env{QTDIR}"
|
|
||||||
},
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Qt": {
|
|
||||||
"checksum": "wVa86FgEkvdCTVp1/nxvrkaemJc="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Presets": {
|
|
||||||
"checksum": "67SmY24ZeVbebyKD0fGfIzb/bGI="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"configurePresets": [
|
|
||||||
{
|
|
||||||
"name": "Qt-Debug",
|
|
||||||
"inherits": "Qt-Default",
|
|
||||||
"binaryDir": "${sourceDir}/out/build/debug",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Debug",
|
|
||||||
"CMAKE_CXX_FLAGS": "-DQT_QML_DEBUG"
|
|
||||||
},
|
|
||||||
"environment": {
|
|
||||||
"QML_DEBUG_ARGS": "-qmljsdebugger=file:{aad3125d-03d3-444f-a577-6e1a42300747},block"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Qt-Release",
|
|
||||||
"inherits": "Qt-Default",
|
|
||||||
"binaryDir": "${sourceDir}/out/build/release",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Release"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "Qt-Default",
|
|
||||||
"inherits": "msvc2022x64Qt6",
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Default": {
|
|
||||||
"checksum": "i66Ru6Xr2iTwQJrInr6OBGYjRK8="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "5.13.2_msvc2017",
|
|
||||||
"inherits": "Qt",
|
|
||||||
"environment": {
|
|
||||||
"QTDIR": "C:/Qt/Qt5.13.2/5.13.2/msvc2017"
|
|
||||||
},
|
|
||||||
"architecture": {
|
|
||||||
"strategy": "external",
|
|
||||||
"value": "x86"
|
|
||||||
},
|
|
||||||
"generator": "Ninja",
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Version": {
|
|
||||||
"checksum": "buLxQ1kUsADOzoJ+w8yPcUB7uPI="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "msvc2022x64Qt6",
|
|
||||||
"inherits": "Qt",
|
|
||||||
"environment": {
|
|
||||||
"QTDIR": "C:/Qt/6.10.1/msvc2022_64"
|
|
||||||
},
|
|
||||||
"architecture": {
|
|
||||||
"strategy": "external",
|
|
||||||
"value": "x64"
|
|
||||||
},
|
|
||||||
"generator": "Ninja",
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Version": {
|
|
||||||
"checksum": "4+Yj6B2/47gfzBNUic9pikpc6XY="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "VS2017x64Default",
|
|
||||||
"inherits": "Qt",
|
|
||||||
"environment": {
|
|
||||||
"QTDIR": "C:/Qt/Qt5.13.2/5.13.2/msvc2017_64"
|
|
||||||
},
|
|
||||||
"architecture": {
|
|
||||||
"strategy": "external",
|
|
||||||
"value": "x64"
|
|
||||||
},
|
|
||||||
"generator": "Ninja",
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Version": {
|
|
||||||
"checksum": "8CScCBxKrPnt9tGlvYctTIj9T7o="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "VS2017x86Default",
|
|
||||||
"inherits": "Qt",
|
|
||||||
"environment": {
|
|
||||||
"QTDIR": "C:/Qt/Qt5.13.2/5.13.2/msvc2017"
|
|
||||||
},
|
|
||||||
"architecture": {
|
|
||||||
"strategy": "external",
|
|
||||||
"value": "x86"
|
|
||||||
},
|
|
||||||
"generator": "Ninja",
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Version": {
|
|
||||||
"checksum": "0W/xbuyiWWemn9gHwntcl/pzeNw="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vendor": {
|
|
||||||
"qt-project.org/Presets": {
|
|
||||||
"checksum": "VXVUfuzC5iU0zlFoq9u8LP40dyc="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,15 @@ FROM debian:trixie
|
|||||||
# Verhindert interaktive Prompts während der Installation
|
# Verhindert interaktive Prompts während der Installation
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
# 1. Multiarch für arm64 aktivieren
|
# 1. Multiarch für armhf aktivieren
|
||||||
RUN dpkg --add-architecture arm64 && \
|
RUN dpkg --add-architecture armhf && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get upgrade -y
|
apt-get upgrade -y
|
||||||
|
|
||||||
# 2. Host-Build-Tools und Cross-Compiler installieren
|
# 2. Host-Build-Tools und Cross-Compiler für 32-Bit installieren
|
||||||
RUN apt-get install -y \
|
RUN apt-get install -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
crossbuild-essential-arm64 \
|
crossbuild-essential-armhf \
|
||||||
cmake \
|
cmake \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
git \
|
git \
|
||||||
@@ -21,12 +21,14 @@ RUN apt-get install -y \
|
|||||||
# 3. Qt6 für den HOST installieren (für moc, uic, etc.)
|
# 3. Qt6 für den HOST installieren (für moc, uic, etc.)
|
||||||
RUN apt-get install -y \
|
RUN apt-get install -y \
|
||||||
qt6-base-dev \
|
qt6-base-dev \
|
||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools \
|
||||||
|
qt6-svg-dev
|
||||||
|
|
||||||
# 4. Qt6 Bibliotheken für das TARGET (arm64) installieren
|
# 4. Qt6 Bibliotheken für das TARGET (armhf) installieren
|
||||||
RUN apt-get install -y \
|
RUN apt-get install -y \
|
||||||
qt6-base-dev:arm64 \
|
qt6-base-dev:armhf \
|
||||||
libglvnd-dev:arm64
|
libglvnd-dev:armhf \
|
||||||
|
qt6-svg-dev:armhf
|
||||||
|
|
||||||
# Aufräumen
|
# Aufräumen
|
||||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
# Name des Zielsystems
|
# Name des Zielsystems
|
||||||
set(CMAKE_SYSTEM_NAME Linux)
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||||
|
|
||||||
# Die Cross-Compiler aus dem Debian-Paket
|
# Die Cross-Compiler aus dem Debian-Paket für 32-Bit (armhf)
|
||||||
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
|
set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
|
||||||
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
|
set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
|
||||||
|
|
||||||
# Wo das System nach Bibliotheken und Headern suchen soll (Sysroot)
|
# Wo das System nach Bibliotheken und Headern suchen soll (Sysroot)
|
||||||
set(CMAKE_SYSROOT /)
|
set(CMAKE_SYSROOT /)
|
||||||
|
|
||||||
# Pfade für pkg-config anpassen, damit es die arm64 .pc Dateien findet
|
# Pfade für pkg-config anpassen, damit es die armhf .pc Dateien findet
|
||||||
set(ENV{PKG_CONFIG_DIR} "")
|
set(ENV{PKG_CONFIG_DIR} "")
|
||||||
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig")
|
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig")
|
||||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
|
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
|
||||||
|
|
||||||
# Suchverhalten für find_package(), find_library() etc.
|
# Suchverhalten für find_package(), find_library() etc.
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Programme (wie moc) auf dem Host suchen
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Programme (wie moc) auf dem Host suchen
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Libs nur im Target (arm64) suchen
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Libs nur im Target (armhf) suchen
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Header nur im Target (arm64) suchen
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Header nur im Target (armhf) suchen
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # CMake-Pakete nur im Target suchen
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # CMake-Pakete nur im Target suchen
|
||||||
|
|
||||||
# *** WICHTIG FÜR QT6 ***
|
# *** WICHTIG FÜR QT6 ***
|
||||||
4
bc.h
4
bc.h
@@ -64,6 +64,10 @@ namespace BCTags
|
|||||||
|
|
||||||
inline constexpr auto Yes = "Yes"_L1;
|
inline constexpr auto Yes = "Yes"_L1;
|
||||||
inline constexpr auto No = "No"_L1;
|
inline constexpr auto No = "No"_L1;
|
||||||
|
|
||||||
|
inline constexpr auto Host = "bionxcontrol"_L1;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include <bcthemeswitchbutton.h>
|
#include <bcthemebutton.h>
|
||||||
#include <bcdriverstatewidget.h>
|
#include <bcdriverstatewidget.h>
|
||||||
#include <bcmainwindow.h>
|
#include <bcmainwindow.h>
|
||||||
#include <bcvaluedelegate.h>
|
#include <bcvaluedelegate.h>
|
||||||
@@ -189,9 +189,9 @@ void BCMainWindow::initStatusBar()
|
|||||||
_statusBar->addPermanentWidget(conState);
|
_statusBar->addPermanentWidget(conState);
|
||||||
conState->installEventFilter(this);
|
conState->installEventFilter(this);
|
||||||
|
|
||||||
BCThemeSwitchButton* themeBtn = new BCThemeSwitchButton(this);
|
BCThemeButton* themeBtn = new BCThemeButton(this);
|
||||||
_statusBar->addPermanentWidget(themeBtn);
|
_statusBar->addPermanentWidget(themeBtn);
|
||||||
connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark)
|
connect(themeBtn, &BCThemeButton::themeChanged, this, [this](bool isDark)
|
||||||
{
|
{
|
||||||
QString message = isDark ? "using DarkMode." : "using LightMode.";
|
QString message = isDark ? "using DarkMode." : "using LightMode.";
|
||||||
onShowMessage( message );
|
onShowMessage( message );
|
||||||
|
|||||||
@@ -30,10 +30,10 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
#include <bcthemeswitchbutton.h>
|
#include <bcthemebutton.h>
|
||||||
|
|
||||||
|
|
||||||
BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
|
BCThemeButton::BCThemeButton(QWidget *parent )
|
||||||
: QPushButton(parent)
|
: QPushButton(parent)
|
||||||
{
|
{
|
||||||
// Visuelles Setup: Flach, keine Ränder, Hand-Cursor
|
// Visuelles Setup: Flach, keine Ränder, Hand-Cursor
|
||||||
@@ -42,7 +42,7 @@ BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
|
|||||||
setFixedSize( 24, 24 );
|
setFixedSize( 24, 24 );
|
||||||
updateIcon();
|
updateIcon();
|
||||||
|
|
||||||
connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggleMode);
|
connect(this, &QPushButton::clicked, this, &BCThemeButton::toggleMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
|
|||||||
* @brief Setzt den DarkMode
|
* @brief Setzt den DarkMode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void BCThemeSwitchButton::setDarkMode( bool isDark )
|
void BCThemeButton::setDarkMode( bool isDark )
|
||||||
{
|
{
|
||||||
_isDarkMode = !isDark;
|
_isDarkMode = !isDark;
|
||||||
toggleMode();
|
toggleMode();
|
||||||
@@ -61,7 +61,7 @@ void BCThemeSwitchButton::setDarkMode( bool isDark )
|
|||||||
* @brief Schaltet den akutellen Mode um.
|
* @brief Schaltet den akutellen Mode um.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void BCThemeSwitchButton::toggleMode()
|
void BCThemeButton::toggleMode()
|
||||||
{
|
{
|
||||||
_isDarkMode = !_isDarkMode;
|
_isDarkMode = !_isDarkMode;
|
||||||
updateIcon();
|
updateIcon();
|
||||||
@@ -73,7 +73,7 @@ void BCThemeSwitchButton::toggleMode()
|
|||||||
* @brief Icon & Tooltip anpassen
|
* @brief Icon & Tooltip anpassen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void BCThemeSwitchButton::updateIcon()
|
void BCThemeButton::updateIcon()
|
||||||
{
|
{
|
||||||
setText(_isDarkMode ? "🌙" : "☀️");
|
setText(_isDarkMode ? "🌙" : "☀️");
|
||||||
setToolTip(_isDarkMode ? "Use LightMode" : "Use DarkMode");
|
setToolTip(_isDarkMode ? "Use LightMode" : "Use DarkMode");
|
||||||
@@ -43,13 +43,13 @@
|
|||||||
* zu wechseln
|
* zu wechseln
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BCThemeSwitchButton : public QPushButton
|
class BCThemeButton : public QPushButton
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit BCThemeSwitchButton(QWidget *parent = nullptr);
|
explicit BCThemeButton(QWidget *parent = nullptr);
|
||||||
void setDarkMode( bool isDark );
|
void setDarkMode( bool isDark );
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -62,8 +62,10 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
|
|||||||
|
|
||||||
const BCValue& bcValue = *(_valueList[ index.row() ].get());
|
const BCValue& bcValue = *(_valueList[ index.row() ].get());
|
||||||
|
|
||||||
|
BCValue::ValueRange params;
|
||||||
|
bool hasData = bcValue.hasValuesForSlider( params );
|
||||||
|
|
||||||
//qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << params.value << " min: " << params.min << " max: " << params.max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%';
|
qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << params.value << " min: " << params.min << " max: " << params.max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%';
|
||||||
|
|
||||||
if( bcValue.isBoolean() )
|
if( bcValue.isBoolean() )
|
||||||
{
|
{
|
||||||
@@ -72,17 +74,15 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
|
|||||||
|
|
||||||
// Signal für sofortige Updates
|
// Signal für sofortige Updates
|
||||||
connect(toggleSwitch, &BCToggleSwitch::toggled, this, [this, toggleSwitch](bool checked)
|
connect(toggleSwitch, &BCToggleSwitch::toggled, this, [this, toggleSwitch](bool checked)
|
||||||
{
|
{
|
||||||
qDebug() << "--- toggled: " << checked;
|
qDebug() << "--- toggled: " << checked;
|
||||||
// Commit data sofort bei Änderung
|
// Commit data sofort bei Änderung
|
||||||
emit const_cast<BCValueDelegate*>(this)->commitData(toggleSwitch);
|
emit const_cast<BCValueDelegate*>(this)->commitData(toggleSwitch);
|
||||||
});
|
});
|
||||||
|
|
||||||
return toggleSwitch;
|
return toggleSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
BCValue::ValueRange params;
|
|
||||||
bool hasData = bcValue.hasValuesForSlider( params );
|
|
||||||
|
|
||||||
if( !hasData )
|
if( !hasData )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -95,17 +95,6 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
|
|||||||
// Commit data sofort bei Änderung
|
// Commit data sofort bei Änderung
|
||||||
emit const_cast<BCValueDelegate*>(this)->commitData(valueSlider);
|
emit const_cast<BCValueDelegate*>(this)->commitData(valueSlider);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
// Signal für sofortige Updates
|
|
||||||
connect(valueSlider, &BCValueSlider::valueCommited, this, [this, valueSlider](int newValue)
|
|
||||||
{
|
|
||||||
qDebug() << " --- value set:" << newValue;
|
|
||||||
// Commit data sofort bei Änderung
|
|
||||||
emit const_cast<BCValueDelegate*>(this)->commitData(valueSlider);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
return valueSlider;
|
return valueSlider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,18 +114,6 @@ void BCValueDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionVi
|
|||||||
|
|
||||||
const BCValue& bcValue = *(_valueList[ index.row()].get());
|
const BCValue& bcValue = *(_valueList[ index.row()].get());
|
||||||
QRect editorRect = bcValue.isBoolean() ? adjustEditorRect(option.rect, 0, 6, -130, -6) : adjustEditorRect(option.rect, 0, 0, 8, 0);
|
QRect editorRect = bcValue.isBoolean() ? adjustEditorRect(option.rect, 0, 6, -130, -6) : adjustEditorRect(option.rect, 0, 0, 8, 0);
|
||||||
|
|
||||||
/*
|
|
||||||
if( !bcValue.isBoolean())
|
|
||||||
{
|
|
||||||
editorRect = adjustEditorRect( option.rect,0,0,8,0 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
editorRect = adjustEditorRect(option.rect, 0, 6, -130, -6);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
editor->setGeometry(editorRect);
|
editor->setGeometry(editorRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +138,7 @@ void BCValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
model->setData(index, reValue, Qt::EditRole);
|
model->setData(index, reValue, Qt::EditRole);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
40
build_and_deploy.sh
Executable file
40
build_and_deploy.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Bricht das Skript ab, falls ein Befehl (z. B. der Build) fehlschlägt
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# --- Variablen (Bitte anpassen!) ---
|
||||||
|
IMAGE_NAME="qt-cross-rpi"
|
||||||
|
# Trage hier den genauen Namen deines fertig kompilierten Executables ein:
|
||||||
|
BINARY_NAME="BionxControl"
|
||||||
|
TARGET_USER="chris"
|
||||||
|
TARGET_IP="192.168.0.106"
|
||||||
|
TARGET_DIR="/home/chris/projects/BionxControl"
|
||||||
|
# Den Namen der Toolchain aus deiner Vorlage oder der zuvor erstellten Datei
|
||||||
|
TOOLCHAIN_FILE="armhf-toolchain.cmake"
|
||||||
|
|
||||||
|
echo "=== 1. Baue das Docker-Image ==="
|
||||||
|
docker build -t ${IMAGE_NAME} .
|
||||||
|
|
||||||
|
echo "=== 2. Kompiliere das Projekt im Container ==="
|
||||||
|
# Wir führen die Build-Befehle direkt im Container aus, anstatt interaktiv zu starten.
|
||||||
|
# Das -it flag wurde entfernt, da wir es als Skript ausführen.
|
||||||
|
docker run --rm -v "$(pwd):/workspace" ${IMAGE_NAME} bash -c "
|
||||||
|
echo 'Bereinige altes Build-Verzeichnis...'
|
||||||
|
rm -rf dockerbuild
|
||||||
|
|
||||||
|
mkdir dockerbuild
|
||||||
|
cd dockerbuild
|
||||||
|
|
||||||
|
echo 'Führe CMake aus...'
|
||||||
|
cmake -DCMAKE_TOOLCHAIN_FILE=../${TOOLCHAIN_FILE} -G Ninja ..
|
||||||
|
|
||||||
|
echo 'Starte Build (Ninja)...'
|
||||||
|
ninja
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "=== 3. Deploy auf den Raspberry Pi via scp ==="
|
||||||
|
# Kopiert das neu gebaute Binary auf die Zielmaschine
|
||||||
|
scp "dockerbuild/${BINARY_NAME}" "${TARGET_USER}@${TARGET_IP}:${TARGET_DIR}"
|
||||||
|
|
||||||
|
echo "=== Deployment erfolgreich abgeschlossen! ==="
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
project(HelloWorldQt6 VERSION 1.0.0 LANGUAGES CXX)
|
|
||||||
|
|
||||||
# Wir nutzen modernes C++
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
# Qt-spezifische Automatismen aktivieren
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
set(CMAKE_AUTOUIC ON)
|
|
||||||
set(CMAKE_AUTORCC ON)
|
|
||||||
|
|
||||||
# Qt6 Widgets-Modul suchen
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
|
||||||
|
|
||||||
# Executable definieren und Quelldateien hinzufügen
|
|
||||||
add_executable(HelloWorldApp
|
|
||||||
main.cpp
|
|
||||||
MainWindow.cpp
|
|
||||||
MainWindow.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Qt6 Bibliotheken linken
|
|
||||||
target_link_libraries(HelloWorldApp PRIVATE Qt6::Widgets)
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#include "MainWindow.hpp"
|
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
|
||||||
: QMainWindow(parent)
|
|
||||||
{
|
|
||||||
// Button erstellen
|
|
||||||
m_helloButton = new QPushButton("Hello World", this);
|
|
||||||
|
|
||||||
// Den Button zum zentralen Widget des Fensters machen
|
|
||||||
setCentralWidget(m_helloButton);
|
|
||||||
|
|
||||||
// Eine angenehme Startgröße für das Fenster setzen
|
|
||||||
resize(400, 300);
|
|
||||||
|
|
||||||
// Ein kleines Extra: Klick auf den Button schließt die App
|
|
||||||
connect(m_helloButton, &QPushButton::clicked, this, &QMainWindow::close);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QMainWindow>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPushButton *m_helloButton;
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#include <QApplication>
|
|
||||||
#include "MainWindow.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
|
|
||||||
MainWindow window;
|
|
||||||
window.show();
|
|
||||||
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
||||||
16
main.cpp
16
main.cpp
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QSysInfo>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@@ -45,9 +46,20 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
BCMainWindow mainWindow;
|
||||||
|
//mainWindow.resize(800, 480);
|
||||||
|
|
||||||
|
if (QSysInfo::machineHostName() == BCTags::Host )
|
||||||
|
{
|
||||||
|
mainWindow.setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||||
|
mainWindow.showFullScreen();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normaler Fenster-Modus (z. B. für die Entwicklung auf dem Desktop)
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
|
||||||
BCMainWindow w;
|
|
||||||
w.show();
|
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|
||||||
|
|||||||
90
nodes/nodes/ntx.h
Normal file
90
nodes/nodes/ntx.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#ifndef NTX_H
|
||||||
|
#define NTX_H
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <string>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basistypen für die Kernbibliothek
|
||||||
|
* unser string typ, ohne den namespace
|
||||||
|
*
|
||||||
|
* Wir nehmen hier std::string, der immer UTF-8 kodiert ist.
|
||||||
|
* std::wstring ist plattformabhängig und führt zu Problemen. (Windows UTF-16 vs. Linux UTF-32)
|
||||||
|
* std::string_view für als mögliche Alternative zu const std::string&
|
||||||
|
*/
|
||||||
|
|
||||||
|
using NtxString = std::string;
|
||||||
|
using NtxStringView = std::string_view;
|
||||||
|
using NtxStringList = std::vector<NtxString>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ntx.h
|
||||||
|
* Enthält die grundlegenden Typdefinitionen und Hilfsfunktionen
|
||||||
|
* für die Kernbibliothek, z.B. NtxClassTypeId, NtxVariant und String-Konvertierungen.
|
||||||
|
* Alle anderen Header (z.B. ntxinode.h, ntxipayload.h) inkludieren dieses als Basis.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// Datentyp für die Typid einer Klasse von Knoten (_nicht_ eines einzelen Knotens)
|
||||||
|
// Option A: Performant (Aktuell)
|
||||||
|
using NtxClassTypeId = uint64_t;
|
||||||
|
|
||||||
|
// Option B: Flexibel / Lesbar (Zukunft?)
|
||||||
|
// using NtxClassTypeId = std::string;
|
||||||
|
|
||||||
|
// Option C: Legacy / Embedded
|
||||||
|
// using NtxClassTypeId = uint32_t;
|
||||||
|
|
||||||
|
// Generischer Zugriff über std::variant (wie eingangs besprochen)
|
||||||
|
using NtxVariant = std::variant<std::monostate, bool, int, double, NtxString>;
|
||||||
|
|
||||||
|
inline bool isEmpty(const NtxVariant& v) {
|
||||||
|
return std::holds_alternative<std::monostate>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
struct overload : Ts... { using Ts::operator()...; };
|
||||||
|
|
||||||
|
inline NtxString variantToString(const NtxVariant& v)
|
||||||
|
{
|
||||||
|
return std::visit(overload
|
||||||
|
{
|
||||||
|
[](std::monostate) { return NtxString{}; },
|
||||||
|
[](const NtxString& s) { return s; },
|
||||||
|
[](bool b) { return NtxString{b ? "true" : "false"}; },
|
||||||
|
[](auto num) { return std::to_string(num); } // Catch-all für int, double
|
||||||
|
}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core (UTF-8) -> Qt (UTF-16)
|
||||||
|
// Uses SSO (Small String Optimization) from Qt where possible.
|
||||||
|
inline QString toQt(const NtxString& str)
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString toQt(NtxStringView str)
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Qt (UTF-16) -> Core (UTF-8)
|
||||||
|
inline NtxString fromQt(const QString& qstr)
|
||||||
|
{
|
||||||
|
return qstr.toStdString(); // Qt converts this internally via toUtf8()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString toQString(const NtxVariant& variant)
|
||||||
|
{
|
||||||
|
return toQt(variantToString(variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NTX_H
|
||||||
|
|
||||||
73
nodes/nodes/ntxcompostionnode.h
Normal file
73
nodes/nodes/ntxcompostionnode.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef NTX_HASANODE_H
|
||||||
|
#define NTX_HASANODE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <ntxnodebase.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implementierung via Komposition (Has-A).
|
||||||
|
*
|
||||||
|
* Hält die Payload als Member 'm_payload'.
|
||||||
|
*/
|
||||||
|
template<typename TPayload>
|
||||||
|
class NtxHasANode : public NtxNodeBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Konstruktor forwarding an m_payload
|
||||||
|
template<typename... Args>
|
||||||
|
explicit NtxHasANode(Args&&... args)
|
||||||
|
: NtxNodeBase{},
|
||||||
|
m_payload{std::forward<Args>(args)...}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: Payload Forwarding
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
size_t getValueCount() const override { return m_payload.getValueCount(); }
|
||||||
|
void clearValues() override { m_payload.clearValues(); }
|
||||||
|
|
||||||
|
bool hasValue(const NtxString& key) const override { return m_payload.hasValue(key); }
|
||||||
|
void setValue(const NtxString& key, const NtxVariant& value) override { m_payload.setValue(key, value); }
|
||||||
|
NtxVariant getValue(const NtxString& key) const override { return m_payload.getValue(key); }
|
||||||
|
void removeValue(const NtxString& key) override { m_payload.removeValue(key); }
|
||||||
|
|
||||||
|
bool hasValue(size_t index) const override { return m_payload.hasValue(index); }
|
||||||
|
void setValue(size_t index, const NtxVariant& value) override { m_payload.setValue(index, value); }
|
||||||
|
NtxVariant getValue(size_t index) const override { return m_payload.getValue(index); }
|
||||||
|
|
||||||
|
// Deducing This Support (Raw Access)
|
||||||
|
// TPayload muss doGetValues() oder Zugriff auf den Vektor unterstützen
|
||||||
|
std::vector<NtxVariant>& doGetValues() override { return m_payload.doGetValues(); }
|
||||||
|
const std::vector<NtxVariant>& doGetValues() const override { return m_payload.doGetValues(); }
|
||||||
|
|
||||||
|
void forEachAttribute(NtxPayloadVisitor visitor) const override
|
||||||
|
{
|
||||||
|
m_payload.forEachAttribute(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr clone() const override
|
||||||
|
{
|
||||||
|
auto newNode = std::make_shared<NtxHasANode<TPayload>>();
|
||||||
|
newNode->m_payload = this->m_payload; // Copy Payload
|
||||||
|
newNode->setClassTypeId(this->getClassTypeId());
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TPayload m_payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_HASANODE_H
|
||||||
115
nodes/nodes/ntxibasicnode.cpp
Normal file
115
nodes/nodes/ntxibasicnode.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include <ntxibasicnode.h>
|
||||||
|
#include <ntxnodefactory.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: CType Aspekt
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
const NtxClassTypeId NtxIBasicNode::getClassTypeId() const
|
||||||
|
{
|
||||||
|
return m_ctypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxString& NtxIBasicNode::getClassTypeLabel() const
|
||||||
|
{
|
||||||
|
return NtxNodeFactory::lookupCTypeLabel(getClassTypeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: Tree Structure
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
void NtxIBasicNode::setParent(NtxNodePtr parent)
|
||||||
|
{
|
||||||
|
m_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxIBasicNode::getChildCount() const
|
||||||
|
{
|
||||||
|
return m_children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxIBasicNode::hasChildren() const
|
||||||
|
{
|
||||||
|
return !m_children.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxIBasicNode::addChild(NtxNodePtr child)
|
||||||
|
{
|
||||||
|
if (child)
|
||||||
|
{
|
||||||
|
m_children.push_back(child);
|
||||||
|
child->setParent(this->shared_from_this());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxIBasicNode::insertChild(size_t index, NtxNodePtr child)
|
||||||
|
{
|
||||||
|
if (!child) return;
|
||||||
|
|
||||||
|
if (index >= m_children.size())
|
||||||
|
m_children.push_back(child);
|
||||||
|
else
|
||||||
|
m_children.insert(m_children.begin() + index, child);
|
||||||
|
|
||||||
|
child->setParent(this->shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxIBasicNode::removeChild(size_t index)
|
||||||
|
{
|
||||||
|
if (index >= m_children.size()) return false;
|
||||||
|
|
||||||
|
if (m_children[index])
|
||||||
|
m_children[index]->setParent(nullptr);
|
||||||
|
|
||||||
|
m_children.erase(m_children.begin() + index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxIBasicNode::getChildIndex(const NtxNodePtr& child) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_children.size(); ++i)
|
||||||
|
{
|
||||||
|
if (m_children[i] == child) return i;
|
||||||
|
}
|
||||||
|
return static_cast<size_t>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxIBasicNode::clearChildren()
|
||||||
|
{
|
||||||
|
for (auto& child : m_children)
|
||||||
|
{
|
||||||
|
if (child) child->setParent(nullptr);
|
||||||
|
}
|
||||||
|
m_children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Protected Hooks
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
void NtxIBasicNode::setClassTypeLabel(const NtxString& idString)
|
||||||
|
{
|
||||||
|
setClassTypeId(NtxNodeFactory::generateCTypeId(idString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxIBasicNode::setClassTypeId(NtxClassTypeId classTypeId)
|
||||||
|
{
|
||||||
|
m_ctypeId = classTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxIBasicNode::doGetParent() const noexcept
|
||||||
|
{
|
||||||
|
return m_parent.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxIBasicNode::doGetChild(size_t index) const noexcept
|
||||||
|
{
|
||||||
|
if (index >= m_children.size()) return nullptr;
|
||||||
|
return m_children[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
66
nodes/nodes/ntxibasicnode.h
Normal file
66
nodes/nodes/ntxibasicnode.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef NTX_NODEBASE_H
|
||||||
|
#define NTX_NODEBASE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <ntxinode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abstrakte Basisklasse, die Baumstruktur und Typ-Information verwaltet.
|
||||||
|
*
|
||||||
|
* Implementiert alle Aspekte von NtxINode, die unabhängig von der Payload sind.
|
||||||
|
*/
|
||||||
|
class NtxIBasicNode : public NtxINode
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxIBasicNode() = default;
|
||||||
|
~NtxIBasicNode() override = default;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: CType Aspekt
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
const NtxClassTypeId getClassTypeId() const override;
|
||||||
|
const NtxString& getClassTypeLabel() const override;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: Tree Structure
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
void setParent(NtxNodePtr parent) override;
|
||||||
|
|
||||||
|
size_t getChildCount() const override;
|
||||||
|
bool hasChildren() const override;
|
||||||
|
|
||||||
|
void addChild(NtxNodePtr child) override;
|
||||||
|
void insertChild(size_t index, NtxNodePtr child) override;
|
||||||
|
bool removeChild(size_t index) override;
|
||||||
|
size_t getChildIndex(const NtxNodePtr& child) const override;
|
||||||
|
void clearChildren() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// --- Implementierung virtueller Hooks ---
|
||||||
|
|
||||||
|
void setClassTypeLabel(const NtxString& idString) override;
|
||||||
|
void setClassTypeId(NtxClassTypeId classTypeId) override;
|
||||||
|
|
||||||
|
NtxNodePtr doGetParent() const noexcept override;
|
||||||
|
NtxNodePtr doGetChild(size_t index) const noexcept override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
NtxClassTypeId m_ctypeId{};
|
||||||
|
NtxNodeWeakPtr m_parent;
|
||||||
|
NtxNodeList m_children;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODEBASE_H
|
||||||
37
nodes/nodes/ntxiclasstype.h
Normal file
37
nodes/nodes/ntxiclasstype.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef NTX_ICLASSTYPE_H
|
||||||
|
#define NTX_ICLASSTYPE_H
|
||||||
|
|
||||||
|
#include <ntx.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Minimales Interface für den 'ClassType' Aspekt: Der CType entspricht
|
||||||
|
* dem Element-namen eines Knotens im XML. Die ClassTypeId ist eine performante
|
||||||
|
* Repräsentation (z.B. Hash) dieses Namens. Kann unterschiedliche Implementierungen haben,
|
||||||
|
* z.B.: einfaches Hochzählen, Hash des Strings, den String selbst oder eine Meta-Klasse,
|
||||||
|
* vgl. QObject.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NtxIClassType
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NtxIClassType() = default;
|
||||||
|
|
||||||
|
virtual const NtxClassTypeId getClassTypeId() const = 0;
|
||||||
|
virtual const NtxString& getClassTypeLabel() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// ✅ Protected - nur abgeleitete Klassen und Friends
|
||||||
|
virtual void setClassTypeLabel(const NtxString& idString) = 0;
|
||||||
|
virtual void setClassTypeId(NtxClassTypeId classTypeId) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_ICLASSTYPE_H
|
||||||
143
nodes/nodes/ntxiclasstyperepo.cpp
Normal file
143
nodes/nodes/ntxiclasstyperepo.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <ntxiclasstyperepo.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
// =========================================================
|
||||||
|
// NtxCTypeHashRepo
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
NtxClassTypeId NtxCTypeHashRepo::generateId(const NtxString& label)
|
||||||
|
{
|
||||||
|
std::hash<NtxString> hasher;
|
||||||
|
// Expliziter Cast falls NtxClassTypeId kleiner als size_t ist
|
||||||
|
NtxClassTypeId id = static_cast<NtxClassTypeId>(hasher(label));
|
||||||
|
|
||||||
|
// Optimistischer Check (Read Lock)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
if (m_idToLabel.find(id) != m_idToLabel.end())
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registrieren (Write Lock)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
m_idToLabel[id] = label;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxClassTypeId NtxCTypeHashRepo::lookupId(const NtxString& label) const
|
||||||
|
{
|
||||||
|
std::hash<NtxString> hasher;
|
||||||
|
NtxClassTypeId id = static_cast<NtxClassTypeId>(hasher(label));
|
||||||
|
|
||||||
|
if (isRegistered(id))
|
||||||
|
return id;
|
||||||
|
|
||||||
|
return {}; // Default constructed ID (0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxString& NtxCTypeHashRepo::lookupLabel(NtxClassTypeId id) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
auto it = m_idToLabel.find(id);
|
||||||
|
if (it != m_idToLabel.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
static const NtxString empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCTypeHashRepo::isRegistered(NtxClassTypeId id) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_idToLabel.find(id) != m_idToLabel.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCTypeHashRepo::isRegistered(const NtxString& label) const
|
||||||
|
{
|
||||||
|
std::hash<NtxString> hasher;
|
||||||
|
return isRegistered(static_cast<NtxClassTypeId>(hasher(label)));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxCTypeHashRepo::getRegisteredCount() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_idToLabel.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// NtxCTypeCounterRepo
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
NtxCTypeCounterRepo::NtxCTypeCounterRepo()
|
||||||
|
: m_nextId(1) // Start bei 1, 0 ist invalid
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxClassTypeId NtxCTypeCounterRepo::generateId(const NtxString& label)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
auto it = m_labelToId.find(label);
|
||||||
|
if (it != m_labelToId.end())
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
// Double check
|
||||||
|
if (auto it = m_labelToId.find(label); it != m_labelToId.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
NtxClassTypeId newId = m_nextId++;
|
||||||
|
m_idToLabel[newId] = label;
|
||||||
|
m_labelToId[label] = newId;
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxClassTypeId NtxCTypeCounterRepo::lookupId(const NtxString& label) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
auto it = m_labelToId.find(label);
|
||||||
|
if (it != m_labelToId.end())
|
||||||
|
return it->second;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxString& NtxCTypeCounterRepo::lookupLabel(NtxClassTypeId id) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
auto it = m_idToLabel.find(id);
|
||||||
|
if (it != m_idToLabel.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
static const NtxString empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCTypeCounterRepo::isRegistered(NtxClassTypeId id) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_idToLabel.find(id) != m_idToLabel.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCTypeCounterRepo::isRegistered(const NtxString& label) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_labelToId.find(label) != m_labelToId.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxCTypeCounterRepo::getRegisteredCount() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_idToLabel.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
126
nodes/nodes/ntxiclasstyperepo.h
Normal file
126
nodes/nodes/ntxiclasstyperepo.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#ifndef NTX_CTYPEREPOSITORY_H
|
||||||
|
#define NTX_CTYPEREPOSITORY_H
|
||||||
|
|
||||||
|
#include <ntx.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Repository Interface für CType ID Management.
|
||||||
|
* Entkoppelt die ID-Generierungsstrategie von der Knoten-Implementierung.
|
||||||
|
*/
|
||||||
|
class NtxIClassTypeRepo
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NtxIClassTypeRepo() = default;
|
||||||
|
|
||||||
|
// Generiert oder holt eine ID für das Label
|
||||||
|
virtual NtxClassTypeId generateId(const NtxString& label) = 0;
|
||||||
|
|
||||||
|
// Lookups (Thread-safe read)
|
||||||
|
virtual NtxClassTypeId lookupId(const NtxString& label) const = 0;
|
||||||
|
virtual const NtxString& lookupLabel(NtxClassTypeId id) const = 0;
|
||||||
|
|
||||||
|
// Checks
|
||||||
|
virtual bool isRegistered(NtxClassTypeId id) const = 0;
|
||||||
|
virtual bool isRegistered(const NtxString& label) const = 0;
|
||||||
|
|
||||||
|
// Diagnostics
|
||||||
|
virtual size_t getRegisteredCount() const = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hash-basierte Implementierung (std::hash).
|
||||||
|
*/
|
||||||
|
class NtxCTypeHashRepo : public NtxIClassTypeRepo
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxClassTypeId generateId(const NtxString& label) override;
|
||||||
|
NtxClassTypeId lookupId(const NtxString& label) const override;
|
||||||
|
const NtxString& lookupLabel(NtxClassTypeId id) const override;
|
||||||
|
|
||||||
|
bool isRegistered(NtxClassTypeId id) const override;
|
||||||
|
bool isRegistered(const NtxString& label) const override;
|
||||||
|
size_t getRegisteredCount() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unordered_map<NtxClassTypeId, NtxString> m_idToLabel;
|
||||||
|
mutable std::shared_mutex m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Zähler-basierte Implementierung (1, 2, 3...).
|
||||||
|
*/
|
||||||
|
class NtxCTypeCounterRepo : public NtxIClassTypeRepo
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxCTypeCounterRepo();
|
||||||
|
|
||||||
|
NtxClassTypeId generateId(const NtxString& label) override;
|
||||||
|
NtxClassTypeId lookupId(const NtxString& label) const override;
|
||||||
|
const NtxString& lookupLabel(NtxClassTypeId id) const override;
|
||||||
|
|
||||||
|
bool isRegistered(NtxClassTypeId id) const override;
|
||||||
|
bool isRegistered(const NtxString& label) const override;
|
||||||
|
size_t getRegisteredCount() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unordered_map<NtxClassTypeId, NtxString> m_idToLabel;
|
||||||
|
std::unordered_map<NtxString, NtxClassTypeId> m_labelToId;
|
||||||
|
NtxClassTypeId m_nextId;
|
||||||
|
mutable std::shared_mutex m_mutex;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
constexpr uint64_t fnv1a_64(std::string_view text) {
|
||||||
|
uint64_t hash = 14695981039346656037ull; // 64-bit offset
|
||||||
|
for (char c : text) {
|
||||||
|
hash ^= static_cast<uint64_t>(c);
|
||||||
|
hash *= 1099511628211ull; // 64-bit prime
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ein Funktor (Funktionsobjekt), das die STL-Vorgaben für Hash-Funktionen erfüllt
|
||||||
|
struct FastFnv1aHasher {
|
||||||
|
std::size_t operator()(std::string_view sv) const {
|
||||||
|
// (Nutzt hier die fnv1a_64 Funktion von oben)
|
||||||
|
return fnv1a_64(sv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deine Map:
|
||||||
|
// Key: string_view (Zero-Copy)
|
||||||
|
// Value: MyData
|
||||||
|
// Hasher: Dein eigener, pfeilschneller FNV-1a (statt dem langsamen std::hash)
|
||||||
|
std::unordered_map<std::string_view, MyData, FastFnv1aHasher> xmlElements;
|
||||||
|
|
||||||
|
OBACHT! Lifetime! Woher kommt die StringView ?
|
||||||
|
|
||||||
|
-> doch: string internisieren ! -> Aber was ist mit copy_over_net ?
|
||||||
|
claude.ai: String Interning ist die richtige Wahl
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_CTYPEREPOSITORY_H
|
||||||
33
nodes/nodes/ntxinode.cpp
Normal file
33
nodes/nodes/ntxinode.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
#include <ntxnodeiterators.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
NtxNodeIterator NtxINode::begin()
|
||||||
|
{
|
||||||
|
return NtxNodeIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeIterator NtxINode::end()
|
||||||
|
{
|
||||||
|
return NtxNodeIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxCNodeIterator NtxINode::begin() const
|
||||||
|
{
|
||||||
|
return NtxCNodeIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxCNodeIterator NtxINode::end() const
|
||||||
|
{
|
||||||
|
return NtxCNodeIterator();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
176
nodes/nodes/ntxinode.h
Normal file
176
nodes/nodes/ntxinode.h
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#ifndef NTX_INODE_H
|
||||||
|
#define NTX_INODE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <ranges> // for std::ranges::subrange
|
||||||
|
#include <type_traits> // for std::conditional_t, std::is_const_v
|
||||||
|
|
||||||
|
#include <ntx.h>
|
||||||
|
#include <ntxiclasstype.h>
|
||||||
|
#include <ntxipayload.h>
|
||||||
|
#include <ntxnodeiterators.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ntxinode.h
|
||||||
|
* NtxINode ist die zentrale Schnittstelle, die NtxIClassType und NtxIPayload vereint
|
||||||
|
* und zusätzlich die Baumstruktur definiert.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxINode;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct NtxNodeDepthFirstIterator;
|
||||||
|
|
||||||
|
using NtxNodePtr = std::shared_ptr<NtxINode>;
|
||||||
|
using NtxNodeCPtr = std::shared_ptr<const NtxINode>;
|
||||||
|
using NtxNodeWeakPtr = std::weak_ptr<NtxINode>;
|
||||||
|
using NtxNodeList = std::vector<NtxNodePtr>;
|
||||||
|
|
||||||
|
using NtxNodeIterator = NtxNodeDepthFirstIterator<NtxINode>;
|
||||||
|
using NtxCNodeIterator = NtxNodeDepthFirstIterator<const NtxINode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Zentrales Interface für einen Knoten im Baum. Basiert auf NtxIClassType und NtxIPayload.
|
||||||
|
* Gibt die Baumstruktur vor (Eltern, Kinder, Geschwister) und bietet Iteratoren für die Traversierung.
|
||||||
|
* Die konkrete Implementierung erfolgt in NtxNode oder abgeleiteten Klassen.
|
||||||
|
* Nutzlast- und ClassType-Aspekte können durch Mixins realisiert werden.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NtxINode :
|
||||||
|
|
||||||
|
public NtxIClassType,
|
||||||
|
public NtxIPayload,
|
||||||
|
public std::enable_shared_from_this<NtxINode>
|
||||||
|
|
||||||
|
{
|
||||||
|
friend class NtxNodeFactory;
|
||||||
|
friend class NtxLinkNode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NtxINode() = default;
|
||||||
|
|
||||||
|
// --- Tree Structure ---
|
||||||
|
virtual void setParent(NtxNodePtr parent) = 0;
|
||||||
|
|
||||||
|
// Vereinfacht durch Deducing this und if constexpr
|
||||||
|
template <typename Self>
|
||||||
|
auto getParent(this Self&& self)
|
||||||
|
{
|
||||||
|
auto ptr = self.doGetParent();
|
||||||
|
if constexpr (std::is_const_v<std::remove_reference_t<Self>>)
|
||||||
|
return std::const_pointer_cast<const NtxINode>(ptr);
|
||||||
|
else
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
auto getChild(this Self&& self, size_t index)
|
||||||
|
{
|
||||||
|
auto ptr = self.doGetChild(index);
|
||||||
|
if constexpr (std::is_const_v<std::remove_reference_t<Self>>)
|
||||||
|
return std::const_pointer_cast<const NtxINode>(ptr);
|
||||||
|
else
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
auto getSibling(this Self&& self)
|
||||||
|
{
|
||||||
|
auto parent = self.getParent();
|
||||||
|
using ResultType = decltype(parent); // NtxNodePtr oder NtxNodeCPtr
|
||||||
|
|
||||||
|
if (!parent) return ResultType{};
|
||||||
|
|
||||||
|
const size_t childCount = parent->getChildCount();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < childCount; ++i)
|
||||||
|
{
|
||||||
|
auto child = parent->getChild(i);
|
||||||
|
// Hinweis: child.get() ist raw-pointer, &self ist Adresse des aktuellen Objekts
|
||||||
|
if (child.get() == &self)
|
||||||
|
{
|
||||||
|
if (i + 1 < childCount)
|
||||||
|
return parent->getChild(i + 1);
|
||||||
|
else
|
||||||
|
return ResultType{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResultType{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<size_t> ownPos() const
|
||||||
|
{
|
||||||
|
auto parent = doGetParent(); // Nutze internal (non-deducing) getter um const-cast zu vermeiden
|
||||||
|
if (!parent) return std::nullopt;
|
||||||
|
|
||||||
|
const size_t childCount = parent->getChildCount();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < childCount; ++i)
|
||||||
|
{
|
||||||
|
auto child = parent->getChild(i); // NtxNodePtr comparison
|
||||||
|
if (child.get() == this)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t getChildCount() const = 0;
|
||||||
|
virtual bool hasChildren() const = 0;
|
||||||
|
|
||||||
|
virtual void addChild(NtxNodePtr child) = 0;
|
||||||
|
virtual void insertChild(size_t index, NtxNodePtr child) = 0;
|
||||||
|
virtual bool removeChild(size_t index) = 0;
|
||||||
|
virtual size_t getChildIndex(const NtxNodePtr& child) const = 0;
|
||||||
|
virtual void clearChildren() = 0;
|
||||||
|
|
||||||
|
virtual NtxNodePtr clone() const = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// Unified Iterators via Deducing This
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
// Ersetzt begin/end und cbegin/cend Logik
|
||||||
|
template <typename Self>
|
||||||
|
auto begin(this Self&& self)
|
||||||
|
{
|
||||||
|
using NodeType = std::remove_reference_t<Self>; // NtxINode oder const NtxINode
|
||||||
|
// Konstruktor erwartet Pointer, &self ist sicher
|
||||||
|
return NtxNodeDepthFirstIterator<NodeType>(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
auto end(this Self&& self)
|
||||||
|
{
|
||||||
|
using NodeType = std::remove_reference_t<Self>;
|
||||||
|
return NtxNodeDepthFirstIterator<NodeType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// Ranges Support (C++23 Style)
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
auto descendants(this Self&& self)
|
||||||
|
{
|
||||||
|
return std::ranges::subrange(self.begin(), self.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual NtxNodePtr doGetParent() const = 0;
|
||||||
|
virtual NtxNodePtr doGetChild(size_t index) const = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_INODE_H
|
||||||
|
|
||||||
98
nodes/nodes/ntxipayload.h
Normal file
98
nodes/nodes/ntxipayload.h
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#ifndef NTX_IPAYLOAD_H
|
||||||
|
#define NTX_IPAYLOAD_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <variant> // for std::get_if
|
||||||
|
#include <functional> // for std::function
|
||||||
|
#include <type_traits> // for std::remove_reference_t
|
||||||
|
|
||||||
|
#include <ntx.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxIPayload;
|
||||||
|
|
||||||
|
using NtxPayloadVisitor = std::function<void(const NtxString&, const NtxVariant&)> const;
|
||||||
|
using NtxPayloadPtr = std::shared_ptr<NtxIPayload>;
|
||||||
|
|
||||||
|
// --- Payload Iterator-Typen (analog zu NtxMaptor<NtxVariant>) ---
|
||||||
|
using NtxPayloadIterator = std::vector<NtxVariant>::iterator;
|
||||||
|
using NtxPayloadCIterator = std::vector<NtxVariant>::const_iterator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Minimales Interface für 'Nutzlast' Aspekt. Kann beliebige Key-Value Paare speichern,
|
||||||
|
* wobei die Werte über NtxVariant typisiert sind. Implementierungen können intern
|
||||||
|
* z.B. std::unordered_map verwenden, einen 'Maptor' oder auf eine Meta-Klasse verweisen
|
||||||
|
* Das Interface bietet auch typsichere Zugriffsmethoden über Templates, die auf std::variant basieren.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NtxIPayload
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NtxIPayload() = default;
|
||||||
|
|
||||||
|
virtual size_t getPropertyCount() const = 0;
|
||||||
|
virtual void clearProperties() = 0;
|
||||||
|
|
||||||
|
// --- NtxIPayload Interface: Key-basiert ---
|
||||||
|
virtual bool hasProperty(const NtxString& key) const = 0;
|
||||||
|
virtual void setProperty(const NtxString& key, const NtxVariant& value) = 0;
|
||||||
|
virtual NtxVariant getProperty(const NtxString& key) const = 0;
|
||||||
|
virtual void removeProperty(const NtxString& key) = 0;
|
||||||
|
|
||||||
|
// --- NtxIPayload Interface: Index-basiert ---
|
||||||
|
virtual bool hasProperty(size_t index) const = 0;
|
||||||
|
virtual void setProperty(size_t index, const NtxVariant& value) = 0;
|
||||||
|
virtual NtxVariant getProperty(size_t index) const = 0;
|
||||||
|
|
||||||
|
//virtual NtxIPayload clone() const = 0;
|
||||||
|
template<typename TPayload>
|
||||||
|
TPayload clonePayload() const
|
||||||
|
{
|
||||||
|
return TPayload(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Payload Wrapper (Convenience) ---
|
||||||
|
template <typename T>
|
||||||
|
void set(const NtxString& key, T&& value)
|
||||||
|
{
|
||||||
|
setProperty(key, NtxVariant(std::forward<T>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::optional<T> get(const NtxString& key) const
|
||||||
|
{
|
||||||
|
// 1. Hole rohen Variant
|
||||||
|
NtxVariant raw = getProperty(key);
|
||||||
|
|
||||||
|
// 2. Prüfe ob der Typ T im Variant aktiv ist
|
||||||
|
// std::get_if liefert nullptr, wenn der Typ nicht passt.
|
||||||
|
if (const T* valPtr = std::get_if<T>(&raw))
|
||||||
|
return *valPtr;
|
||||||
|
// Typ passt nicht oder Key existiert nicht (monostate)
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @brief Komfort-Überladung: Liefert 'defaultValue', wenn Key fehlt oder Typ falsch ist.
|
||||||
|
template <typename T>
|
||||||
|
T get(const NtxString& key, const T& defaultValue) const
|
||||||
|
{
|
||||||
|
auto res = get<T>(key);
|
||||||
|
if (res.has_value())
|
||||||
|
return res.value();
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void forEachProperty(NtxPayloadVisitor visitor) const = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_IPAYLOAD_H
|
||||||
212
nodes/nodes/ntxlinknode.cpp
Normal file
212
nodes/nodes/ntxlinknode.cpp
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
#include <ntxlinknode.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <ntxnodeiterators.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxProxyNodeExpiredException : public std::runtime_error
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
NtxNodePtr lockOriginalNodeOrThrow(const NtxNodeWeakPtr& original)
|
||||||
|
{
|
||||||
|
auto orig = original.lock();
|
||||||
|
if (!orig)
|
||||||
|
throw NtxProxyNodeExpiredException("NtxLinkNode: Original node expired");
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeCPtr lockOriginalNodeOrThrowConst(const NtxNodeWeakPtr& original)
|
||||||
|
{
|
||||||
|
auto orig = original.lock();
|
||||||
|
if (!orig)
|
||||||
|
throw NtxProxyNodeExpiredException("NtxLinkNode: Original node expired");
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private constructor
|
||||||
|
NtxLinkNode::NtxLinkNode(NtxNodePtr originalNode)
|
||||||
|
: m_originalNode{originalNode}
|
||||||
|
{
|
||||||
|
if (!originalNode)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("NtxLinkNode: Original node cannot be nullptr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory method
|
||||||
|
NtxNodePtr NtxLinkNode::makeLink(NtxNodePtr originalNode)
|
||||||
|
{
|
||||||
|
return std::shared_ptr<NtxLinkNode>(new NtxLinkNode(originalNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::setParent(NtxNodePtr parent)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->setParent(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Type Access
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
const NtxClassTypeId NtxLinkNode::getClassTypeId() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getClassTypeId();
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxString& NtxLinkNode::getClassTypeLabel() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getClassTypeLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::setClassTypeLabel(const NtxString& idString)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->setClassTypeLabel(idString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::setClassTypeId(NtxClassTypeId classTypeId)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->setClassTypeId(classTypeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Original Node Access
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxNodePtr NtxLinkNode::getOriginalNode()
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeCPtr NtxLinkNode::getOriginalNode() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrowConst(m_originalNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxLinkNode::isValid() const
|
||||||
|
{
|
||||||
|
return !m_originalNode.expired();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Payload: Key-basiert
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxLinkNode::hasProperty(const NtxString& key) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->hasProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::setProperty(const NtxString& key, const NtxVariant& value)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->setProperty(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxVariant NtxLinkNode::getProperty(const NtxString& key) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::removeProperty(const NtxString& key)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->removeProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::clearProperties()
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->clearProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::forEachProperty(NtxPayloadVisitor visitor) const
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->forEachProperty(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Payload: Index-basiert
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxLinkNode::hasProperty(size_t index) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->hasProperty(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::setProperty(size_t index, const NtxVariant& value)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->setProperty(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxVariant NtxLinkNode::getProperty(size_t index) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getProperty(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Children
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
size_t NtxLinkNode::getChildCount() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getChildCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxLinkNode::hasChildren() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->hasChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxLinkNode::doGetParent() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->doGetParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxLinkNode::doGetChild(size_t index) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getChild(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::addChild(NtxNodePtr child)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->addChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxLinkNode::removeChild(size_t index)
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->removeChild(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::clearChildren()
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->clearChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxLinkNode::clone() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxLinkNode::insertChild(size_t index, NtxNodePtr child)
|
||||||
|
{
|
||||||
|
lockOriginalNodeOrThrow(m_originalNode)->insertChild(index, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxLinkNode::getChildIndex(const NtxNodePtr& child) const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getChildIndex(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxLinkNode::getPropertyCount() const
|
||||||
|
{
|
||||||
|
return lockOriginalNodeOrThrow(m_originalNode)->getPropertyCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
72
nodes/nodes/ntxlinknode.h
Normal file
72
nodes/nodes/ntxlinknode.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef NTX_LINKNODE_H
|
||||||
|
#define NTX_LINKNODE_H
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxLinkNode : public NtxINode
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Factory method to create a link to an original node
|
||||||
|
static NtxNodePtr makeLink(NtxNodePtr originalNode);
|
||||||
|
|
||||||
|
void setParent(NtxNodePtr parent) override;
|
||||||
|
|
||||||
|
const NtxClassTypeId getClassTypeId() const override;
|
||||||
|
const NtxString& getClassTypeLabel() const override;
|
||||||
|
void setClassTypeLabel(const NtxString& idString) override;
|
||||||
|
void setClassTypeId(const NtxClassTypeId classTypeId) override;
|
||||||
|
|
||||||
|
// Kinder
|
||||||
|
size_t getChildCount() const override;
|
||||||
|
bool hasChildren() const override;
|
||||||
|
|
||||||
|
void addChild(NtxNodePtr child) override;
|
||||||
|
void insertChild(size_t index, NtxNodePtr child) override;
|
||||||
|
bool removeChild(size_t index) override;
|
||||||
|
size_t getChildIndex(const NtxNodePtr& child) const override;
|
||||||
|
size_t getPropertyCount() const override;
|
||||||
|
void clearChildren() override;
|
||||||
|
|
||||||
|
NtxNodePtr clone() const override;
|
||||||
|
|
||||||
|
// Payload: Key-basiert
|
||||||
|
bool hasProperty(const NtxString& key) const override;
|
||||||
|
void setProperty(const NtxString& key, const NtxVariant& value) override;
|
||||||
|
NtxVariant getProperty(const NtxString& key) const override;
|
||||||
|
void removeProperty(const NtxString& key) override;
|
||||||
|
void clearProperties() override;
|
||||||
|
void forEachProperty(NtxPayloadVisitor visitor) const override;
|
||||||
|
|
||||||
|
// Payload: Index-basiert
|
||||||
|
bool hasProperty(size_t index) const override;
|
||||||
|
void setProperty(size_t index, const NtxVariant& value) override;
|
||||||
|
NtxVariant getProperty(size_t index) const override;
|
||||||
|
|
||||||
|
// Returns the original node this link points to
|
||||||
|
NtxNodePtr getOriginalNode();
|
||||||
|
NtxNodeCPtr getOriginalNode() const;
|
||||||
|
|
||||||
|
// Checks if the link is still valid (original node exists)
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodePtr doGetParent() const override;
|
||||||
|
NtxNodePtr doGetChild(size_t index) const override;
|
||||||
|
|
||||||
|
// Private constructor - use makeLink() factory method
|
||||||
|
explicit NtxLinkNode(NtxNodePtr originalNode);
|
||||||
|
|
||||||
|
// Weak pointer to avoid circular references
|
||||||
|
NtxNodeWeakPtr m_originalNode;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_LINKNODE_H
|
||||||
18
nodes/nodes/ntxnode.cpp
Normal file
18
nodes/nodes/ntxnode.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <ntxnode.h>
|
||||||
|
#include <ntxnodefactory.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
// Für Factory mit CTypeId
|
||||||
|
NtxNode::NtxNode(NtxClassTypeId id)
|
||||||
|
: NtxNode()
|
||||||
|
{
|
||||||
|
setClassTypeId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
45
nodes/nodes/ntxnode.h
Normal file
45
nodes/nodes/ntxnode.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef NTX_NODE_H
|
||||||
|
#define NTX_NODE_H
|
||||||
|
|
||||||
|
#include <ntxtcloneable.h>
|
||||||
|
#include <ntxtaggregatenode.h>
|
||||||
|
#include <ntxtmixinnode.h>
|
||||||
|
#include <ntxmaptor.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
class NtxNodeFactory;
|
||||||
|
|
||||||
|
using NtxVariantMaptor = NtxMaptor<NtxVariant>;
|
||||||
|
using NtxMixInNode = NtxTMixInNode<NtxVariantMaptor>;
|
||||||
|
using NtxAggregateNode = NtxTAggregateNode<NtxVariantMaptor>;
|
||||||
|
|
||||||
|
//class NtxNode : public NtxTCloneable<NtxNode, NtxMixInNode>
|
||||||
|
class NtxNode : public NtxTCloneable<NtxNode, NtxAggregateNode>
|
||||||
|
{
|
||||||
|
friend class NtxNodeFactory;
|
||||||
|
friend class NtxLinkNode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxNode() = default;
|
||||||
|
~NtxNode() override = default;
|
||||||
|
|
||||||
|
// Copy-Ctor wird von NtxTCloneable::clone() benötigt
|
||||||
|
NtxNode(const NtxNode&) = default;
|
||||||
|
NtxNode& operator=(const NtxNode&) = delete;
|
||||||
|
|
||||||
|
NtxNode(NtxNode&&) noexcept = default;
|
||||||
|
NtxNode& operator=(NtxNode&&) noexcept = default;
|
||||||
|
|
||||||
|
// clone() wird automatisch durch NtxTCloneable generiert
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
explicit NtxNode(NtxClassTypeId id);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODE_H
|
||||||
112
nodes/nodes/ntxnodefactory.cpp
Normal file
112
nodes/nodes/ntxnodefactory.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "ntxnodefactory.h"
|
||||||
|
#include "ntxnode.h"
|
||||||
|
#include <ntxiclasstyperepo.h> // implementation of default repository
|
||||||
|
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
NtxNodeFactory::NtxNodeFactory()
|
||||||
|
: m_ctypeRepository(std::make_unique<NtxCTypeHashRepo>()) // Default: Hash-basiert
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeFactory& NtxNodeFactory::getInstance()
|
||||||
|
{
|
||||||
|
static NtxNodeFactory instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxString& NtxNodeFactory::lookupCTypeLabel(NtxClassTypeId id)
|
||||||
|
{
|
||||||
|
return getInstance().getCTypeRepository().lookupLabel(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxClassTypeId NtxNodeFactory::generateCTypeId(const NtxString& cTypeLabel)
|
||||||
|
{
|
||||||
|
return getInstance().getCTypeRepository().generateId(cTypeLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeNode(const NtxString& label)
|
||||||
|
{
|
||||||
|
return getInstance().makeNodeByLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeNode(NtxClassTypeId id)
|
||||||
|
{
|
||||||
|
return getInstance().makeNodeById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeSharedNode(const NtxString& label, NtxNodePtr sharedSource)
|
||||||
|
{
|
||||||
|
return getInstance().makeSharedNodeByLabel(label, sharedSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeSharedNode(NtxClassTypeId id, NtxNodePtr sharedSource)
|
||||||
|
{
|
||||||
|
return getInstance().makeSharedNodeById(id, sharedSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Geile Recursion!
|
||||||
|
NtxNodePtr NtxNodeFactory::cloneNode(NtxNodeCPtr originalNode)
|
||||||
|
{
|
||||||
|
return originalNode->clone();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeNodeByLabel(const NtxString& ctypelabel)
|
||||||
|
{
|
||||||
|
// ID generieren und zuweisen
|
||||||
|
NtxClassTypeId id = m_ctypeRepository->generateId(ctypelabel);
|
||||||
|
return makeNodeById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeNodeById(NtxClassTypeId id)
|
||||||
|
{
|
||||||
|
// Node erstellen über friend-Zugriff
|
||||||
|
auto node = std::shared_ptr<NtxNode>(new NtxNode(id));
|
||||||
|
|
||||||
|
// Label aus Repository holen (wird in NtxIBasicNode::getClassTypeLabel() automatisch gemacht)
|
||||||
|
// Kein expliziter setClassTypeLabel-Aufruf nötig
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeSharedNodeByLabel(const NtxString& label, NtxNodePtr sharedSource)
|
||||||
|
{
|
||||||
|
// ID generieren und zuweisen
|
||||||
|
NtxClassTypeId id = m_ctypeRepository->generateId(label);
|
||||||
|
return makeSharedNodeById(id, sharedSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeFactory::makeSharedNodeById(NtxClassTypeId id, NtxNodePtr sharedSource)
|
||||||
|
{
|
||||||
|
return nullptr; // std::shared_ptr<NtxSharedNode>(new NtxSharedNode(id, sharedSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
NtxIClassTypeRepo& NtxNodeFactory::getCTypeRepository()
|
||||||
|
{
|
||||||
|
return *m_ctypeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxIClassTypeRepo& NtxNodeFactory::getCTypeRepository() const
|
||||||
|
{
|
||||||
|
return *m_ctypeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxNodeFactory::setCTypeRepository(std::unique_ptr<NtxIClassTypeRepo> repo)
|
||||||
|
{
|
||||||
|
if (repo)
|
||||||
|
m_ctypeRepository = std::move(repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
59
nodes/nodes/ntxnodefactory.h
Normal file
59
nodes/nodes/ntxnodefactory.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef NTX_NODEFACTORY_H
|
||||||
|
#define NTX_NODEFACTORY_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
#include <ntxiclasstyperepo.h> // Include for repo interface
|
||||||
|
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Factory für die Erstellung von Nodes. Das Management von den ClassTypeIds und ihren
|
||||||
|
* ClassTypeLabels wird hier versteckt. @see NtxIClassTypeRepo
|
||||||
|
*/
|
||||||
|
class NtxNodeFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Singleton-Zugriff auf die Factory-Instanz.
|
||||||
|
static NtxNodeFactory& getInstance();
|
||||||
|
// Statische Hilfsmethoden für CType-Management und Node-Erstellung.
|
||||||
|
static const NtxString& lookupCTypeLabel(NtxClassTypeId id);
|
||||||
|
static NtxClassTypeId generateCTypeId(const NtxString& cTypeLabel);
|
||||||
|
|
||||||
|
static NtxNodePtr makeNode(const NtxString& label);
|
||||||
|
static NtxNodePtr makeNode(NtxClassTypeId id);
|
||||||
|
static NtxNodePtr makeSharedNode(const NtxString& label, NtxNodePtr sharedSource);
|
||||||
|
static NtxNodePtr makeSharedNode(NtxClassTypeId id, NtxNodePtr sharedSource);
|
||||||
|
//static NtxNodePtr makeLinkedNode(const NtxString& label, NtxNodePtr sharedSource);
|
||||||
|
//static NtxNodePtr makeLinkedNode(NtxClassTypeId id, NtxNodePtr sharedSource);
|
||||||
|
|
||||||
|
//static NtxNodePtr cloneNode(NtxNodeCPtr originalNode);
|
||||||
|
|
||||||
|
NtxNodePtr makeNodeByLabel(const NtxString& label);
|
||||||
|
NtxNodePtr makeNodeById(NtxClassTypeId id);
|
||||||
|
NtxNodePtr makeSharedNodeByLabel(const NtxString& label, NtxNodePtr sharedSource);
|
||||||
|
NtxNodePtr makeSharedNodeById(NtxClassTypeId id, NtxNodePtr sharedSource);
|
||||||
|
|
||||||
|
NtxIClassTypeRepo& getCTypeRepository();
|
||||||
|
const NtxIClassTypeRepo& getCTypeRepository() const;
|
||||||
|
void setCTypeRepository(std::unique_ptr<NtxIClassTypeRepo> repo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodeFactory();
|
||||||
|
~NtxNodeFactory() = default;
|
||||||
|
|
||||||
|
// Nicht kopierbar
|
||||||
|
NtxNodeFactory(const NtxNodeFactory&) = delete;
|
||||||
|
NtxNodeFactory& operator=(const NtxNodeFactory&) = delete;
|
||||||
|
|
||||||
|
// Das zentrale Repository für alle Nodes
|
||||||
|
std::unique_ptr<NtxIClassTypeRepo> m_ctypeRepository;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODEFACTORY_H
|
||||||
127
nodes/nodes/ntxnodeiterators.h
Normal file
127
nodes/nodes/ntxnodeiterators.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#ifndef NTXNODE_ITERATORS_H
|
||||||
|
#define NTXNODE_ITERATORS_H
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <stack>
|
||||||
|
#include <type_traits> // Wichtig für std::remove_cv_t
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tiefensuche-Iterator für NtxINode Baumstrukturen
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct NtxNodeDepthFirstIterator
|
||||||
|
{
|
||||||
|
// C++20 Concepts Support:
|
||||||
|
// Wir definieren iterator_concept, um explizit Forward Iterator Verhalten zu signalisieren.
|
||||||
|
using iterator_concept = std::forward_iterator_tag;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// FIX 1: value_type darf NIE const sein, auch wenn T const ist!
|
||||||
|
using value_type = std::remove_cv_t<T>;
|
||||||
|
|
||||||
|
using pointer = T*;
|
||||||
|
using reference = T&;
|
||||||
|
|
||||||
|
NtxNodeDepthFirstIterator()
|
||||||
|
: m_root{ nullptr }, m_current{ nullptr }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit NtxNodeDepthFirstIterator(pointer node)
|
||||||
|
: m_root{ node }, m_current{ node }
|
||||||
|
{
|
||||||
|
if (m_current) {
|
||||||
|
m_stack.push({ m_current, 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer get() const
|
||||||
|
{
|
||||||
|
return m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
int level() const
|
||||||
|
{
|
||||||
|
return static_cast<int>(m_stack.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIX 2: Dereferenzierung muss const sein
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIX 2: Dereferenzierung muss const sein
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return *m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return m_current != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeDepthFirstIterator& operator++()
|
||||||
|
{
|
||||||
|
if (!m_current || m_stack.empty()) {
|
||||||
|
m_current = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!m_stack.empty()) {
|
||||||
|
auto& top = m_stack.top();
|
||||||
|
auto node = top.first;
|
||||||
|
auto& nextChildIndex = top.second;
|
||||||
|
|
||||||
|
// node->getChildCount() funktioniert, da T (bzw. ntx::NtxINode) hier vollständig bekannt ist
|
||||||
|
const auto childCount = node->getChildCount();
|
||||||
|
while (nextChildIndex < childCount) {
|
||||||
|
auto child = node->getChild(nextChildIndex++);
|
||||||
|
if (child) {
|
||||||
|
m_current = child.get();
|
||||||
|
m_stack.push({ m_current, 0 });
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeDepthFirstIterator operator++(int)
|
||||||
|
{
|
||||||
|
NtxNodeDepthFirstIterator tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden Friends für Vergleiche (ADL)
|
||||||
|
friend bool operator==(const NtxNodeDepthFirstIterator& a, const NtxNodeDepthFirstIterator& b)
|
||||||
|
{
|
||||||
|
return a.m_current == b.m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const NtxNodeDepthFirstIterator& a, const NtxNodeDepthFirstIterator& b)
|
||||||
|
{
|
||||||
|
return a.m_current != b.m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
pointer m_root;
|
||||||
|
pointer m_current;
|
||||||
|
std::stack<std::pair<pointer, size_t>> m_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTXNODE_ITERATORS_H
|
||||||
57
nodes/nodes/ntxnodeproxy.h
Normal file
57
nodes/nodes/ntxnodeproxy.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef NTX_NODEPROXY_H
|
||||||
|
#define NTX_NODEPROXY_H
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transparenter Proxy für NtxINodePtr.
|
||||||
|
*
|
||||||
|
* operator->() → delegiert ALLE Methodenaufrufe an den internen Pointer.
|
||||||
|
* operator NtxINodePtr() → impliziter Cast wo NtxINodePtr erwartet wird.
|
||||||
|
*
|
||||||
|
* Keine einzige NtxINode-Methode muss reimplementiert werden.
|
||||||
|
*/
|
||||||
|
class NtxNodeProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit NtxNodeProxy(NtxINodePtr node)
|
||||||
|
: m_node(std::move(node))
|
||||||
|
{}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Das Herzstück: leitet JEDEN Methodenaufruf transparent weiter
|
||||||
|
// proxy->getProperty("x") → m_node->getProperty("x")
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
NtxINode* operator->() { return m_node.get(); }
|
||||||
|
const NtxINode* operator->() const { return m_node.get(); }
|
||||||
|
|
||||||
|
NtxINode& operator*() { return *m_node; }
|
||||||
|
const NtxINode& operator*() const { return *m_node; }
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Implizite Konvertierung — überall wo NtxINodePtr erwartet wird
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
operator NtxINodePtr() const { return m_node; }
|
||||||
|
operator NtxNodeCPtr() const { return m_node; }
|
||||||
|
operator bool() const { return m_node != nullptr; }
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Expliziter Zugriff
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
NtxINodePtr get() const { return m_node; }
|
||||||
|
|
||||||
|
void reset(NtxINodePtr node = nullptr) { m_node = std::move(node); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxINodePtr m_node;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODEPROXY_H
|
||||||
150
nodes/nodes/ntxsharedpayload.cpp
Normal file
150
nodes/nodes/ntxsharedpayload.cpp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include <ntxsharedpayload.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxSharedPayload: Key-basiert
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxSharedPayload::NtxSharedPayload(const NtxPayloadPtr& original)
|
||||||
|
: m_originalPayload(original)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxSharedPayload::hasValue(const NtxString& key) const
|
||||||
|
{
|
||||||
|
return m_originalPayload && m_originalPayload->hasValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::setValue(const NtxString& key, const NtxVariant& value)
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
m_originalPayload->setValue(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxVariant NtxSharedPayload::getValue(const NtxString& key) const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->getValue(key);
|
||||||
|
return NtxVariant{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::removeValue(const NtxString& key)
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
m_originalPayload->removeValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::clearValues()
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
m_originalPayload->clearValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxSharedPayload::getValueCount() const
|
||||||
|
{
|
||||||
|
return m_originalPayload ? m_originalPayload->getValueCount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::forEachProperty(NtxPayloadVisitor visitor) const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
m_originalPayload->forEachProperty(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NtxStringList NtxSharedPayload::getValueKeys() const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->getValueKeys();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxSharedPayload: Index-basiert
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxSharedPayload::hasValue(size_t index) const
|
||||||
|
{
|
||||||
|
return m_originalPayload && m_originalPayload->hasValue(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::setValue(size_t index, const NtxVariant& value)
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
m_originalPayload->setValue(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxVariant NtxSharedPayload::getValue(size_t index) const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->getValue(index);
|
||||||
|
return NtxVariant{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxSharedPayload: Iteratoren
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
NtxPayloadIterator NtxSharedPayload::valuesBegin()
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->valuesBegin();
|
||||||
|
// Leerer Iterator: Kein gültiger Fallback möglich → UB vermeiden
|
||||||
|
static std::vector<NtxVariant> empty;
|
||||||
|
return empty.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxPayloadIterator NtxSharedPayload::valuesEnd()
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->valuesEnd();
|
||||||
|
static std::vector<NtxVariant> empty;
|
||||||
|
return empty.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxPayloadCIterator NtxSharedPayload::valuesBegin() const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->valuesBegin();
|
||||||
|
static const std::vector<NtxVariant> empty;
|
||||||
|
return empty.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxPayloadCIterator NtxSharedPayload::valuesEnd() const
|
||||||
|
{
|
||||||
|
if (m_originalPayload)
|
||||||
|
return m_originalPayload->valuesEnd();
|
||||||
|
static const std::vector<NtxVariant> empty;
|
||||||
|
return empty.cend();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxSharedPayload: Sonstiges
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxPayloadPtr NtxSharedPayload::getOriginalPayload() const
|
||||||
|
{
|
||||||
|
return m_originalPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::setOriginalPayload(const NtxPayloadPtr& original)
|
||||||
|
{
|
||||||
|
m_originalPayload = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxSharedPayload::isValid() const
|
||||||
|
{
|
||||||
|
return m_originalPayload != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxSharedPayload::detach()
|
||||||
|
{
|
||||||
|
m_originalPayload.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
89
nodes/nodes/ntxtaggregatenode.h
Normal file
89
nodes/nodes/ntxtaggregatenode.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#ifndef NTX_AGGREGATENODE_H
|
||||||
|
#define NTX_AGGREGATENODE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ntxibasicnode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implementierung via Aggregation (KISS-Variante).
|
||||||
|
*
|
||||||
|
* Besitzt oder referenziert einen NtxIPayload über shared_ptr.
|
||||||
|
* Owning: NtxTAggregateNode(std::make_shared<MyPayload>(...))
|
||||||
|
* Referencing: NtxTAggregateNode(existingPayloadPtr)
|
||||||
|
*/
|
||||||
|
template<typename TPayloadImpl>
|
||||||
|
class NtxTAggregateNode : public NtxIBasicNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Owning: erstellt eigene TPayloadImpl-Instanz
|
||||||
|
/*
|
||||||
|
template<typename... Args>
|
||||||
|
explicit NtxTAggregateNode(Args&&... args)
|
||||||
|
: NtxIBasicNode{}
|
||||||
|
, m_payload(std::make_shared<TPayloadImpl>(std::forward<Args>(args)...))
|
||||||
|
{}
|
||||||
|
*/
|
||||||
|
|
||||||
|
NtxTAggregateNode()
|
||||||
|
: NtxIBasicNode{}, m_payload(std::make_shared<TPayloadImpl>())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Referencing: verknüpft bestehenden Payload
|
||||||
|
explicit NtxTAggregateNode(std::shared_ptr<TPayloadImpl> sharedPayload)
|
||||||
|
: NtxIBasicNode{}
|
||||||
|
, m_payload(std::move(sharedPayload))
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Payload zur Laufzeit austauschen (Owning ↔ Referencing)
|
||||||
|
void setPayload(NtxPayloadPtr payload) { m_payload = std::move(payload); }
|
||||||
|
NtxPayloadPtr getPayload() const { return m_payload; }
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxIPayload Interface: Delegation
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
size_t getPropertyCount() const override { return m_payload->getPropertyCount(); }
|
||||||
|
void clearProperties() override { m_payload->clearProperties(); }
|
||||||
|
|
||||||
|
bool hasProperty(const NtxString& key) const override { return m_payload->hasProperty(key); }
|
||||||
|
void setProperty(const NtxString& key, const NtxVariant& value) override
|
||||||
|
{ m_payload->setProperty(key, value); }
|
||||||
|
NtxVariant getProperty(const NtxString& key) const override
|
||||||
|
{ return m_payload->getProperty(key); }
|
||||||
|
void removeProperty(const NtxString& key) override { m_payload->removeProperty(key); }
|
||||||
|
|
||||||
|
bool hasProperty(size_t index) const override { return m_payload->hasProperty(index); }
|
||||||
|
void setProperty(size_t index, const NtxVariant& value) override
|
||||||
|
{ m_payload->setProperty(index, value); }
|
||||||
|
NtxVariant getProperty(size_t index) const override { return m_payload->getProperty(index); }
|
||||||
|
|
||||||
|
void forEachProperty(NtxPayloadVisitor visitor) const override
|
||||||
|
{
|
||||||
|
//m_payload->forEachProperty(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr clone() const override
|
||||||
|
{
|
||||||
|
// m_payload ist bereits shared_ptr<TPayloadImpl> — kein cast nötig
|
||||||
|
auto newNode = std::make_shared<NtxTAggregateNode<TPayloadImpl>>(
|
||||||
|
std::make_shared<TPayloadImpl>(*m_payload));
|
||||||
|
newNode->setClassTypeId(this->getClassTypeId());
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// NtxPayloadPtr m_payload; // Nein! das ist eine völlig unnötige Einschränkung auf IPayload.
|
||||||
|
std::shared_ptr<TPayloadImpl> m_payload;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_AGGREGATENODE_H
|
||||||
34
nodes/nodes/ntxtcloneable.h
Normal file
34
nodes/nodes/ntxtcloneable.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef NTX_CLONEABLE_H
|
||||||
|
#define NTX_CLONEABLE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ntxinode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CRTP-Mixin: generiert clone() automatisch für jede Subklasse.
|
||||||
|
*
|
||||||
|
* Voraussetzung: Derived ist copy-konstruierbar.
|
||||||
|
*
|
||||||
|
* Verwendung:
|
||||||
|
* class MyNode : public NtxTCloneable<MyNode, NtxTMixInNode<MyPayload>> { ... };
|
||||||
|
*/
|
||||||
|
template<typename Derived, typename Base>
|
||||||
|
class NtxTCloneable : public Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Base::Base; // Konstruktoren durchreichen
|
||||||
|
|
||||||
|
NtxNodePtr clone() const override
|
||||||
|
{
|
||||||
|
return std::make_shared<Derived>(static_cast<const Derived&>(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_CLONEABLE_H
|
||||||
73
nodes/nodes/ntxtmixinnode.h
Normal file
73
nodes/nodes/ntxtmixinnode.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef NTX_TMIXINNODE_H
|
||||||
|
#define NTX_TMIXINNODE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <ntxibasicnode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implementierung via Vererbung (Mixin).
|
||||||
|
*
|
||||||
|
* TPayloadImpl wird als Basisklasse eingemischt.
|
||||||
|
*/
|
||||||
|
template<typename TPayloadImpl>
|
||||||
|
class NtxTMixInNode : public NtxIBasicNode, public TPayloadImpl
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Konstruktor forwarding an PayloadImpl
|
||||||
|
template<typename... Args>
|
||||||
|
explicit NtxTMixInNode(Args&&... args)
|
||||||
|
: NtxIBasicNode{},
|
||||||
|
TPayloadImpl{std::forward<Args>(args)...}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NtxINode Interface: Payload Forwarding
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
size_t getPropertyCount() const override { return TPayloadImpl::getPropertyCount(); }
|
||||||
|
void clearProperties() override { TPayloadImpl::clearProperties(); }
|
||||||
|
|
||||||
|
bool hasProperty(const NtxString& key) const override { return TPayloadImpl::hasProperty(key); }
|
||||||
|
void setProperty(const NtxString& key, const NtxVariant& value) override { TPayloadImpl::setProperty(key, value); }
|
||||||
|
NtxVariant getProperty(const NtxString& key) const override { return TPayloadImpl::getProperty(key); }
|
||||||
|
void removeProperty(const NtxString& key) override { TPayloadImpl::removeProperty(key); }
|
||||||
|
|
||||||
|
bool hasProperty(size_t index) const override { return TPayloadImpl::hasProperty(index); }
|
||||||
|
void setProperty(size_t index, const NtxVariant& value) override { TPayloadImpl::setProperty(index, value); }
|
||||||
|
NtxVariant getProperty(size_t index) const override { return TPayloadImpl::getProperty(index); }
|
||||||
|
|
||||||
|
|
||||||
|
// Deducing This Support (Raw Access)
|
||||||
|
//std::vector<NtxVariant>& doGetValues() override { return TPayloadImpl::doGetValues(); }
|
||||||
|
//const std::vector<NtxVariant>& doGetValues() const override { return TPayloadImpl::doGetValues(); }
|
||||||
|
|
||||||
|
|
||||||
|
void forEachProperty(NtxPayloadVisitor visitor) const override
|
||||||
|
{
|
||||||
|
//TPayloadImpl::forEachProperty(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NtxNodePtr clone() const override
|
||||||
|
{
|
||||||
|
// Basic Clone Implementation: Kopiert PayloadImpl Teil via Copy-Ctor
|
||||||
|
auto newNode = std::make_shared<NtxTMixInNode<TPayloadImpl>>(*static_cast<const TPayloadImpl*>(this));
|
||||||
|
// Base Properties kopieren
|
||||||
|
newNode->setClassTypeId(this->getClassTypeId());
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_TMIXINNODE_H
|
||||||
83
nodes/services/ntxclipboard.cpp
Normal file
83
nodes/services/ntxclipboard.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include "ntxclipboard.h"
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
void NtxClipboard::copyNodes(const NtxNodeList& nodes)
|
||||||
|
{
|
||||||
|
m_nodeEntryList.clear();
|
||||||
|
|
||||||
|
for (const auto& node : nodes)
|
||||||
|
{
|
||||||
|
if (!node) continue;
|
||||||
|
|
||||||
|
NtxNodeEntry entry;
|
||||||
|
entry.clone = createOrphanClone(node);
|
||||||
|
entry.originalParent = node->getParent();
|
||||||
|
entry.originalPosition = node->ownPos().value_or(0);
|
||||||
|
|
||||||
|
m_nodeEntryList.push_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxClipboard::cutNodes(const NtxNodeList& nodes)
|
||||||
|
{
|
||||||
|
// Identisch zu copyNodes - Der Unterschied liegt in NtxHeadNode
|
||||||
|
copyNodes(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeList NtxClipboard::pasteNodes() const
|
||||||
|
{
|
||||||
|
NtxNodeList result;
|
||||||
|
|
||||||
|
for (const auto& entry : m_nodeEntryList)
|
||||||
|
{
|
||||||
|
if (!entry.clone) continue;
|
||||||
|
|
||||||
|
// Neuen Klon vom Clipboard-Clone erstellen (mehrfaches Paste möglich)
|
||||||
|
auto freshClone = entry.clone->clone();
|
||||||
|
if (freshClone)
|
||||||
|
{
|
||||||
|
result.push_back(freshClone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxClipboard::isEmpty() const
|
||||||
|
{
|
||||||
|
return m_nodeEntryList.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxClipboard::getCount() const
|
||||||
|
{
|
||||||
|
return m_nodeEntryList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxClipboard::clear()
|
||||||
|
{
|
||||||
|
m_nodeEntryList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxNodeEntryList& NtxClipboard::getEntries() const
|
||||||
|
{
|
||||||
|
return m_nodeEntryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxClipboard::createOrphanClone(NtxNodePtr node) const
|
||||||
|
{
|
||||||
|
if (!node) return nullptr;
|
||||||
|
|
||||||
|
// Deep-Clone erstellen
|
||||||
|
auto clone = node->clone();
|
||||||
|
|
||||||
|
// Parent-Verbindung trennen (Elternlos machen)
|
||||||
|
if (clone)
|
||||||
|
{
|
||||||
|
clone->setParent(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
47
nodes/services/ntxclipboard.h
Normal file
47
nodes/services/ntxclipboard.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef NTX_CLIPBOARD_H
|
||||||
|
#define NTX_CLIPBOARD_H
|
||||||
|
|
||||||
|
#include <ntxicommand.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Einfaches Clipboard für Copy/Cut/Paste-Operationen.
|
||||||
|
*
|
||||||
|
* Speichert elternlose Deep-Clones mit Original-Position für Undo.
|
||||||
|
* Kein Singleton - wird als Member von NtxHeadNode verwendet.
|
||||||
|
*/
|
||||||
|
class NtxClipboard
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxClipboard() = default;
|
||||||
|
~NtxClipboard() = default;
|
||||||
|
|
||||||
|
// Copy/Cut Operations
|
||||||
|
void copyNodes(const NtxNodeList& nodes);
|
||||||
|
void cutNodes(const NtxNodeList& nodes);
|
||||||
|
|
||||||
|
// Paste Operations
|
||||||
|
NtxNodeList pasteNodes() const;
|
||||||
|
|
||||||
|
// State
|
||||||
|
bool isEmpty() const;
|
||||||
|
size_t getCount() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// Access
|
||||||
|
const NtxNodeEntryList& getEntries() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodeEntryList m_nodeEntryList;
|
||||||
|
|
||||||
|
NtxNodePtr createOrphanClone(NtxNodePtr node) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_CLIPBOARD_H
|
||||||
141
nodes/services/ntxcmdlist.cpp
Normal file
141
nodes/services/ntxcmdlist.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include <NtxCmdList.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
NtxCmdList::NtxCmdList()
|
||||||
|
: m_executedCount(0)
|
||||||
|
{
|
||||||
|
setDescription("Multi Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxCmdList::NtxCmdList(const NtxString& description)
|
||||||
|
: m_executedCount(0)
|
||||||
|
{
|
||||||
|
setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCmdList::execute()
|
||||||
|
{
|
||||||
|
m_executedCount = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_commands.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!m_commands[i]->canExecute())
|
||||||
|
{
|
||||||
|
rollback(i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_commands[i]->execute())
|
||||||
|
{
|
||||||
|
rollback(i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_executedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCmdList::undo()
|
||||||
|
{
|
||||||
|
if (m_executedCount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = m_executedCount; i > 0; --i)
|
||||||
|
{
|
||||||
|
size_t index = i - 1;
|
||||||
|
|
||||||
|
if (!m_commands[index]->canUndo())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_commands[index]->undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_executedCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCmdList::canExecute() const
|
||||||
|
{
|
||||||
|
if (m_commands.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const auto& cmd : m_commands)
|
||||||
|
{
|
||||||
|
if (!cmd->canExecute())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCmdList::canUndo() const
|
||||||
|
{
|
||||||
|
if (m_executedCount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_executedCount; ++i)
|
||||||
|
{
|
||||||
|
if (m_commands[i]->canUndo())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxString NtxCmdList::getDescription() const
|
||||||
|
{
|
||||||
|
NtxString desc = NtxICommand::getDescription();
|
||||||
|
|
||||||
|
if (!desc.empty())
|
||||||
|
return desc;
|
||||||
|
|
||||||
|
if (m_commands.empty())
|
||||||
|
return "Empty Multi Command";
|
||||||
|
|
||||||
|
return "Multi Command (" + std::to_string(m_commands.size()) + " commands)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxCmdList::addCommand(NtxCommandUPtr command)
|
||||||
|
{
|
||||||
|
if (command)
|
||||||
|
{
|
||||||
|
m_commands.push_back(std::move(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxCmdList::clear()
|
||||||
|
{
|
||||||
|
m_commands.clear();
|
||||||
|
m_executedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxCmdList::count() const
|
||||||
|
{
|
||||||
|
return m_commands.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxCmdList::isEmpty() const
|
||||||
|
{
|
||||||
|
return m_commands.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxCmdList::rollback(size_t upToIndex)
|
||||||
|
{
|
||||||
|
for (size_t i = upToIndex; i > 0; --i)
|
||||||
|
{
|
||||||
|
size_t index = i - 1;
|
||||||
|
|
||||||
|
if (m_commands[index]->canUndo())
|
||||||
|
{
|
||||||
|
m_commands[index]->undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_executedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
42
nodes/services/ntxcmdlist.h
Normal file
42
nodes/services/ntxcmdlist.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef NTX_CMDLIST_H
|
||||||
|
#define NTX_CMDLIST_H
|
||||||
|
|
||||||
|
#include <ntxicommand.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxCmdList : public NtxICommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxCmdList();
|
||||||
|
explicit NtxCmdList(const NtxString& description);
|
||||||
|
~NtxCmdList() override = default;
|
||||||
|
|
||||||
|
bool execute() override;
|
||||||
|
bool undo() override;
|
||||||
|
|
||||||
|
bool canExecute() const override;
|
||||||
|
bool canUndo() const override;
|
||||||
|
|
||||||
|
NtxString getDescription() const override;
|
||||||
|
|
||||||
|
void addCommand(NtxCommandUPtr command);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
size_t count() const;
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<NtxCommandUPtr> m_commands;
|
||||||
|
size_t m_executedCount;
|
||||||
|
|
||||||
|
void rollback(size_t upToIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_CMDLIST_H
|
||||||
55
nodes/services/ntxheadnode.cpp
Normal file
55
nodes/services/ntxheadnode.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <ntxheadnode.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
bool NtxHeadNode::copyNode(NtxNodePtr node)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::cutNode(NtxNodePtr node)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxHeadNode::pasteNode(NtxNodePtr parent, int index)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::addNode(NtxNodePtr parent, NtxNodePtr node, int index)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::deleteNode(NtxNodePtr node)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::hasClipboard() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxHeadNode::clearClipboard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxHeadNode::peekClipboard() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::validateNode(NtxNodePtr node) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxHeadNode::canAddToParent(NtxNodePtr parent, NtxNodePtr child) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
46
nodes/services/ntxheadnode.h
Normal file
46
nodes/services/ntxheadnode.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef NTX_HEADNODE_H
|
||||||
|
#define NTX_HEADNODE_H
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
#include <ntxclipboard.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
class NtxINode;
|
||||||
|
using NtxNodePtr = std::shared_ptr<NtxINode>;
|
||||||
|
|
||||||
|
class NtxHeadNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxHeadNode() = default;
|
||||||
|
~NtxHeadNode() = default;
|
||||||
|
|
||||||
|
NtxHeadNode(const NtxHeadNode&) = delete;
|
||||||
|
NtxHeadNode& operator=(const NtxHeadNode&) = delete;
|
||||||
|
NtxHeadNode(NtxHeadNode&&) = default;
|
||||||
|
NtxHeadNode& operator=(NtxHeadNode&&) = default;
|
||||||
|
|
||||||
|
bool copyNode(NtxNodePtr node);
|
||||||
|
bool cutNode(NtxNodePtr node);
|
||||||
|
NtxNodePtr pasteNode(NtxNodePtr parent, int index = -1);
|
||||||
|
bool addNode(NtxNodePtr parent, NtxNodePtr node, int index = -1);
|
||||||
|
bool deleteNode(NtxNodePtr node);
|
||||||
|
|
||||||
|
bool hasClipboard() const;
|
||||||
|
void clearClipboard();
|
||||||
|
NtxNodePtr peekClipboard() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxClipboard m_clipboard; // ← Eigene Klasse als Member!
|
||||||
|
bool m_isCutOperation{false};
|
||||||
|
|
||||||
|
bool validateNode(NtxNodePtr node) const;
|
||||||
|
bool canAddToParent(NtxNodePtr parent, NtxNodePtr child) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_HEADNODE_H
|
||||||
21
nodes/services/ntxicommand.cpp
Normal file
21
nodes/services/ntxicommand.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include <ntxicommand.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
bool NtxICommand::canUndo() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxString NtxICommand::getDescription() const
|
||||||
|
{
|
||||||
|
return m_description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxICommand::setDescription(const NtxString& description)
|
||||||
|
{
|
||||||
|
m_description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
52
nodes/services/ntxicommand.h
Normal file
52
nodes/services/ntxicommand.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef NTX_ICOMMAND_H
|
||||||
|
#define NTX_ICOMMAND_H
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// Einfache Struct für Clipboard-Eintrag
|
||||||
|
struct NtxNodeEntry
|
||||||
|
{
|
||||||
|
NtxNodePtr clone; // Elternloser Deep-Clone
|
||||||
|
NtxNodeWeakPtr originalParent; // Schwache Referenz für Undo
|
||||||
|
size_t originalPosition; // Position im Original-Parent
|
||||||
|
};
|
||||||
|
|
||||||
|
using NtxNodeEntryList = std::vector<NtxNodeEntry>;
|
||||||
|
|
||||||
|
class NtxICommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NtxICommand() = default;
|
||||||
|
|
||||||
|
virtual bool execute() = 0;
|
||||||
|
virtual bool undo() = 0;
|
||||||
|
|
||||||
|
virtual bool canExecute() const = 0;
|
||||||
|
virtual bool canUndo() const;
|
||||||
|
|
||||||
|
virtual NtxString getDescription() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
NtxICommand() = default;
|
||||||
|
NtxICommand(const NtxICommand&) = default;
|
||||||
|
NtxICommand& operator=(const NtxICommand&) = default;
|
||||||
|
|
||||||
|
void setDescription(const NtxString& description);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxString m_description;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NtxCommandPtr = std::shared_ptr<NtxICommand>;
|
||||||
|
using NtxCommandUPtr = std::unique_ptr<NtxICommand>;
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_ICOMMAND_H
|
||||||
161
nodes/services/ntxnodecommands.cpp
Normal file
161
nodes/services/ntxnodecommands.cpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#include "ntxnodecommands.h"
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxInsertNodeCmd
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxInsertNodeCmd::NtxInsertNodeCmd(NtxNodePtr parent, NtxNodePtr node, size_t index)
|
||||||
|
: m_parent(std::move(parent))
|
||||||
|
, m_node(std::move(node))
|
||||||
|
, m_index(index)
|
||||||
|
{
|
||||||
|
setDescription("Insert node");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxInsertNodeCmd::execute()
|
||||||
|
{
|
||||||
|
if (!canExecute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_index > m_parent->getChildCount())
|
||||||
|
m_index = m_parent->getChildCount();
|
||||||
|
|
||||||
|
m_parent->insertChild(m_index, m_node);
|
||||||
|
m_executed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxInsertNodeCmd::undo()
|
||||||
|
{
|
||||||
|
if (!m_executed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto idx = m_parent->getChildIndex(m_node);
|
||||||
|
if (idx == static_cast<size_t>(-1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_parent->removeChild(idx);
|
||||||
|
m_executed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxInsertNodeCmd::canExecute() const
|
||||||
|
{
|
||||||
|
return m_parent && m_node && !m_executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxRemoveNodeCmd
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxRemoveNodeCmd::NtxRemoveNodeCmd(NtxNodePtr node)
|
||||||
|
: m_node(std::move(node))
|
||||||
|
{
|
||||||
|
setDescription("Remove node");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxRemoveNodeCmd::execute()
|
||||||
|
{
|
||||||
|
if (!canExecute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_parent = m_node->getParent();
|
||||||
|
if (!m_parent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pos = m_node->ownPos();
|
||||||
|
if (!pos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_index = *pos;
|
||||||
|
m_parent->removeChild(m_index);
|
||||||
|
m_executed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxRemoveNodeCmd::undo()
|
||||||
|
{
|
||||||
|
if (!m_executed || !m_parent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_parent->insertChild(m_index, m_node);
|
||||||
|
m_executed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxRemoveNodeCmd::canExecute() const
|
||||||
|
{
|
||||||
|
return m_node && m_node->getParent() && !m_executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxMacroCommand
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxMacroCommand::NtxMacroCommand(const NtxString& description)
|
||||||
|
{
|
||||||
|
setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxCommandUPtr NtxMacroCommand::create(
|
||||||
|
const NtxString& description,
|
||||||
|
std::vector<NtxCommandUPtr> commands)
|
||||||
|
{
|
||||||
|
auto macro = std::make_unique<NtxMacroCommand>(description);
|
||||||
|
for (auto& cmd : commands)
|
||||||
|
macro->add(std::move(cmd));
|
||||||
|
return macro;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxMacroCommand::add(NtxCommandUPtr cmd)
|
||||||
|
{
|
||||||
|
if (cmd)
|
||||||
|
m_commands.push_back(std::move(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMacroCommand::execute()
|
||||||
|
{
|
||||||
|
m_executedCount = 0;
|
||||||
|
|
||||||
|
for (auto& cmd : m_commands)
|
||||||
|
{
|
||||||
|
if (!cmd->execute())
|
||||||
|
{
|
||||||
|
// Rollback: Bereits ausgeführte rückgängig machen
|
||||||
|
for (size_t i = m_executedCount; i > 0; --i)
|
||||||
|
m_commands[i - 1]->undo();
|
||||||
|
m_executedCount = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++m_executedCount;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMacroCommand::undo()
|
||||||
|
{
|
||||||
|
for (size_t i = m_executedCount; i > 0; --i)
|
||||||
|
{
|
||||||
|
if (!m_commands[i - 1]->undo())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_executedCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMacroCommand::canExecute() const
|
||||||
|
{
|
||||||
|
if (m_commands.empty())
|
||||||
|
return false;
|
||||||
|
for (const auto& cmd : m_commands)
|
||||||
|
{
|
||||||
|
if (!cmd->canExecute())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
102
nodes/services/ntxnodecommands.h
Normal file
102
nodes/services/ntxnodecommands.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#ifndef NTX_NODECOMMANDS_H
|
||||||
|
#define NTX_NODECOMMANDS_H
|
||||||
|
|
||||||
|
#include <ntxicommand.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Atomar: Knoten einfügen
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fügt einen Knoten an einer Position ein.
|
||||||
|
* Undo entfernt ihn wieder.
|
||||||
|
*/
|
||||||
|
class NtxInsertNodeCmd : public NtxICommand
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxInsertNodeCmd(NtxNodePtr parent, NtxNodePtr node, size_t index);
|
||||||
|
|
||||||
|
bool execute() override;
|
||||||
|
bool undo() override;
|
||||||
|
bool canExecute() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodePtr m_parent;
|
||||||
|
NtxNodePtr m_node;
|
||||||
|
size_t m_index;
|
||||||
|
bool m_executed{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Atomar: Knoten entfernen
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Entfernt einen Knoten. Merkt sich Parent + Position für Undo.
|
||||||
|
*/
|
||||||
|
class NtxRemoveNodeCmd : public NtxICommand
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit NtxRemoveNodeCmd(NtxNodePtr node);
|
||||||
|
|
||||||
|
bool execute() override;
|
||||||
|
bool undo() override;
|
||||||
|
bool canExecute() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodePtr m_node;
|
||||||
|
NtxNodePtr m_parent;
|
||||||
|
size_t m_index{0};
|
||||||
|
bool m_executed{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Makro-Command (Composite Command)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Führt mehrere Commands als atomare Einheit aus.
|
||||||
|
*
|
||||||
|
* execute(): Alle Commands vorwärts ausführen.
|
||||||
|
* undo(): Alle Commands rückwärts rückgängig machen.
|
||||||
|
* Bei Fehler: Rollback der bereits ausgeführten.
|
||||||
|
*/
|
||||||
|
class NtxMacroCommand : public NtxICommand
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxMacroCommand() = default;
|
||||||
|
explicit NtxMacroCommand(const NtxString& description);
|
||||||
|
|
||||||
|
/// Factory: Erzeugt ein Makro aus einer Liste von Commands.
|
||||||
|
static NtxCommandUPtr create(
|
||||||
|
const NtxString& description,
|
||||||
|
std::vector<NtxCommandUPtr> commands);
|
||||||
|
|
||||||
|
void add(NtxCommandUPtr cmd);
|
||||||
|
|
||||||
|
bool execute() override;
|
||||||
|
bool undo() override;
|
||||||
|
bool canExecute() const override;
|
||||||
|
|
||||||
|
size_t commandCount() const { return m_commands.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<NtxCommandUPtr> m_commands;
|
||||||
|
size_t m_executedCount{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODECOMMANDS_H
|
||||||
209
nodes/services/ntxnodetree.cpp
Normal file
209
nodes/services/ntxnodetree.cpp
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <ntxnodetree.h>
|
||||||
|
#include <ntxnodefactory.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
NtxNodeTree::NtxNodeTree(NtxClassTypeId id)
|
||||||
|
{
|
||||||
|
//m_ctypeId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeTree::clone() const
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
auto cloned = NtxNodeFactory::makeNode(getClassTypeLabel());
|
||||||
|
qDebug() << "Cloning NtxNodeTree with CTypeLabel: NOT DONE: " << getClassTypeLabel().c_str();
|
||||||
|
|
||||||
|
forEachProperty([&](const NtxString& key, const NtxVariant& value) {
|
||||||
|
cloned->setProperty(key, value);
|
||||||
|
});
|
||||||
|
for (size_t i = 0; i < getChildCount(); ++i)
|
||||||
|
cloned->addChild(getChild(i)->clone());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return cloned;
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Command-Ausführung (delegiert an NtxUndoStack)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxNodeTree::execute(NtxCommandUPtr cmd)
|
||||||
|
{
|
||||||
|
if (!cmd || !cmd->canExecute())
|
||||||
|
return false;
|
||||||
|
m_undoStack.push(std::move(cmd));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::undo()
|
||||||
|
{
|
||||||
|
if (!canUndo())
|
||||||
|
return false;
|
||||||
|
m_undoStack.undo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::redo()
|
||||||
|
{
|
||||||
|
if (!canRedo())
|
||||||
|
return false;
|
||||||
|
m_undoStack.redo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::canUndo() const { return m_undoStack.canUndo(); }
|
||||||
|
bool NtxNodeTree::canRedo() const { return m_undoStack.canRedo(); }
|
||||||
|
NtxString NtxNodeTree::undoText() const { return m_undoStack.undoText(); }
|
||||||
|
NtxString NtxNodeTree::redoText() const { return m_undoStack.redoText(); }
|
||||||
|
|
||||||
|
void NtxNodeTree::setClean() { m_undoStack.setClean(); }
|
||||||
|
bool NtxNodeTree::isDirty() const { return !m_undoStack.isClean(); }
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Einzel-Operationen
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxNodeTree::addNode(NtxNodePtr parent, NtxNodePtr node, size_t index)
|
||||||
|
{
|
||||||
|
return execute(std::make_unique<NtxInsertNodeCmd>(
|
||||||
|
std::move(parent), std::move(node), index));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::deleteNode(NtxNodePtr node)
|
||||||
|
{
|
||||||
|
return execute(std::make_unique<NtxRemoveNodeCmd>(std::move(node)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::moveNode(NtxNodePtr node, NtxNodePtr newParent, size_t newIndex)
|
||||||
|
{
|
||||||
|
// Move = Remove + Insert als Makro
|
||||||
|
auto macro = std::make_unique<NtxMacroCommand>("Move node");
|
||||||
|
macro->add(std::make_unique<NtxRemoveNodeCmd>(node));
|
||||||
|
macro->add(std::make_unique<NtxInsertNodeCmd>(
|
||||||
|
std::move(newParent), std::move(node), newIndex));
|
||||||
|
return execute(std::move(macro));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Listen-Operationen (Makro-Commands)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool NtxNodeTree::addNodes(NtxNodePtr parent, const NtxNodeList& nodes, size_t index)
|
||||||
|
{
|
||||||
|
std::vector<NtxCommandUPtr> cmds;
|
||||||
|
cmds.reserve(nodes.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nodes.size(); ++i)
|
||||||
|
{
|
||||||
|
cmds.push_back(std::make_unique<NtxInsertNodeCmd>(
|
||||||
|
parent, nodes[i], index + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return execute(NtxMacroCommand::create(
|
||||||
|
"Insert " + std::to_string(nodes.size()) + " nodes",
|
||||||
|
std::move(cmds)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::deleteNodes(const NtxNodeList& nodes)
|
||||||
|
{
|
||||||
|
std::vector<NtxCommandUPtr> cmds;
|
||||||
|
cmds.reserve(nodes.size());
|
||||||
|
|
||||||
|
// Rückwärts löschen → Indizes bleiben stabil
|
||||||
|
for (auto it = nodes.rbegin(); it != nodes.rend(); ++it)
|
||||||
|
cmds.push_back(std::make_unique<NtxRemoveNodeCmd>(*it));
|
||||||
|
|
||||||
|
return execute(NtxMacroCommand::create(
|
||||||
|
"Delete " + std::to_string(nodes.size()) + " nodes",
|
||||||
|
std::move(cmds)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Clipboard
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
void NtxNodeTree::copyNodes(const NtxNodeList& nodes)
|
||||||
|
{
|
||||||
|
m_clipboard.copyNodes(nodes);
|
||||||
|
m_isCutOperation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxNodeTree::cutNodes(const NtxNodeList& nodes)
|
||||||
|
{
|
||||||
|
m_clipboard.copyNodes(nodes);
|
||||||
|
m_isCutOperation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodeList NtxNodeTree::pasteNodes(NtxNodePtr parent, size_t index)
|
||||||
|
{
|
||||||
|
NtxNodeList pasted = m_clipboard.pasteNodes();
|
||||||
|
if (pasted.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (m_isCutOperation)
|
||||||
|
{
|
||||||
|
// Cut+Paste = Remove originals + Insert clones (als Makro)
|
||||||
|
auto macro = std::make_unique<NtxMacroCommand>(
|
||||||
|
"Paste " + std::to_string(pasted.size()) + " nodes (cut)");
|
||||||
|
|
||||||
|
// 1. Originale entfernen (rückwärts für stabile Indizes)
|
||||||
|
const auto& entries = m_clipboard.getEntries();
|
||||||
|
for (auto it = entries.rbegin(); it != entries.rend(); ++it)
|
||||||
|
{
|
||||||
|
if (auto origParent = it->originalParent.lock())
|
||||||
|
{
|
||||||
|
if (it->originalPosition < origParent->getChildCount())
|
||||||
|
{
|
||||||
|
auto origNode = origParent->getChild(it->originalPosition);
|
||||||
|
if (origNode)
|
||||||
|
macro->add(std::make_unique<NtxRemoveNodeCmd>(origNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Clones einfügen
|
||||||
|
for (size_t i = 0; i < pasted.size(); ++i)
|
||||||
|
{
|
||||||
|
macro->add(std::make_unique<NtxInsertNodeCmd>(
|
||||||
|
parent, pasted[i], index + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!execute(std::move(macro)))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
m_isCutOperation = false;
|
||||||
|
m_clipboard.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Copy+Paste = nur Insert
|
||||||
|
if (!addNodes(parent, pasted, index))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return pasted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeTree::hasClipboard() const
|
||||||
|
{
|
||||||
|
return !m_clipboard.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxNodeTree::clearClipboard()
|
||||||
|
{
|
||||||
|
m_clipboard.clear();
|
||||||
|
m_isCutOperation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
99
nodes/services/ntxnodetree.h
Normal file
99
nodes/services/ntxnodetree.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#ifndef NTX_NODETREE_H
|
||||||
|
#define NTX_NODETREE_H
|
||||||
|
|
||||||
|
#include <ntxnode.h>
|
||||||
|
#include <ntxclipboard.h>
|
||||||
|
#include <ntxundostack.h>
|
||||||
|
#include <ntxnodecommands.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Service-Schicht für Knoten-Operationen mit Undo/Redo.
|
||||||
|
*
|
||||||
|
* Ist selbst ein NtxINode (Composite-Pattern): Kann als Wurzel
|
||||||
|
* eines Teilbaums dienen und in andere NtxNodeTrees eingehängt werden.
|
||||||
|
*
|
||||||
|
* Bietet:
|
||||||
|
* - Command-basierte Baum-Manipulation (add/delete/move)
|
||||||
|
* - Undo/Redo via NtxUndoStack
|
||||||
|
* - Copy/Cut/Paste via NtxClipboard
|
||||||
|
* - Listen-Operationen via NtxMacroCommand
|
||||||
|
*/
|
||||||
|
class NtxNodeTree //: public NtxNode
|
||||||
|
{
|
||||||
|
friend class NtxNodeFactory;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//~NtxNodeTree() override = default;
|
||||||
|
|
||||||
|
NtxNodeTree(const NtxNodeTree&) = delete;
|
||||||
|
NtxNodeTree& operator=(const NtxNodeTree&) = delete;
|
||||||
|
|
||||||
|
NtxNodePtr clone() const;// override;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Command-Ausführung
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/// Führt ein beliebiges Command aus (mit Undo-Support).
|
||||||
|
bool execute(NtxCommandUPtr cmd);
|
||||||
|
|
||||||
|
bool undo();
|
||||||
|
bool redo();
|
||||||
|
bool canUndo() const;
|
||||||
|
bool canRedo() const;
|
||||||
|
NtxString undoText() const;
|
||||||
|
NtxString redoText() const;
|
||||||
|
|
||||||
|
void setClean();
|
||||||
|
bool isDirty() const;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Einzel-Operationen (erzeugen intern Commands)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool addNode(NtxNodePtr parent, NtxNodePtr node, size_t index);
|
||||||
|
bool deleteNode(NtxNodePtr node);
|
||||||
|
bool moveNode(NtxNodePtr node, NtxNodePtr newParent, size_t newIndex);
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Listen-Operationen (erzeugen NtxMacroCommand)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool addNodes(NtxNodePtr parent, const NtxNodeList& nodes, size_t index);
|
||||||
|
bool deleteNodes(const NtxNodeList& nodes);
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Clipboard
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
void copyNodes(const NtxNodeList& nodes);
|
||||||
|
void cutNodes(const NtxNodeList& nodes);
|
||||||
|
NtxNodeList pasteNodes(NtxNodePtr parent, size_t index);
|
||||||
|
|
||||||
|
bool hasClipboard() const;
|
||||||
|
void clearClipboard();
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Zugriff auf Interna
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
NtxUndoStack& undoStack() { return m_undoStack; }
|
||||||
|
const NtxUndoStack& undoStack() const { return m_undoStack; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
NtxNodeTree() = default;
|
||||||
|
explicit NtxNodeTree(NtxClassTypeId id);
|
||||||
|
|
||||||
|
NtxUndoStack m_undoStack;
|
||||||
|
NtxClipboard m_clipboard;
|
||||||
|
bool m_isCutOperation{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_NODETREE_H
|
||||||
209
nodes/services/ntxnodexml.cpp
Normal file
209
nodes/services/ntxnodexml.cpp
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "ntxnodexml.h"
|
||||||
|
#include <ntxnodefactory.h>
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream> // Add this include at the top of the file
|
||||||
|
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
// Rekursive Hilfsfunktion
|
||||||
|
static NtxNodePtr parseElementRecursive(const pugi::xml_node& xmlNode)
|
||||||
|
{
|
||||||
|
if (!xmlNode)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// 1. Tag-Name als ID-String verwenden
|
||||||
|
NtxString tagName = xmlNode.name();
|
||||||
|
|
||||||
|
// Ignoriere spezielle Knotentypen (Kommentare, PCDATA/Text, etc.) wenn sie leer sind
|
||||||
|
// In diesem einfachen Beispiel behandeln wir nur Element-Knoten als echte NtxNodes.
|
||||||
|
if (xmlNode.type() != pugi::node_element)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// 2. Erzeuge Knoten via Factory (nutzt implizit CTypeFactory für Mapping)
|
||||||
|
NtxNodePtr newNode = NtxNodeFactory::makeNode(tagName);
|
||||||
|
if (!newNode)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// 3. Attribute übertragen
|
||||||
|
for (pugi::xml_attribute attr : xmlNode.attributes())
|
||||||
|
{
|
||||||
|
NtxString key = attr.name();
|
||||||
|
|
||||||
|
// Einfache Heuristik für Typen:
|
||||||
|
// PugiXML liefert strings. Wir könnten versuchen zu konvertieren oder alles als String lassen.
|
||||||
|
// Hier: Alles als String speichern, oder versuchen int/double zu parsen.
|
||||||
|
// Fürs erste: Speichern als String, Payload erlaubt mixed types.
|
||||||
|
newNode->set(key, NtxString(attr.value()));
|
||||||
|
|
||||||
|
/* Optional: Intelligentes Parsen
|
||||||
|
if (attr.as_int() != 0 || std::string(attr.value()) == "0")
|
||||||
|
newNode->set(key, attr.as_int());
|
||||||
|
else
|
||||||
|
newNode->set(key, NtxString(attr.value()));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Kinder rekursiv verarbeiten
|
||||||
|
for (pugi::xml_node child : xmlNode.children())
|
||||||
|
{
|
||||||
|
NtxNodePtr childNode = parseElementRecursive(child);
|
||||||
|
if (childNode)
|
||||||
|
{
|
||||||
|
newNode->addChild(childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeXml::loadFromFile(const std::string& filename)
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file(filename.c_str());
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
std::cerr << "XML Load Error: " << result.description() << " in " << filename << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wir nehmen den ersten Root-Knoten (das Wurzelelement)
|
||||||
|
// doc.document_element() gibt das erste Kind vom Typ Element zurück (ignoriert PI, DOCTYPE etc.)
|
||||||
|
return parseElementRecursive(doc.document_element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxNodePtr NtxNodeXml::loadFromString(const std::string& xmlContent)
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_string(xmlContent.c_str());
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
std::cerr << "XML Parse Error: " << result.description() << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseElementRecursive(doc.document_element());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rekursive Hilfsfunktion zum Serialisieren
|
||||||
|
static void serializeNodeRecursive(const NtxNodeCPtr& node, pugi::xml_node& xmlNode)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 1. Tag-Name aus CType-Label holen
|
||||||
|
NtxString tagName = node->getClassTypeLabel();
|
||||||
|
pugi::xml_node childElement = xmlNode.append_child(tagName.c_str());
|
||||||
|
|
||||||
|
// 2. Alle Attribute aus dem Payload schreiben
|
||||||
|
node->forEachProperty([&childElement](const NtxString& key, const NtxVariant& value)
|
||||||
|
{
|
||||||
|
// Konvertiere NtxVariant zu String und setze als XML-Attribut
|
||||||
|
std::visit([&childElement, &key](auto&& arg)
|
||||||
|
{
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, std::monostate>)
|
||||||
|
{
|
||||||
|
// Überspringe leere Werte
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, NtxString>)
|
||||||
|
{
|
||||||
|
childElement.append_attribute(key.c_str()).set_value(arg.c_str());
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, int>)
|
||||||
|
{
|
||||||
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, double>)
|
||||||
|
{
|
||||||
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, bool>)
|
||||||
|
{
|
||||||
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||||||
|
}
|
||||||
|
}, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Kinder rekursiv verarbeiten
|
||||||
|
for (size_t i = 0; i < node->getChildCount(); ++i)
|
||||||
|
{
|
||||||
|
NtxNodeCPtr child = node->getChild(i);
|
||||||
|
if (child)
|
||||||
|
{
|
||||||
|
serializeNodeRecursive(child, childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeXml::saveToFile(const NtxNodePtr& node, const std::string& filename)
|
||||||
|
{
|
||||||
|
return saveToFile(std::const_pointer_cast<const NtxINode>(node), filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxNodeXml::saveToFile(const NtxNodeCPtr& node, const std::string& filename)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot save null node to file: " << filename << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
// XML-Deklaration hinzufügen
|
||||||
|
pugi::xml_node declaration = doc.prepend_child(pugi::node_declaration);
|
||||||
|
declaration.append_attribute("version") = "1.0";
|
||||||
|
declaration.append_attribute("encoding") = "UTF-8";
|
||||||
|
|
||||||
|
// Root-Node serialisieren
|
||||||
|
serializeNodeRecursive(node, doc);
|
||||||
|
|
||||||
|
// In Datei speichern mit Formatierung
|
||||||
|
bool success = doc.save_file(filename.c_str(), " ", pugi::format_default, pugi::encoding_utf8);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to save XML to file: " << filename << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NtxNodeXml::saveToString(const NtxNodePtr& node)
|
||||||
|
{
|
||||||
|
return saveToString(std::const_pointer_cast<const NtxINode>(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NtxNodeXml::saveToString(const NtxNodeCPtr& node)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot serialize null node to string" << std::endl;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
// XML-Deklaration hinzufügen
|
||||||
|
pugi::xml_node declaration = doc.prepend_child(pugi::node_declaration);
|
||||||
|
declaration.append_attribute("version") = "1.0";
|
||||||
|
declaration.append_attribute("encoding") = "UTF-8";
|
||||||
|
|
||||||
|
// Root-Node serialisieren
|
||||||
|
serializeNodeRecursive(node, doc);
|
||||||
|
|
||||||
|
// In String konvertieren
|
||||||
|
std::ostringstream oss;
|
||||||
|
doc.save(oss, " ", pugi::format_default, pugi::encoding_utf8);
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
27
nodes/services/ntxnodexml.h
Normal file
27
nodes/services/ntxnodexml.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef NTX_NODEXML_H
|
||||||
|
#define NTX_NODEXML_H
|
||||||
|
|
||||||
|
#include <ntxinode.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
class NtxNodeXml
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static NtxNodePtr loadFromFile(const std::string& filename);
|
||||||
|
static NtxNodePtr loadFromString(const std::string& xmlContent);
|
||||||
|
|
||||||
|
// Methoden zum Speichern
|
||||||
|
static bool saveToFile(const NtxNodePtr& node, const std::string& filename);
|
||||||
|
static bool saveToFile(const NtxNodeCPtr& node, const std::string& filename);
|
||||||
|
|
||||||
|
static std::string saveToString(const NtxNodePtr& node);
|
||||||
|
static std::string saveToString(const NtxNodeCPtr& node);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NTX_NODEXML_H
|
||||||
156
nodes/services/ntxundostack.cpp
Normal file
156
nodes/services/ntxundostack.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include <ntxundostack.h>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
NtxUndoStack::NtxUndoStack()
|
||||||
|
: m_index(0)
|
||||||
|
, m_cleanIndex(0)
|
||||||
|
, m_undoLimit(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::push(NtxCommandUPtr command)
|
||||||
|
{
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!command->canExecute())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!command->execute())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_index < static_cast<int>(m_commands.size()))
|
||||||
|
{
|
||||||
|
m_commands.erase(m_commands.begin() + m_index, m_commands.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_commands.push_back(std::move(command));
|
||||||
|
m_index++;
|
||||||
|
|
||||||
|
if (m_cleanIndex > m_index)
|
||||||
|
m_cleanIndex = -1;
|
||||||
|
|
||||||
|
checkUndoLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxUndoStack::canUndo() const
|
||||||
|
{
|
||||||
|
return m_index > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxUndoStack::canRedo() const
|
||||||
|
{
|
||||||
|
return m_index < static_cast<int>(m_commands.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::undo()
|
||||||
|
{
|
||||||
|
if (!canUndo())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_index--;
|
||||||
|
|
||||||
|
if (m_commands[m_index]->canUndo())
|
||||||
|
{
|
||||||
|
m_commands[m_index]->undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::redo()
|
||||||
|
{
|
||||||
|
if (!canRedo())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_commands[m_index]->canExecute())
|
||||||
|
{
|
||||||
|
m_commands[m_index]->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NtxUndoStack::index() const
|
||||||
|
{
|
||||||
|
return m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NtxUndoStack::count() const
|
||||||
|
{
|
||||||
|
return static_cast<int>(m_commands.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxString NtxUndoStack::undoText() const
|
||||||
|
{
|
||||||
|
if (!canUndo())
|
||||||
|
return NtxString();
|
||||||
|
|
||||||
|
return m_commands[m_index - 1]->getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxString NtxUndoStack::redoText() const
|
||||||
|
{
|
||||||
|
if (!canRedo())
|
||||||
|
return NtxString();
|
||||||
|
|
||||||
|
return m_commands[m_index]->getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::clear()
|
||||||
|
{
|
||||||
|
m_commands.clear();
|
||||||
|
m_index = 0;
|
||||||
|
m_cleanIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::setClean()
|
||||||
|
{
|
||||||
|
m_cleanIndex = m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxUndoStack::isClean() const
|
||||||
|
{
|
||||||
|
return m_cleanIndex == m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::setUndoLimit(int limit)
|
||||||
|
{
|
||||||
|
m_undoLimit = limit;
|
||||||
|
checkUndoLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
int NtxUndoStack::undoLimit() const
|
||||||
|
{
|
||||||
|
return m_undoLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtxICommand* NtxUndoStack::command(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= static_cast<int>(m_commands.size()))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return m_commands[index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxUndoStack::checkUndoLimit()
|
||||||
|
{
|
||||||
|
if (m_undoLimit <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int deleteCount = m_index - m_undoLimit;
|
||||||
|
if (deleteCount <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_commands.erase(m_commands.begin(), m_commands.begin() + deleteCount);
|
||||||
|
m_index -= deleteCount;
|
||||||
|
|
||||||
|
if (m_cleanIndex != -1)
|
||||||
|
{
|
||||||
|
m_cleanIndex -= deleteCount;
|
||||||
|
if (m_cleanIndex < 0)
|
||||||
|
m_cleanIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
56
nodes/services/ntxundostack.h
Normal file
56
nodes/services/ntxundostack.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef NTX_UNDO_STACK_H
|
||||||
|
#define NTX_UNDO_STACK_H
|
||||||
|
|
||||||
|
#include <ntxicommand.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
class NtxUndoStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxUndoStack();
|
||||||
|
~NtxUndoStack() = default;
|
||||||
|
|
||||||
|
NtxUndoStack(const NtxUndoStack&) = delete;
|
||||||
|
NtxUndoStack& operator=(const NtxUndoStack&) = delete;
|
||||||
|
|
||||||
|
void push(NtxCommandUPtr command);
|
||||||
|
|
||||||
|
bool canUndo() const;
|
||||||
|
bool canRedo() const;
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
|
||||||
|
int index() const;
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
NtxString undoText() const;
|
||||||
|
NtxString redoText() const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void setClean();
|
||||||
|
bool isClean() const;
|
||||||
|
|
||||||
|
void setUndoLimit(int limit);
|
||||||
|
int undoLimit() const;
|
||||||
|
|
||||||
|
const NtxICommand* command(int index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<NtxCommandUPtr> m_commands;
|
||||||
|
int m_index;
|
||||||
|
int m_cleanIndex;
|
||||||
|
int m_undoLimit;
|
||||||
|
|
||||||
|
void checkUndoLimit();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_UNDO_STACK_H
|
||||||
88
nodes/util/ntxmapindex.cpp
Normal file
88
nodes/util/ntxmapindex.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#include <ntxmapindex.h>
|
||||||
|
#include <ranges> // Zwingend erforderlich für std::views und std::ranges::to
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
// --- Lookup ---
|
||||||
|
|
||||||
|
std::optional<size_t> NtxMapIndex::indexOf(const std::string& key) const noexcept
|
||||||
|
{
|
||||||
|
auto it = m_keyToIndex.find(key);
|
||||||
|
if (it != m_keyToIndex.end())
|
||||||
|
return it->second;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMapIndex::containsKey(const std::string& key) const noexcept
|
||||||
|
{
|
||||||
|
return m_keyToIndex.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Modification ---
|
||||||
|
|
||||||
|
void NtxMapIndex::addKey(const std::string& key, size_t index)
|
||||||
|
{
|
||||||
|
m_keyToIndex[key] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxMapIndex::removeAt(size_t index)
|
||||||
|
{
|
||||||
|
auto it = m_keyToIndex.begin();
|
||||||
|
while (it != m_keyToIndex.end())
|
||||||
|
{
|
||||||
|
if (it->second == index)
|
||||||
|
it = m_keyToIndex.erase(it);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (it->second > index)
|
||||||
|
it->second--;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMapIndex::removeKey(const std::string& key)
|
||||||
|
{
|
||||||
|
return m_keyToIndex.erase(key) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMapIndex::replaceKey(const std::string& oldKey, const std::string& newKey)
|
||||||
|
{
|
||||||
|
auto it = m_keyToIndex.find(oldKey);
|
||||||
|
if (it == m_keyToIndex.end() || oldKey == newKey)
|
||||||
|
return false;
|
||||||
|
if (m_keyToIndex.contains(newKey))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t idx = it->second;
|
||||||
|
m_keyToIndex.erase(it);
|
||||||
|
m_keyToIndex[newKey] = idx;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMapIndex::addAlias(const std::string& existingKey, const std::string& alias)
|
||||||
|
{
|
||||||
|
auto idx = indexOf(existingKey);
|
||||||
|
if (!idx || containsKey(alias))
|
||||||
|
return false;
|
||||||
|
m_keyToIndex[alias] = *idx;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NtxMapIndex::size() const noexcept
|
||||||
|
{
|
||||||
|
return m_keyToIndex.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NtxMapIndex::empty() const noexcept
|
||||||
|
{
|
||||||
|
return m_keyToIndex.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NtxMapIndex::clear() noexcept
|
||||||
|
{
|
||||||
|
m_keyToIndex.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
61
nodes/util/ntxmapindex.h
Normal file
61
nodes/util/ntxmapindex.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef NTX_MAPINDEX_H
|
||||||
|
#define NTX_MAPINDEX_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <optional>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bidirektionaler Index: String-Key → size_t Position.
|
||||||
|
*
|
||||||
|
* Verwendet Komposition statt Vererbung.
|
||||||
|
* Key → Index: O(1) via unordered_map.
|
||||||
|
*/
|
||||||
|
class NtxMapIndex
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NtxMapIndex() = default;
|
||||||
|
|
||||||
|
// --- Lookup ---
|
||||||
|
std::optional<size_t> indexOf(const std::string& key) const noexcept;
|
||||||
|
bool containsKey(const std::string& key) const noexcept;
|
||||||
|
|
||||||
|
// --- Iteration ---
|
||||||
|
|
||||||
|
/// Iteriert über alle Key→Index Paare.
|
||||||
|
/// Template muss im Header bleiben.
|
||||||
|
template <typename Fn>
|
||||||
|
void forEachKey(Fn&& fn) const
|
||||||
|
{
|
||||||
|
for (const auto& [key, idx] : m_keyToIndex)
|
||||||
|
fn(key, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Modification ---
|
||||||
|
void addKey(const std::string& key, size_t index);
|
||||||
|
void removeAt(size_t index);
|
||||||
|
bool removeKey(const std::string& key);
|
||||||
|
bool replaceKey(const std::string& oldKey, const std::string& newKey);
|
||||||
|
bool addAlias(const std::string& existingKey, const std::string& alias);
|
||||||
|
|
||||||
|
size_t size() const noexcept;
|
||||||
|
bool empty() const noexcept;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unordered_map<std::string, size_t> m_keyToIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NtxMapIndexPtr = std::shared_ptr<NtxMapIndex>;
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_MAPINDEX_H
|
||||||
177
nodes/util/ntxmaptor.h
Normal file
177
nodes/util/ntxmaptor.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#ifndef NTX_MAPTOR_H
|
||||||
|
#define NTX_MAPTOR_H
|
||||||
|
|
||||||
|
#include "ntxmapindex.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ntx
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generischer Maptor (Vector + Map), adaptiert an das NtxIPayload-Interface.
|
||||||
|
*
|
||||||
|
* Entfernt die STL-Container-Schnittstelle (push_back, begin, end, etc.) zugunsten
|
||||||
|
* der expliziten Payload-Methodik (setProperty, getProperty, etc.).
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class NtxMaptor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
using NtxMapIndexPtr = std::shared_ptr<NtxMapIndex>;
|
||||||
|
|
||||||
|
// Zugriff auf Iteratoren für Range-Based Loops (via valuesBegin/End)
|
||||||
|
using iterator = typename std::vector<T>::iterator;
|
||||||
|
using const_iterator = typename std::vector<T>::const_iterator;
|
||||||
|
|
||||||
|
// --- Konstruktoren ---
|
||||||
|
|
||||||
|
NtxMaptor()
|
||||||
|
: m_index(std::make_shared<NtxMapIndex>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit NtxMaptor(const NtxMapIndexPtr& index)
|
||||||
|
{
|
||||||
|
if (!index)
|
||||||
|
throw std::invalid_argument("NtxMaptor: index must not be null");
|
||||||
|
m_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtxMaptor(const NtxMaptor&) = default;
|
||||||
|
NtxMaptor& operator=(const NtxMaptor&) = default;
|
||||||
|
NtxMaptor(NtxMaptor&&) noexcept = default;
|
||||||
|
NtxMaptor& operator=(NtxMaptor&&) noexcept = default;
|
||||||
|
|
||||||
|
virtual ~NtxMaptor() = default;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// NtxIPayload Interface-Adaption
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
// --- Kapazität ---
|
||||||
|
|
||||||
|
[[nodiscard]] size_t getPropertyCount() const noexcept
|
||||||
|
{
|
||||||
|
return m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearProperties()
|
||||||
|
{
|
||||||
|
m_data.clear();
|
||||||
|
m_index->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Key-basierter Zugriff ---
|
||||||
|
|
||||||
|
bool hasProperty(const std::string& key) const
|
||||||
|
{
|
||||||
|
return m_index->containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setzt oder fügt einen Wert hinzu (Upsert).
|
||||||
|
* Wenn der Key existiert, wird der Wert überschrieben.
|
||||||
|
* Wenn nicht, wird er angehängt.
|
||||||
|
*/
|
||||||
|
void setProperty(const std::string& key, const T& value)
|
||||||
|
{
|
||||||
|
auto idx = m_index->indexOf(key);
|
||||||
|
if (idx)
|
||||||
|
{
|
||||||
|
m_data[*idx] = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_index->addKey(key, m_data.size());
|
||||||
|
m_data.push_back(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Deducing this" für const/non-const getProperty(key)
|
||||||
|
template <typename Self>
|
||||||
|
auto&& getProperty(this Self&& self, const std::string& key)
|
||||||
|
{
|
||||||
|
auto idx = self.m_index->indexOf(key);
|
||||||
|
if (!idx)
|
||||||
|
throw std::out_of_range("NtxMaptor: Key not found: " + key);
|
||||||
|
return std::forward<Self>(self).m_data[*idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeProperty(const std::string& key)
|
||||||
|
{
|
||||||
|
auto idx = m_index->indexOf(key);
|
||||||
|
if (!idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t index = *idx;
|
||||||
|
// Index entfernen und nachfolgende Indizes im Index-Objekt korrigieren
|
||||||
|
m_index->removeAt(index);
|
||||||
|
// Daten aus Vektor entfernen
|
||||||
|
m_data.erase(m_data.begin() + static_cast<std::ptrdiff_t>(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Index-basierter Zugriff ---
|
||||||
|
|
||||||
|
bool hasProperty(size_t index) const
|
||||||
|
{
|
||||||
|
return index < m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProperty(size_t index, const T& value)
|
||||||
|
{
|
||||||
|
if (index >= m_data.size())
|
||||||
|
{
|
||||||
|
throw std::out_of_range("NtxMaptor: Index out of range");
|
||||||
|
}
|
||||||
|
m_data[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Deducing this" für const/non-const getProperty(index)
|
||||||
|
template <typename Self>
|
||||||
|
auto&& getProperty(this Self&& self, size_t index)
|
||||||
|
{
|
||||||
|
return std::forward<Self>(self).m_data.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Iteratoren (NtxIPayload Style) ---
|
||||||
|
|
||||||
|
// Erlaubt das Iterieren über die internen Werte
|
||||||
|
template <typename Self>
|
||||||
|
auto begin(this Self&& self) { return std::forward<Self>(self).m_data.begin(); }
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
auto end(this Self&& self) { return std::forward<Self>(self).m_data.end(); }
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Erweiterte Index-Funktionen (Alias, Replace)
|
||||||
|
// Diese bleiben erhalten, da sie spezifische NtxMapIndex Features sind
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bool replaceKey(const std::string& oldKey, const std::string& newKey)
|
||||||
|
{
|
||||||
|
return m_index->replaceKey(oldKey, newKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAlias(const std::string& key, const std::string& alias)
|
||||||
|
{
|
||||||
|
return m_index->addAlias(key, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<T> m_data;
|
||||||
|
NtxMapIndexPtr m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ntx
|
||||||
|
|
||||||
|
#endif // NTX_MAPTOR_H
|
||||||
394
nodes/xml/ntc_dummy.xml
Normal file
394
nodes/xml/ntc_dummy.xml
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
<TestDataList Fitze="fatze">
|
||||||
|
<TestData Name="NT600-Testprojekt_min">
|
||||||
|
<TestDataBase/>
|
||||||
|
<TPNamesData>
|
||||||
|
<TPNames Name="NT600-Testprojekt_min">
|
||||||
|
<LogicalTpCount>4096</LogicalTpCount>
|
||||||
|
</TPNames>
|
||||||
|
</TPNamesData>
|
||||||
|
<ParaSetList>
|
||||||
|
<ParaSet Hardware="MT40" MeasType="Continuity">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="20"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT40" MeasType="Short">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="20"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeShortTest" Value="Fast"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT1500DC" MeasType="Insulation">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RHigh" Value="1e+006"/>
|
||||||
|
<ParaValue Type="U" Value="100"/>
|
||||||
|
<ParaValue Type="TDwell" Value="0"/>
|
||||||
|
<ParaValue Type="TRise" Value="0"/>
|
||||||
|
<ParaValue Type="IsoVariation" Value="0"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeISO" Value="ISO"/>
|
||||||
|
<ParaValue Type="ModeShortTest" Value="NormalFast"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ShortenMatrixAB" Value="false"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT2000" MeasType="BreakdownDC">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="U" Value="100"/>
|
||||||
|
<ParaValue Type="TDwell" Value="1"/>
|
||||||
|
<ParaValue Type="TRise" Value="0.02"/>
|
||||||
|
<ParaValue Type="TFall" Value="0"/>
|
||||||
|
<ParaValue Type="Imax" Value="0"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
|
||||||
|
<ParaValue Type="ModeShortTest" Value="NormalExact"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ShortenMatrixAB" Value="false"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT40" MeasType="Detection">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="20"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="FastCompOpenTest" Value="false"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
</ParaSetList>
|
||||||
|
<SegmentList>
|
||||||
|
<Segment Name="Segment 1" Enabled="Yes">
|
||||||
|
<SegmentMessage Text="" ConfirmationType="1" DisplayTime="1" Image=""/>
|
||||||
|
<QueryList>
|
||||||
|
<Query QueryMessage="TODO: to complete" QueryType="-1" FailedText=""/>
|
||||||
|
</QueryList>
|
||||||
|
<TestControls>
|
||||||
|
<General>
|
||||||
|
<UseSegmentParameters>On</UseSegmentParameters>
|
||||||
|
</General>
|
||||||
|
<EnabledTestSteps>
|
||||||
|
<Continuity>On</Continuity>
|
||||||
|
<Short>On</Short>
|
||||||
|
<Insulation>Off</Insulation>
|
||||||
|
<Breakdown>Off</Breakdown>
|
||||||
|
<Detection>Off</Detection>
|
||||||
|
<Component>On</Component>
|
||||||
|
</EnabledTestSteps>
|
||||||
|
<AdvancedSettings>
|
||||||
|
<Monitoring>Default</Monitoring>
|
||||||
|
<LooseContact>Default</LooseContact>
|
||||||
|
<StimulusBeforeMessage>Default</StimulusBeforeMessage>
|
||||||
|
<UseStimulusCards>Default</UseStimulusCards>
|
||||||
|
<ErrorStopComponent>Default</ErrorStopComponent>
|
||||||
|
<OptimizedLinkTest>Default</OptimizedLinkTest>
|
||||||
|
<AdvancedSwitchOpenTest>Default</AdvancedSwitchOpenTest>
|
||||||
|
<SegmentRetest>Default</SegmentRetest>
|
||||||
|
</AdvancedSettings>
|
||||||
|
</TestControls>
|
||||||
|
<ParaSetList>
|
||||||
|
<ParaSet Hardware="MT20" MeasType="Continuity">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="12"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeMeasBidir" Value="true"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
<ParaValue Type="PreciseMode" Value="false" PleaseFindMe="treasure"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT20" MeasType="Short">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="12"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeShortTest" Value="Fast"/>
|
||||||
|
<ParaValue Type="ModeMeasBidir" Value="true"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
<ParaValue Type="PreciseMode" Value="false"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT1500DC" MeasType="Insulation">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RHigh" Value="1e+006"/>
|
||||||
|
<ParaValue Type="U" Value="100"/>
|
||||||
|
<ParaValue Type="TDwell" Value="0"/>
|
||||||
|
<ParaValue Type="TRise" Value="0"/>
|
||||||
|
<ParaValue Type="IsoVariation" Value="0"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeISO" Value="ISO"/>
|
||||||
|
<ParaValue Type="ModeShortTest" Value="NormalFast"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ShortenMatrixAB" Value="false"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT2000" MeasType="BreakdownDC">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="U" Value="100"/>
|
||||||
|
<ParaValue Type="TDwell" Value="1"/>
|
||||||
|
<ParaValue Type="TRise" Value="0.02"/>
|
||||||
|
<ParaValue Type="TFall" Value="0"/>
|
||||||
|
<ParaValue Type="Imax" Value="0"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
|
||||||
|
<ParaValue Type="ModeShortTest" Value="NormalExact"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ShortenMatrixAB" Value="false"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT40" MeasType="Detection">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="20"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="FastCompOpenTest" Value="false"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
</ParaSetList>
|
||||||
|
<NetList>
|
||||||
|
<Net Name="Net 1">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>1</TestPoint>
|
||||||
|
<TestPoint>22</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 2">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>2</TestPoint>
|
||||||
|
<TestPoint>29</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 3">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>3</TestPoint>
|
||||||
|
<TestPoint>26</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 4">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>4</TestPoint>
|
||||||
|
<TestPoint>27</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 5">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>5</TestPoint>
|
||||||
|
<TestPoint>28</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 6">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>6</TestPoint>
|
||||||
|
<TestPoint>21</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 7">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>7</TestPoint>
|
||||||
|
<TestPoint>30</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
<Net Name="Net 8">
|
||||||
|
<SubNet Name="*Sub 1">
|
||||||
|
<TestPoint>8</TestPoint>
|
||||||
|
<TestPoint>31</TestPoint>
|
||||||
|
</SubNet>
|
||||||
|
</Net>
|
||||||
|
</NetList>
|
||||||
|
<PrimList>
|
||||||
|
<Prim>
|
||||||
|
<PrimGroup Name="Grp 1">
|
||||||
|
<TestPoint>1</TestPoint>
|
||||||
|
<TestPoint>2</TestPoint>
|
||||||
|
<TestPoint>3</TestPoint>
|
||||||
|
<TestPoint>4</TestPoint>
|
||||||
|
<TestPoint>5</TestPoint>
|
||||||
|
<TestPoint>6</TestPoint>
|
||||||
|
<TestPoint>7</TestPoint>
|
||||||
|
<TestPoint>8</TestPoint>
|
||||||
|
<TestPoint>9</TestPoint>
|
||||||
|
<TestPoint>10</TestPoint>
|
||||||
|
<TestPoint>11</TestPoint>
|
||||||
|
<TestPoint>12</TestPoint>
|
||||||
|
<TestPoint>13</TestPoint>
|
||||||
|
<TestPoint>14</TestPoint>
|
||||||
|
<TestPoint>15</TestPoint>
|
||||||
|
<TestPoint>16</TestPoint>
|
||||||
|
<TestPoint>17</TestPoint>
|
||||||
|
<TestPoint>18</TestPoint>
|
||||||
|
<TestPoint>19</TestPoint>
|
||||||
|
<TestPoint>20</TestPoint>
|
||||||
|
<TestPoint>23</TestPoint>
|
||||||
|
<TestPoint>24</TestPoint>
|
||||||
|
<TestPoint>25</TestPoint>
|
||||||
|
<TestPoint>32</TestPoint>
|
||||||
|
<TestPoint>33</TestPoint>
|
||||||
|
<TestPoint>34</TestPoint>
|
||||||
|
<TestPoint>35</TestPoint>
|
||||||
|
<TestPoint>36</TestPoint>
|
||||||
|
<TestPoint>37</TestPoint>
|
||||||
|
<TestPoint>38</TestPoint>
|
||||||
|
<TestPoint>41</TestPoint>
|
||||||
|
<TestPoint>42</TestPoint>
|
||||||
|
<TestPoint>43</TestPoint>
|
||||||
|
<TestPoint>44</TestPoint>
|
||||||
|
<TestPoint>45</TestPoint>
|
||||||
|
<TestPoint>46</TestPoint>
|
||||||
|
<TestPoint>47</TestPoint>
|
||||||
|
<TestPoint>48</TestPoint>
|
||||||
|
<TestPoint>49</TestPoint>
|
||||||
|
<TestPoint>50</TestPoint>
|
||||||
|
<TestPoint>51</TestPoint>
|
||||||
|
<TestPoint>52</TestPoint>
|
||||||
|
<TestPoint>53</TestPoint>
|
||||||
|
<TestPoint>54</TestPoint>
|
||||||
|
<TestPoint>55</TestPoint>
|
||||||
|
<TestPoint>56</TestPoint>
|
||||||
|
<TestPoint>57</TestPoint>
|
||||||
|
<TestPoint>58</TestPoint>
|
||||||
|
<TestPoint>59</TestPoint>
|
||||||
|
<TestPoint>60</TestPoint>
|
||||||
|
<TestPoint>61</TestPoint>
|
||||||
|
<TestPoint>62</TestPoint>
|
||||||
|
<TestPoint>63</TestPoint>
|
||||||
|
<TestPoint>64</TestPoint>
|
||||||
|
</PrimGroup>
|
||||||
|
</Prim>
|
||||||
|
</PrimList>
|
||||||
|
<ComponentListData>
|
||||||
|
<ComponentList Type="Resistor">
|
||||||
|
<TypeParameters>
|
||||||
|
<ParaSet Hardware="MT20" MeasType="MfResistance">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="U" Value="12"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeMeasBidir" Value="true"/>
|
||||||
|
<ParaValue Type="PreciseMode" Value="false"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
</TypeParameters>
|
||||||
|
<Component Name="R 1" Type="Resistor" FromTestPoint="8" ToTestPoint="39" PlusTolerance="10" MinusTolerance="10" Value="1013.27" Offset="0"/>
|
||||||
|
<Component Name="R 2" Type="Resistor" FromTestPoint="10" ToTestPoint="39" PlusTolerance="10" MinusTolerance="10" Value="1013.27" Offset="0"/>
|
||||||
|
<Component Name="FAT_RESISTOR" Type="Resistor" FromTestPoint="11" ToTestPoint="39" PlusTolerance="10" MinusTolerance="10" Value="1013.27" Offset="0"/>
|
||||||
|
</ComponentList>
|
||||||
|
<ComponentList Type="Diode">
|
||||||
|
<TypeParameters>
|
||||||
|
<ParaSet Hardware="MT20" MeasType="MfDiode">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="U" Value="12"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="PreciseMode" Value="false"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
</TypeParameters>
|
||||||
|
<Component Name="D 1" Type="Diode" FromTestPoint="7" ToTestPoint="40" PlusTolerance="10" MinusTolerance="10" MaxCurrent="0.001" ForwardVoltage="0.63" ReverseVoltage="12"/>
|
||||||
|
</ComponentList>
|
||||||
|
</ComponentListData>
|
||||||
|
</Segment>
|
||||||
|
</SegmentList>
|
||||||
|
</TestData>
|
||||||
|
</TestDataList>
|
||||||
|
<TestSysCfgData>
|
||||||
|
<TestSysCfg SystemVariant="CX">
|
||||||
|
<TesterConfig>
|
||||||
|
<TestSysBase>
|
||||||
|
<TesterDescription Name="Virtual NT " Device="22701 N000 0000" Calibration="2024-08-22" Production="" SystemType="DBTCab"/>
|
||||||
|
<SoftwareVersion Version="673" Release="E"/>
|
||||||
|
<Connection Type="TCPIP" Settings="127.0.0.1:2048"/>
|
||||||
|
<UserPort Inputs="0" Outputs="0"/>
|
||||||
|
<Options Kelvin="Yes" ExtVoltDetect="Yes" AMC="Yes"/>
|
||||||
|
</TestSysBase>
|
||||||
|
<MeasHardwareList>
|
||||||
|
<MeasHardware Name="MT40" SerialNumber="0" Calibration="1971-01-01" Production="2025-10-24"/>
|
||||||
|
<MeasHardware Name="MT20" SerialNumber="0" Calibration="1971-01-01" Production="2025-10-24"/>
|
||||||
|
<MeasHardware Name="MT1500DC" SerialNumber="0" Calibration="1971-01-01" Production="2025-10-24"/>
|
||||||
|
<MeasHardware Name="MT2000" SerialNumber="0" Calibration="1971-01-01" Production="2025-10-24"/>
|
||||||
|
<MeasHardware Name="MT_EXT" SerialNumber="0" Calibration="1971-01-01" Production="2025-10-24"/>
|
||||||
|
</MeasHardwareList>
|
||||||
|
<MatrixDescList>
|
||||||
|
<MatrixDesc Type="RM80" TPCount="64" ConnectorType="DIN41612-64" KelvinMode="" MaxVoltAC="1060" MaxVoltDC="1500" MaxCurrAC="2" MaxCurrDC="2" BounceTime="0.007" UsageType="TestPoint"/>
|
||||||
|
</MatrixDescList>
|
||||||
|
<RackList Gaps="No">
|
||||||
|
<Rack Number="1">
|
||||||
|
<MatrixList>
|
||||||
|
<Matrix Type="RM80" Slot="1" Kelvin="No" Start="1"/>
|
||||||
|
</MatrixList>
|
||||||
|
</Rack>
|
||||||
|
</RackList>
|
||||||
|
</TesterConfig>
|
||||||
|
<ControllerLst>
|
||||||
|
<Controller Type="TPU" Node="0"/>
|
||||||
|
</ControllerLst>
|
||||||
|
<SystemData/>
|
||||||
|
<Miscellaneous>
|
||||||
|
<SafetyFunctions ExtVoltDetect="Off" GndScan="Off">
|
||||||
|
<ParaSetList>
|
||||||
|
<ParaSet Hardware="MT_EXT" MeasType="ExtVoltage">
|
||||||
|
<MeasPara/>
|
||||||
|
</ParaSet>
|
||||||
|
<ParaSet Hardware="MT40" MeasType="GndScan">
|
||||||
|
<MeasPara>
|
||||||
|
<ParaValue Type="RLow" Value="100"/>
|
||||||
|
<ParaValue Type="RHigh" Value="20000"/>
|
||||||
|
<ParaValue Type="U" Value="20"/>
|
||||||
|
<ParaValue Type="I" Value="0.1"/>
|
||||||
|
<ParaValue Type="TDelay" Value="0.0005"/>
|
||||||
|
</MeasPara>
|
||||||
|
<MeasControl>
|
||||||
|
<ParaValue Type="ModeShortTest" Value="Fast"/>
|
||||||
|
<ParaValue Type="ModeGroupTest" Value="GG"/>
|
||||||
|
<ParaValue Type="ModeGroupFaultLocation" Value="None"/>
|
||||||
|
<ParaValue Type="ModeClassicFaultLocation" Value="All"/>
|
||||||
|
</MeasControl>
|
||||||
|
</ParaSet>
|
||||||
|
</ParaSetList>
|
||||||
|
</SafetyFunctions>
|
||||||
|
</Miscellaneous>
|
||||||
|
</TestSysCfg>
|
||||||
|
</TestSysCfgData>
|
||||||
140
nodes/xml/ntc_person.xml
Normal file
140
nodes/xml/ntc_person.xml
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Persons>
|
||||||
|
<Person Name="Anna Müller" Age="28" Email="anna.mueller@example.de" Salary="52000.50" Active="true">
|
||||||
|
<Address Street="Lindenstraße 12" City="Berlin" ZipCode="10115"/>
|
||||||
|
<Sibling Name="Max Müller" Age="17" EducationalInstitution="Friedrich-Schiller-Gymnasium">
|
||||||
|
<Friend Name="Leon Berger"/>
|
||||||
|
<Friend Name="Niklas Stein"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Lisa Müller" Age="14" EducationalInstitution="Albert-Einstein-Realschule">
|
||||||
|
<Friend Name="Mia Scholz"/>
|
||||||
|
<Friend Name="Lena Kraft"/>
|
||||||
|
<Friend Name="Sophie Engel"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Thomas Schmidt" Age="45" Email="t.schmidt@example.de" Salary="78000.00" Active="true">
|
||||||
|
<Sibling Name="Petra Schmidt" Age="19" EducationalInstitution="Goethe-Universität Frankfurt">
|
||||||
|
<Friend Name="Carolin Seidel"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Maria Weber" Age="33" Email="m.weber@example.de" Salary="61500.75" Active="true">
|
||||||
|
<Sibling Name="Klaus Weber" Age="16" EducationalInstitution="Max-Planck-Gymnasium"/>
|
||||||
|
<Sibling Name="Eva Weber" Age="12" EducationalInstitution="Heinrich-Heine-Gesamtschule">
|
||||||
|
<Friend Name="Hannah Vogel"/>
|
||||||
|
<Friend Name="Leonie Fuchs"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Stefan Fischer" Age="52" Email="s.fischer@example.de" Salary="92000.00" Active="false">
|
||||||
|
<Address Street="Bachweg 5" City="Stuttgart" ZipCode="70173"/>
|
||||||
|
<Sibling Name="Monika Fischer" Age="15" EducationalInstitution="Leibniz-Gymnasium">
|
||||||
|
<Friend Name="Alina Roth"/>
|
||||||
|
<Friend Name="Marie Lorenz"/>
|
||||||
|
<Friend Name="Clara Dietrich"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Laura Hoffmann" Age="26" Email="laura.hoffmann@example.de" Salary="48000.00" Active="true">
|
||||||
|
<Sibling Name="Tim Hoffmann" Age="11" EducationalInstitution="Pestalozzi-Grundschule">
|
||||||
|
<Friend Name="Finn Krause"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Michael Bauer" Age="39" Email="m.bauer@example.de" Salary="67500.25" Active="true">
|
||||||
|
<Sibling Name="Susanne Bauer" Age="18" EducationalInstitution="Humboldt-Gymnasium">
|
||||||
|
<Friend Name="Janina Weiß"/>
|
||||||
|
<Friend Name="Katharina Sommer"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Frank Bauer" Age="13" EducationalInstitution="Carl-von-Ossietzky-Realschule"/>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Sandra Koch" Age="31" Email="s.koch@example.de" Salary="55000.00" Active="true">
|
||||||
|
<Sibling Name="Bernd Koch" Age="9" EducationalInstitution="Astrid-Lindgren-Grundschule">
|
||||||
|
<Friend Name="Paul Werner"/>
|
||||||
|
<Friend Name="Jonas Haas"/>
|
||||||
|
<Friend Name="Emil Schuster"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Daniel Wagner" Age="48" Email="d.wagner@example.de" Salary="85000.00" Active="true">
|
||||||
|
<Address Street="Königsallee 88" City="Düsseldorf" ZipCode="40212"/>
|
||||||
|
<Sibling Name="Claudia Wagner" Age="17" EducationalInstitution="Theodor-Heuss-Gymnasium">
|
||||||
|
<Friend Name="Nora Böhm"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Julia Becker" Age="29" Email="j.becker@example.de" Salary="51000.50" Active="false">
|
||||||
|
<Sibling Name="Martin Becker" Age="15" EducationalInstitution="Schiller-Realschule">
|
||||||
|
<Friend Name="David Pfeiffer"/>
|
||||||
|
<Friend Name="Lukas Franke"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Anja Becker" Age="10" EducationalInstitution="Erich-Kästner-Grundschule"/>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Christian Schulz" Age="36" Email="c.schulz@example.de" Salary="63000.00" Active="true">
|
||||||
|
<Sibling Name="Birgit Schulz" Age="19" EducationalInstitution="Technische Universität München">
|
||||||
|
<Friend Name="Stefanie Keller"/>
|
||||||
|
<Friend Name="Nadine Möller"/>
|
||||||
|
<Friend Name="Verena Huber"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Sabine Richter" Age="42" Email="s.richter@example.de" Salary="71000.00" Active="true">
|
||||||
|
<Address Street="Marktplatz 3" City="Heidelberg" ZipCode="69117"/>
|
||||||
|
<Sibling Name="Holger Richter" Age="16" EducationalInstitution="Konrad-Adenauer-Gymnasium">
|
||||||
|
<Friend Name="Maximilian Graf"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Markus Klein" Age="55" Email="m.klein@example.de" Salary="95000.00" Active="true">
|
||||||
|
<Sibling Name="Gabriele Klein" Age="14" EducationalInstitution="Sophie-Scholl-Gesamtschule">
|
||||||
|
<Friend Name="Emilia Winter"/>
|
||||||
|
<Friend Name="Charlotte Baumann"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Ralf Klein" Age="8" EducationalInstitution="Wilhelm-Busch-Grundschule"/>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Nicole Wolf" Age="27" Email="n.wolf@example.de" Salary="46500.00" Active="true">
|
||||||
|
<Address Street="Hafenstraße 21" City="Hamburg" ZipCode="20457"/>
|
||||||
|
<Sibling Name="Patrick Wolf" Age="12" EducationalInstitution="Otto-Hahn-Realschule">
|
||||||
|
<Friend Name="Felix Brandt"/>
|
||||||
|
<Friend Name="Moritz Hahn"/>
|
||||||
|
<Friend Name="Till Albrecht"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Florian Schröder" Age="34" Email="f.schroeder@example.de" Salary="58000.75" Active="false">
|
||||||
|
<Sibling Name="Kerstin Schröder" Age="18" EducationalInstitution="Willy-Brandt-Gesamtschule">
|
||||||
|
<Friend Name="Miriam Jäger"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Petra Neumann" Age="41" Email="p.neumann@example.de" Salary="69000.00" Active="true">
|
||||||
|
<Address Street="Schloßgasse 7" City="Freiburg" ZipCode="79098"/>
|
||||||
|
<Sibling Name="Uwe Neumann" Age="7" EducationalInstitution="Brüder-Grimm-Grundschule">
|
||||||
|
<Friend Name="Ben Schubert"/>
|
||||||
|
<Friend Name="Leo Heinrich"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Heike Neumann" Age="13" EducationalInstitution="Clara-Schumann-Realschule"/>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Andreas Braun" Age="38" Email="a.braun@example.de" Salary="64500.00" Active="true">
|
||||||
|
<Sibling Name="Tanja Braun" Age="11" EducationalInstitution="Johannes-Gutenberg-Grundschule">
|
||||||
|
<Friend Name="Maya Schmitt"/>
|
||||||
|
<Friend Name="Ida Lehmann"/>
|
||||||
|
<Friend Name="Rosa Günther"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Kathrin Zimmermann" Age="30" Email="k.zimmermann@example.de" Salary="53000.00" Active="true">
|
||||||
|
<Sibling Name="Tobias Zimmermann" Age="16" EducationalInstitution="Ernst-Moritz-Arndt-Gymnasium">
|
||||||
|
<Friend Name="Jan Vogt"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Robert Krüger" Age="47" Email="r.krueger@example.de" Salary="82000.00" Active="false">
|
||||||
|
<Sibling Name="Ingrid Krüger" Age="19" EducationalInstitution="Ruprecht-Karls-Universität Heidelberg">
|
||||||
|
<Friend Name="Helena Wirth"/>
|
||||||
|
<Friend Name="Franziska Otto"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Melanie Hartmann" Age="25" Email="m.hartmann@example.de" Salary="44000.00" Active="true">
|
||||||
|
<Sibling Name="Sven Hartmann" Age="15" EducationalInstitution="Bertolt-Brecht-Gymnasium">
|
||||||
|
<Friend Name="Tom Schreiber"/>
|
||||||
|
<Friend Name="Erik Ludwig"/>
|
||||||
|
<Friend Name="Philipp Arndt"/>
|
||||||
|
</Sibling>
|
||||||
|
<Sibling Name="Jana Hartmann" Age="6" EducationalInstitution="Maria-Montessori-Grundschule"/>
|
||||||
|
</Person>
|
||||||
|
<Person Name="Jens Lange" Age="44" Email="j.lange@example.de" Salary="73500.00" Active="true">
|
||||||
|
<Sibling Name="Silke Lange" Age="10" EducationalInstitution="Anne-Frank-Grundschule">
|
||||||
|
<Friend Name="Ella Simon"/>
|
||||||
|
<Friend Name="Amelie Horn"/>
|
||||||
|
</Sibling>
|
||||||
|
</Person>
|
||||||
|
</Persons>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
/* Text: #000000, #1f1f1f */
|
/* Text: #000000, #1f1f1f */
|
||||||
/* Borders: #e1e1e1, #d1d1d1 */
|
/* Borders: #e1e1e1, #d1d1d1 */
|
||||||
|
|
||||||
|
|
||||||
/* === QWidget Base === */
|
/* === QWidget Base === */
|
||||||
QWidget {
|
QWidget {
|
||||||
background-color: #f3f3f3;
|
background-color: #f3f3f3;
|
||||||
@@ -250,3 +251,10 @@ QLineEdit:disabled {
|
|||||||
color: #a0a0a0;
|
color: #a0a0a0;
|
||||||
border: 1px solid #e1e1e1;
|
border: 1px solid #e1e1e1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
QSlider {
|
||||||
|
background: hotpink;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<Value ID='Cons_Throttle_Maxspeed_Hi' Label='Throttle Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' ValueType='Float'/>
|
<Value ID='Cons_Throttle_Maxspeed_Hi' Label='Throttle Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' ValueType='Float'/>
|
||||||
|
|
||||||
<Value ID='Cons_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='true' UnitLabel='mm' Min='0' Max='2300' Factor='1.5625' ValueType='Number'/>
|
<Value ID='Cons_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='true' UnitLabel='mm' Min='0' Max='2300' Factor='1.5625' ValueType='Number'/>
|
||||||
<Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Factor='1.5625' ValueType='Float'/>
|
<Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Min="0" Max="100" Factor='1.5625' ValueType='Float'/>
|
||||||
</Device>
|
</Device>
|
||||||
|
|
||||||
</Bike>
|
</Bike>
|
||||||
|
|||||||
Reference in New Issue
Block a user