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