5 Commits

Author SHA1 Message Date
911f169b5e Visual updates 2026-04-04 14:29:18 +02:00
884e8e903e Fixed build process. 2026-04-03 22:28:07 +02:00
Christoph Holzheuer
0b54793b08 added nodes. 2026-04-02 15:15:28 +02:00
Christoph Holzheuer
b05180f575 changed aarch64 build system 2026-04-02 14:36:47 +02:00
Christoph Holzheuer
c3e5092845 Removed old cmake files. 2026-04-02 10:41:27 +02:00
64 changed files with 4624 additions and 431 deletions

View File

@@ -1,6 +1,5 @@
QT += core gui svg widgets
CONFIG += c++23
# You can make your code fail to compile if it uses deprecated APIs.
@@ -9,34 +8,6 @@ CONFIG += c++23
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.
# 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
@@ -48,7 +19,7 @@ SOURCES += \
bcdriver.cpp \
bcdriverstatewidget.cpp \
bcdrivertinycan.cpp \
bcthemeswitchbutton.cpp \
bcthemebutton.cpp \
bctoggleswitch.cpp \
bctransmitter.cpp \
bcvalue.cpp \
@@ -69,7 +40,7 @@ HEADERS += \
bcdriverstatewidget.h \
bcdrivertinycan.h \
bcmainwindow.h \
bcthemeswitchbutton.h \
bcthemebutton.h \
bctoggleswitch.h \
bctransmitter.h \
bcvalue.h \

View File

@@ -1,138 +1,137 @@
cmake_minimum_required(VERSION 3.16)
# Projektname und unterstützte Sprachen (C++ für Qt, C für die Treiber)
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)
project(BionxControl VERSION 1.0 LANGUAGES C CXX)
# --- C++23 aktivieren ---
set(CMAKE_CXX_STANDARD 23)
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_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# ------------------------------------------------------------------------------
# 2. Qt-Pakete laden
# ------------------------------------------------------------------------------
# Äquivalent zu: QT += core gui widgets svg
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Svg)
find_package(Qt6 REQUIRED COMPONENTS Gui Svg Widgets)
# ------------------------------------------------------------------------------
# 3. Quellcode definieren
# ------------------------------------------------------------------------------
set(PROJECT_SOURCES
qt_standard_project_setup()
qt_add_executable(BionxControl WIN32 MACOSX_BUNDLE
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
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
)
# ------------------------------------------------------------------------------
# 4. Plattform-Spezifika (Architektur-Entscheidung)
# ------------------------------------------------------------------------------
# Windows-Spezifische Quellen und Libs
# Plattformspezifische CAN-Treiber einbinden
if(WIN32)
message(STATUS "Konfiguration für Windows.")
list(APPEND PROJECT_SOURCES
libwin/can_drv_win.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")
target_sources(BionxControl PRIVATE libwin/can_drv_win.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(BionxControl PRIVATE libwin/mhs_can_drv.c)
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
.
libwin
)
# ------------------------------------------------------------------------------
# 6. Bibliotheken linken
# ------------------------------------------------------------------------------
target_link_libraries(BionxControl PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Svg
Qt::Core
Qt::Gui
Qt::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
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()

View File

@@ -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="
}
}
}

View File

@@ -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="
}
}
}

View File

@@ -4,15 +4,15 @@ FROM debian:trixie
# Verhindert interaktive Prompts während der Installation
ENV DEBIAN_FRONTEND=noninteractive
# 1. Multiarch für arm64 aktivieren
RUN dpkg --add-architecture arm64 && \
# 1. Multiarch für armhf aktivieren
RUN dpkg --add-architecture armhf && \
apt-get update && \
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 \
build-essential \
crossbuild-essential-arm64 \
crossbuild-essential-armhf \
cmake \
ninja-build \
git \
@@ -21,12 +21,14 @@ RUN apt-get install -y \
# 3. Qt6 für den HOST installieren (für moc, uic, etc.)
RUN apt-get install -y \
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 \
qt6-base-dev:arm64 \
libglvnd-dev:arm64
qt6-base-dev:armhf \
libglvnd-dev:armhf \
qt6-svg-dev:armhf
# Aufräumen
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

View File

@@ -1,23 +1,23 @@
# Name des Zielsystems
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Die Cross-Compiler aus dem Debian-Paket
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
# Die Cross-Compiler aus dem Debian-Paket für 32-Bit (armhf)
set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
# Wo das System nach Bibliotheken und Headern suchen soll (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_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})
# 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_LIBRARY ONLY) # Libs nur im Target (arm64) suchen
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Header 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 (armhf) suchen
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # CMake-Pakete nur im Target suchen
# *** WICHTIG FÜR QT6 ***

4
bc.h
View File

@@ -64,6 +64,10 @@ namespace BCTags
inline constexpr auto Yes = "Yes"_L1;
inline constexpr auto No = "No"_L1;
inline constexpr auto Host = "bionxcontrol"_L1;
}
/**

View File

@@ -34,7 +34,7 @@
#include <QTimer>
#include <QMessageBox>
#include <bcthemeswitchbutton.h>
#include <bcthemebutton.h>
#include <bcdriverstatewidget.h>
#include <bcmainwindow.h>
#include <bcvaluedelegate.h>
@@ -189,9 +189,9 @@ void BCMainWindow::initStatusBar()
_statusBar->addPermanentWidget(conState);
conState->installEventFilter(this);
BCThemeSwitchButton* themeBtn = new BCThemeSwitchButton(this);
BCThemeButton* themeBtn = new BCThemeButton(this);
_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.";
onShowMessage( message );

View File

@@ -30,10 +30,10 @@
***************************************************************************/
#include <bcthemeswitchbutton.h>
#include <bcthemebutton.h>
BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
BCThemeButton::BCThemeButton(QWidget *parent )
: QPushButton(parent)
{
// Visuelles Setup: Flach, keine Ränder, Hand-Cursor
@@ -42,7 +42,7 @@ BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
setFixedSize( 24, 24 );
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
*/
void BCThemeSwitchButton::setDarkMode( bool isDark )
void BCThemeButton::setDarkMode( bool isDark )
{
_isDarkMode = !isDark;
toggleMode();
@@ -61,7 +61,7 @@ void BCThemeSwitchButton::setDarkMode( bool isDark )
* @brief Schaltet den akutellen Mode um.
*/
void BCThemeSwitchButton::toggleMode()
void BCThemeButton::toggleMode()
{
_isDarkMode = !_isDarkMode;
updateIcon();
@@ -73,7 +73,7 @@ void BCThemeSwitchButton::toggleMode()
* @brief Icon & Tooltip anpassen
*/
void BCThemeSwitchButton::updateIcon()
void BCThemeButton::updateIcon()
{
setText(_isDarkMode ? "🌙" : "☀️");
setToolTip(_isDarkMode ? "Use LightMode" : "Use DarkMode");

View File

@@ -43,13 +43,13 @@
* zu wechseln
*/
class BCThemeSwitchButton : public QPushButton
class BCThemeButton : public QPushButton
{
Q_OBJECT
public:
explicit BCThemeSwitchButton(QWidget *parent = nullptr);
explicit BCThemeButton(QWidget *parent = nullptr);
void setDarkMode( bool isDark );
signals:

View File

@@ -62,8 +62,10 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
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() )
{
@@ -81,8 +83,6 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
return toggleSwitch;
}
BCValue::ValueRange params;
bool hasData = bcValue.hasValuesForSlider( params );
if( !hasData )
return nullptr;
@@ -95,17 +95,6 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
// Commit data sofort bei Änderung
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;
}
@@ -125,18 +114,6 @@ void BCValueDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionVi
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);
/*
if( !bcValue.isBoolean())
{
editorRect = adjustEditorRect( option.rect,0,0,8,0 );
}
else
{
editorRect = adjustEditorRect(option.rect, 0, 6, -130, -6);
}
*/
editor->setGeometry(editorRect);
}
@@ -161,7 +138,7 @@ void BCValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, c
}
model->setData(index, reValue, Qt::EditRole);
return;
}

40
build_and_deploy.sh Executable file
View 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! ==="

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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();
}

View File

@@ -31,6 +31,7 @@
#include <QApplication>
#include <QSysInfo>
#include <QFile>
#include <QDebug>
@@ -45,9 +46,20 @@ int main(int argc, char *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();

90
nodes/nodes/ntx.h Normal file
View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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>

View File

@@ -7,6 +7,7 @@
/* Text: #000000, #1f1f1f */
/* Borders: #e1e1e1, #d1d1d1 */
/* === QWidget Base === */
QWidget {
background-color: #f3f3f3;
@@ -250,3 +251,10 @@ QLineEdit:disabled {
color: #a0a0a0;
border: 1px solid #e1e1e1;
}
/*
QSlider {
background: hotpink;
}
*/

View File

@@ -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_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>
</Bike>

0
runme.sh Normal file
View File

9
runme.txt Normal file
View File

@@ -0,0 +1,9 @@
docker build -t qt-cross-rpi .
docker run --rm -it -v "$(pwd):/workspace" qt-cross-rpi bash
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../arm32-toolchain.cmake -G Ninja ..
ninja