created src subdir.

This commit is contained in:
2025-08-06 23:34:43 +02:00
parent 6ff6ea02a4
commit 6bdc487146
63 changed files with 3084 additions and 29 deletions

View File

@@ -0,0 +1,153 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqappdata.h>
#include <QDebug>
#include <QApplication>
#include <QStyle>
#include <QIcon>
#include <QMap>
#include <QMetaEnum>
#include <QPushButton>
namespace XQAppData
{
template<typename E>
constexpr auto to_underlying(E e) noexcept
{
return static_cast<std::underlying_type_t<E>>(e);
}
class XQAppIconMap : public QMap<QString,QIcon>
{
public:
void init()
{
auto from = to_underlying(QIcon::ThemeIcon::AddressBookNew);
auto to = to_underlying(QIcon::ThemeIcon::NThemeIcons);
for (auto i = from; i < to; ++i)
{
QIcon::ThemeIcon f = static_cast<QIcon::ThemeIcon>(i);
QIcon icon = QIcon::fromTheme(f);
// Nur hinzufügen, wenn das Icon existiert
if (!icon.isNull())
insert(icon.name(), icon);
}
}
};
static XQAppIconMap s_IconMap;
bool hasTypeIcon(const QString& key )
{
if(s_IconMap.isEmpty())
s_IconMap.init();
return !key.isEmpty() && s_IconMap.contains(key);
}
QIcon typeIcon(const QString& key )
{
if( hasTypeIcon(key) )
return s_IconMap[key];
return QApplication::style()->standardIcon(QStyle::SP_TrashIcon);
}
/*
{ "icnFolder" , QApplication::style()->standardIcon(QStyle::SP_DirIcon) },
{ "icnFolder" , QApplication::style()->standardIcon(QStyle::SP_DirIcon)},
{ "icn02Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogBack)},
{ "icn03Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogContentsView)},
{ "icn04Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogDetailedView)},
{ "icn05Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogEnd)},
{ "icn06Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogInfoView)},
{ "icn07Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogListView)},
{ "icn08Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogNewFolder)},
{ "icn09Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogStart)},
{ "icn10Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogToParent)},
{ "icn11Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowBack)},
{ "icn12Dummy", QApplication::style()->standardIcon(QStyle::SP_DirIcon)},
{ "icn13Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaSkipBackward)},
{ "icn14Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowDown)},
{ "icn15Dummy", QApplication::style()->standardIcon(QStyle::SP_DirLinkIcon)},
{ "icn16Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaSkipForward)},
{ "icn17Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowForward)},
{ "icn18Dummy", QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon)},
{ "icn19Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaStop)},
{ "icn20Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowLeft)},
{ "icn21Dummy", QApplication::style()->standardIcon(QStyle::SP_DockWidgetCloseButton)},
{ "icn22Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaVolume)},
{ "icn23Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowRight)},
{ "icn24Dummy", QApplication::style()->standardIcon(QStyle::SP_DriveCDIcon)},
{ "icn25Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaVolumeMuted)},
{ "icn26Dummy", QApplication::style()->standardIcon(QStyle::SP_ArrowUp)},
{ "icn27Dummy", QApplication::style()->standardIcon(QStyle::SP_DriveDVDIcon)},
{ "icn28Dummy", QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical)},
{ "icn29Dummy", QApplication::style()->standardIcon(QStyle::SP_BrowserReload)},
{ "icn30Dummy", QApplication::style()->standardIcon(QStyle::SP_DriveFDIcon)},
{ "icn31Dummy", QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation)},
{ "icn32Dummy", QApplication::style()->standardIcon(QStyle::SP_BrowserStop)},
{ "icn33Dummy", QApplication::style()->standardIcon(QStyle::SP_DriveHDIcon)},
{ "icn34Dummy", QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion)},
{ "icn35Dummy", QApplication::style()->standardIcon(QStyle::SP_CommandLink)},
{ "icn36Dummy", QApplication::style()->standardIcon(QStyle::SP_DriveNetIcon)},
{ "icn37Dummy", QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning)},
{ "icn38Dummy", QApplication::style()->standardIcon(QStyle::SP_ComputerIcon)},
{ "icn39Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogBack)},
{ "icn40Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarCloseButton)},
{ "icn41Dummy", QApplication::style()->standardIcon(QStyle::SP_CustomBase)},
{ "icn42Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogContentsView)},
{ "icn43Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarContextHelpButton)},
{ "icn44Dummy", QApplication::style()->standardIcon(QStyle::SP_DesktopIcon)},
{ "icn45Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogDetailedView)},
{ "icn46Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarMaxButton)},
{ "icn47Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton)},
{ "icn48Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogEnd)},
{ "icn49Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarMenuButton)},
{ "icn50Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton)},
{ "icn51Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogInfoView)},
{ "icn52Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarMinButton)},
{ "icn53Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogCloseButton)},
{ "icn54Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogListView)},
{ "icn55Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarNormalButton)},
{ "icn56Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogDiscardButton)},
{ "icn57Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogNewFolder)},
{ "icn58Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarShadeButton)},
{ "icn59Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogHelpButton)},
{ "icn60Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogStart)},
{ "icn61Dummy", QApplication::style()->standardIcon(QStyle::SP_TitleBarUnshadeButton)},
{ "icn62Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogNoButton)},
{ "icn63Dummy", QApplication::style()->standardIcon(QStyle::SP_FileDialogToParent)},
{ "icn64Dummy", QApplication::style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton)},
{ "icn65Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogOkButton)},
{ "icn66Dummy", QApplication::style()->standardIcon(QStyle::SP_FileIcon)},
{ "icn67Dummy", QApplication::style()->standardIcon(QStyle::SP_ToolBarVerticalExtensionButton)},
{ "icn68Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogResetButton)},
{ "icn70Dummy", QApplication::style()->standardIcon(QStyle::SP_FileLinkIcon)},
{ "icn71Dummy", QApplication::style()->standardIcon(QStyle::SP_TrashIcon)},
{ "icn72Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogSaveButton)},
{ "icn73Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaPause)},
{ "icn74Dummy", QApplication::style()->standardIcon(QStyle::SP_VistaShield)},
{ "icn75Dummy", QApplication::style()->standardIcon(QStyle::SP_DialogYesButton)},
{ "icn76Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaPlay)},
{ "icn77Dummy", QApplication::style()->standardIcon(QStyle::SP_DirClosedIcon)},
{ "icn79Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaSeekBackward)},
{ "icn80Dummy", QApplication::style()->standardIcon(QStyle::SP_DirHomeIcon)},
{ "icn81Dummy", QApplication::style()->standardIcon(QStyle::SP_MediaSeekForward)}
}
*/
}; // namespace XQAppData

View File

@@ -0,0 +1,49 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQAPPDATA_H
#define XQAPPDATA_H
#include <qnamespace.h>
#include <QVariant>
#include <QMap>
#include <QStringView>
#include <QIcon>
#include <pugixml.hpp>
const QString c_Version = "0.1.1 04.09.2024";
const QString c_MainModelName = "DocumentTreeModel";
const QString c_ChildModelName = "DocumentDetailsModel";
const QString c_ProjectID = "ProjectID";
const QString c_ModelSheetFileName = "xml/modelsheets.xml";
const QString c_ModelDummyFileName = "xml/saved_testfile.xtr";
const QString c_DocumentDirectory = "xml/";
const QString c_DocumentFileName1 = "xml/modeldata1.xtr";
const QString c_DocumentFileName2 = "xml/modeldata2.xtr";
const QString c_DocumentFileName3 = "xml/modeldata3.xtr";
const QString c_FriendlyName = "FriendlyName";
namespace XQAppData
{
class XQAppIconMap;
bool hasTypeIcon(const QString& key );
QIcon typeIcon(const QString& key );
}
#endif // XQAPPDATA_H

View File

@@ -0,0 +1,150 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <znode_factory.h>
#include <xqchildmodel.h>
#include <xqselectionmodel.h>
#include <xqitemdelegate.h>
#include <xqappdata.h>
#include <xqtreeview.h>
#include <xqitemfactory.h>
XQChildModel::XQChildModel( QObject *parent )
: XQModel{parent}
{
}
XQChildModel::~XQChildModel()
{
}
/**
* @brief XQChildModel::initModel: Create the own modelstructure
* using subtree 'Components'
*/
void XQChildModel::initModel(const QString& modelName)
{
// #0: Wir suchen die Model-Beschreibung
XQNodePtr modelSheet = _itemFactory.findModelSheet( modelName ); // throws
// #1: Wie erzeugen die Model-Struktur: Jedes Kind beschreibt einen
// XML-Datentyp, z.B. <Panel atr1="..." />, <Battery .../>
// Jeder XML-Knoten entspricht einer Zeile im späteren Model, jedes
// Attribut wird einem eigenen Feld (XQItem) abgebildet.
for( auto& sheetNode : modelSheet->children() )
{
XQItemList list = _itemFactory.makeHeaderRow( sheetNode );
// für jeden XML-Knotentyp in der Modelbeschreibung erzeugen wir eine section
addSection(list, sheetNode );
/*
// empty row
XQNodePtr contentNode = XQNode::make_node( sheetNode->tag_name() );
XQItemList emptyRow = _itemFactory.makeEmptyRow( contentNode, sheetNode );
appendRow( emptyRow );
*/
} // for
}
void XQChildModel::setContent( const XQNodePtr& contentRoot )
{
// __fix: set object name ??
qDebug() << " --- create Model Data: " << contentRoot->to_string();
// Die Datenbasis als shared_ptr sichern
_contentRoot = contentRoot;
// Wir gehen über alle Einträge, die verschiedenen Typen
// haben, hier: <Panel>. <Battery> ...
for (const auto& contentEntry : _contentRoot->children())
{
// Das ist hier der Typ des Eintrags: Panel, Battery ...
QString key = contentEntry->tag_name();
// 'silent failure' hier der Datenbaum kann auch Knoten enthalten
// die nicht für uns gedacht sind.
if (!_sections.hasValidSection(key))
continue;
XQModelSection& section = _sections.at( key );
// wir speichern das parent des datenknoten auch in der
// section.
// contentEntry->parent == _contentRoot, aber halt nur weil das model flach ist
section.contentRootNode = contentEntry->parent();
int newRow = _sections.lastRow(section);
//qDebug() << " --- AHJA: " << key << " -- last Row dazu: " << newRow;
XQItemList list = _itemFactory.makeContentRow( contentEntry, section.sheetRootNode );
// als Baum?
//section.headerItem().appendRow( list );
insertRow( newRow, list);
} // for
}
void XQChildModel::createModelContentChildRow( QStandardItem* parent, XQNodePtr contentNode )
{
/*
parent->appendRow( { new XQItem("Optionals", XQItemType::HeaderStyle ), new XQItem( "Value", XQItemType::HeaderStyle )} );
for( auto& child : contentNode->children() )
{
XQItemList list;
list.append( new XQItem( child->attribute("DataItem"), XQItemType::StaticStyle ) );
list.append( new XQItem( child->attribute("DataValue"), XQItemType::StaticStyle ) );
parent->appendRow( list );
}
*/
}
void XQChildModel::initContextMenu()
{
// __fixme! add a menu title
_contextMenu->clear();
const QModelIndex& curIdx = _treeView->currentIndex();
bool hasSel = curIdx.isValid() && _treeView->selectionModel()->hasSelection();
bool canPaste = _clipBoard.canPaste( curIdx );
_contextMenu->addAction( "icn11Dummy", "Undo", XQCommand::cmdUndo, _undoStack->canUndo() );
_contextMenu->addAction( "icn17Dummy", "Redo", XQCommand::cmdRedo, _undoStack->canRedo() );
_contextMenu->addAction( "icn58Dummy", "Cut", XQCommand::cmdCut, hasSel );
_contextMenu->addAction( "icn61Dummy", "Paste", XQCommand::cmdPaste, canPaste );
_contextMenu->addAction( "icn55Dummy", "Copy", XQCommand::cmdCopy, hasSel );
//_contextMenu->addAction( "icn35Dummy", "Move", XQCommand::cmdMove, hasSel );
_contextMenu->addAction( "icn70Dummy", "New", XQCommand::cmdNew, hasSel );
_contextMenu->addAction( "icn50Dummy", "Delete", XQCommand::cmdDelete, hasSel );
// __fixme! set 'toggle section <name>' entry
//contextMenu.actions().first()->setText("<name>");
_contextMenu->addAction( "icn29Dummy", "Toggle Section", XQCommand::cmdToggleSection, hasSel);
}

View File

@@ -0,0 +1,44 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQCHILDMODEL_H
#define XQCHILDMODEL_H
#include <xqmodel.h>
class XQChildModel : public XQModel
{
Q_OBJECT
public:
explicit XQChildModel(QObject *parent = nullptr);
virtual ~XQChildModel();
void initModel(const QString& modelName) override;
void setContent(const XQNodePtr& contentRoot );
protected:
virtual void createModelContentChildRow(QStandardItem* parent, XQNodePtr contentNode );
//void setupViewProperties() override;
void initContextMenu() override;
};
#endif // XQCHILDMODEL_H

View File

@@ -0,0 +1,58 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqdocumentstore.h>
#include <xqitem.h>
#include <QFile>
XQDocument::XQDocument(const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQModel* aModelView )
: fileName{ aFileName }, friendlyName{ aFriendlyName }, treeItem{ aTreeItem }, modelView{ aModelView }
{
}
XQDocument::~XQDocument()
{
//delete _documentRoot;
}
///
/// ---
///
XQDocumentStore::~XQDocumentStore()
{
//for (auto entry : *this)
// delete entry;
//for( int i=0; i<size();++i)
// delete at(i);
}
void XQDocumentStore::addDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQModel* aModelView )
{
XQDocument newDocument( aFileName, aFriendlyName, aTreeItem,aModelView );
addAtKey( aFileName, newDocument );
// attention: this assumes the presence of the 'ProjectID' value
//addAlias( aFileName, aTreeItem->attribute(c_ProjectID) );
//addAlias( aFileName, "fitze!" );
}

View File

@@ -0,0 +1,57 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQDOCUMENTSTORE_H
#define XQDOCUMENTSTORE_H
#include <xqmaptor.h>
#include <xqnode.h>
class XQModel;
class XQItem;
// should this be internal??
struct XQDocument
{
XQDocument() = default;
XQDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQModel* aModelView );
virtual ~XQDocument();
QString fileName; // also used as key
QString friendlyName;
XQItem* treeItem{};
XQModel* modelView{};
};
class XQDocumentStore : public XQMaptor<XQDocument>
{
public:
XQDocumentStore() = default;
virtual ~ XQDocumentStore();
void addDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQModel* aModelView );
protected:
XQNode _treeRootNode{ "treeRootNode" };
};
#endif // XQDOCUMENTSTORE_H

View File

@@ -0,0 +1,83 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QPersistentModelIndex>
#include <QClipboard>
#include <xqmainmodel.h>
#include <xqexception.h>
#include <xqtreeview.h>
#include <xqitemdelegate.h>
#include <xqitem.h>
#include <xqappdata.h>
#include <xqselectionmodel.h>
#include <xqitemfactory.h>
XQMainModel::XQMainModel(QObject *parent )
: XQModel{parent}
{
}
XQMainModel::~XQMainModel()
{
// delete
}
void XQMainModel::initModel(const QString& modelName)
{
XQNodePtr modelSheet = _itemFactory.findModelSheet( modelName );
// #1: create structure: create static sections for this model
for( auto& sheetNode : modelSheet->children() )
{
qDebug() << " create main model: " << sheetNode->tag_name();
XQItemList list = { _itemFactory.makeHeaderItem( sheetNode ) };
Q_ASSERT(!list.isEmpty());
addSection(list, sheetNode );
//appendRow( list );
}
}
XQItem* XQMainModel::createTreeEntry( XQNodePtr contentNode )
{
for(const auto& section : _sections )
{
if( contentNode->attribute("State") == section.sheetRootNode->attribute("State") )
{
XQItem* newTreeentry = _itemFactory.makeContentItem( contentNode, section.sheetRootNode );
section.headerItem().appendRow( newTreeentry );
_treeView->expand( section.modelIndex );
// ??
_treeView->setCurrentIndex( section.modelIndex );
newTreeentry->setContentNode(contentNode);
emit xqItemCreated( newTreeentry );
return newTreeentry;
}
}
throw XQException( "createTreeEntry: main model should not be emtpy!" );
}
void XQMainModel::initContextMenu()
{
}

View File

@@ -0,0 +1,47 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMAINMODEL_H
#define XQMAINMODEL_H
//#include <QItemSelectionModel>
#include <xqmodel.h>
//#include <xqmodelsections.h>
class XQTreeView;
class XQMainModel : public XQModel
{
Q_OBJECT
public:
explicit XQMainModel(QObject *parent = nullptr);
virtual ~XQMainModel();
void initModel(const QString& modelName) override;
XQItem* createTreeEntry( XQNodePtr contentNode );
public slots:
protected:
void initContextMenu() override;
};
#endif // XQMAINMODEL_H

View File

@@ -0,0 +1,285 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <xqmainwindow.h>
#include <xqcommand.h>
#include <xqexception.h>
#include <xqitemfactory.h>
#include <xqnodewriter.h>
XQMainWindow::XQMainWindow( QWidget* parent )
: QMainWindow(parent)
{
setupUi(this);
setWindowTitle( QString("XTree %1").arg(c_Version));
initMainWindow();
}
void XQMainWindow::initMainWindow()
{
qDebug() << " --- initMainWindow(): here we go!";
// als allererstes laden wir die Modelschreibungen
XQItemFactory::instance().initItemFactory( c_ModelSheetFileName );
_undoView->setStack( &_undoStack );
_actionUndo->setData( XQCommand::cmdUndo);
_actionRedo->setData( XQCommand::cmdRedo);
_actionCut->setData( XQCommand::cmdCut);
_actionCopy->setData( XQCommand::cmdCopy);
_actionPaste->setData( XQCommand::cmdPaste);
_actionNew->setData( XQCommand::cmdNew);
_actionDelete->setData( XQCommand::cmdDelete);
connect( _actionUndo, &QAction::triggered, this, &XQMainWindow::onUndo );
connect( _actionRedo, &QAction::triggered, this, &XQMainWindow::onRedo );
connect( _actionOpen, &QAction::triggered, this, &XQMainWindow::onOpenDocument );
connect( _actionSave, &QAction::triggered, this, &XQMainWindow::onSaveDocument );
connect( _actionSaveAs, &QAction::triggered, this, &XQMainWindow::onSaveDocumentAs );
connect( _actionExit, &QAction::triggered, this, &XQMainWindow::onExit );
connect( _actionAbout, &QAction::triggered, this, &XQMainWindow::onAbout );
//connect( _mainTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex)) );
connect( _mainTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onTreeItemClicked(QModelIndex)) );
connect( _tabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(onTabClicked(int)) );
connect( &_mainModelView, &XQModel::xqItemCreated, this, [=, this](XQItem* item)
{
// when a new main tree item has been created ...
QString pID = item->contentNode()->attribute(c_ProjectID);
_mainTreeView->setCurrentIndex( item->index() );
// ... we set the current view to this node
if( _documentStore.contains( pID ) )
_tabWidget->setCurrentWidget( _documentStore[pID].modelView->treeView() );
} );
try
{
// hand over undostack
_mainModelView.setUndoStack(&_undoStack);
// hand over left side navigation tree
_mainModelView.setTreeView(_mainTreeView);
// #1. init the left side main tree view
_mainModelView.initModel( c_MainModelName );
// #2. load demo data
loadDocument( c_DocumentFileName1 );
//loadDocument( c_DocumentFileName2 );
qDebug() << " --- all here: " << XQNode::s_Count;
}
catch( XQException& exception )
{
qDebug() << exception.what();
QMessageBox::critical( this, "Failure", QString("Failure: %1").arg(exception.what()) );
}
}
void XQMainWindow::onUndo()
{
qDebug() << " --- undo Pressed";
if(_undoStack.canUndo())
_undoStack.undo();
}
void XQMainWindow::onRedo()
{
qDebug() << " --- redo Pressed";
if(_undoStack.canRedo())
_undoStack.redo();
}
void XQMainWindow::onCreateDocument()
{
qDebug() << " ---- create document Pressed!";
}
void XQMainWindow::onOpenDocument()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Project"), c_DocumentDirectory, tr("project data(*.xtr)") );
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(this, "Warning", "Cannot load file: " + file.errorString());
return;
}
// close dummy file ...
file.close();
loadDocument( fileName );
}
void XQMainWindow::onSaveDocument()
{
qDebug() << " ---- save Pressed!";
saveDocument( c_ModelDummyFileName );
}
void XQMainWindow::onSaveDocumentAs()
{
QString fileName = QFileDialog::getSaveFileName(this, "Save as", c_DocumentDirectory, tr("project data(*.xtr)") );
QFile file(fileName);
// open dummy file
if (!file.open(QFile::WriteOnly | QFile::Text))
{
QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());
return;
}
// close dummy file ...
file.close();
// and create a xml stream
saveDocument( fileName );
}
void XQMainWindow::onCloseDocument()
{
qDebug() << " ---- close Pressed!";
}
void XQMainWindow::onExit()
{
qApp->exit();
}
void XQMainWindow::onAbout()
{
QMessageBox msgBox(QMessageBox::NoIcon, "About", "", QMessageBox::Ok);
QString text = "<b>xtree concept</b><br>";
text += "2024 c.holzheuer<br><br>";
text += "<a href=\"https://sourceworx.org/xtree\">sourceworx.org/xtree</a>";
msgBox.setTextFormat(Qt::RichText); // This allows you to click the link
msgBox.setText( text );
msgBox.exec();
}
// when item in the left tree is clicked, switch view on the right side
void XQMainWindow::onTreeItemClicked(const QModelIndex& index )
{
XQItem& entry = XQItem::xqItemFromIndex(index);
qDebug() << " --- mainWindow onTreeItemClicked:" << entry.text();
return;
//_mainTreeView->selectionModel()->select(index, QItemSelectionModel::Select);
//entry->setBackground( QBrush( Qt::green ) );
QString key = entry.attribute(c_ProjectID);
if( _documentStore.contains(key) )
_tabWidget->setCurrentWidget( _documentStore[key].modelView->treeView() );
}
// when item in the left tree is clicked, switch view on the right side
void XQMainWindow::onTabClicked( int index )
{
const QString& key = _documentStore[index].treeItem->attribute( c_ProjectID );
qDebug() << " ---- tab clicked: " << index << " : " << _documentStore[index].friendlyName << ": " << key;
_mainTreeView->setCurrentIndex( _documentStore[index].treeItem->index() );
}
void XQMainWindow::loadDocument( const QString& fileName )
{
//ntThrowIfNullPtr
// gibts die Datei?
if( !QFile::exists( fileName) )
throw XQException( "no such file", fileName );
// load data tree from xml file
XQNodeFactory treeLoader;
XQNodePtr rawTree = treeLoader.load_tree( qPrintable(fileName) );
// versteckten root node ignorieren
XQNodePtr contentRoot = rawTree->first_child();
// Project-ID behandeln
const QString& pID = contentRoot->attribute(c_ProjectID);
int idx = _documentStore.indexOf( pID );
if( idx > -1 )
{
const XQDocument& doc = _documentStore.at(idx);
QMessageBox::warning( this, "Load Document", QString("File: %1 already loaded.").arg( fileName ) );
_mainTreeView->setCurrentIndex( doc.treeItem->index() );
_tabWidget->setCurrentIndex( idx );
return;
}
// 'friendly Name' ist ein Link auf ein anderes Attribute
// das als Namen verwendet wird.
const QString& fName = contentRoot->friendly_name();
QString pTitle = QString("Project %1: %2").arg( pID, fName );
// Eine neue TreeView erzeugn und im TabWidget parken.
XQTreeView* childTreeView = new XQTreeView(_tabWidget);
_tabWidget->addTab( childTreeView, pTitle );
_tabWidget->setCurrentWidget( childTreeView );
setWindowTitle( pTitle );
// Ein neues Child-Model erzeugen
XQChildModel* childModel = new XQChildModel(this);
// die Modelstruktur anlegen
childModel->initModel( c_ChildModelName );
// Den globalen undo-stack ...
childModel->setUndoStack(&_undoStack);
// und die TreeView übergeben
childModel->setTreeView(childTreeView);
// read the model data
childModel->setContent( contentRoot->first_child() );
// create new entry in the left side main tree view
XQItem* newEntry = _mainModelView.createTreeEntry( contentRoot );
_mainTreeView->setCurrentIndex( newEntry->index() );
_documentStore.addDocument( fileName, pTitle, newEntry, childModel );
}
void XQMainWindow::saveDocument( const QString& fileName )
{
XQNodeWriter nodeWriter;
int curIdx = _tabWidget->currentIndex();
XQNodePtr rootNode = _documentStore[curIdx].treeItem->contentNode();
nodeWriter.dumpTree( rootNode, fileName );
}

View File

@@ -0,0 +1,67 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMAINWINDOW_H
#define XQMAINWINDOW_H
#include <QMainWindow>
#include <ui_xqmainwindow.h>
#include <xqdocumentstore.h>
#include <xqmainmodel.h>
#include <xqchildmodel.h>
#include <xqappdata.h>
class XQMainWindow : public QMainWindow, public Ui_XQMainWindow
{
Q_OBJECT
public:
XQMainWindow( QWidget* parent = nullptr );
virtual ~XQMainWindow() = default;
void initMainWindow();
public slots:
void onUndo();
void onRedo();
void onCreateDocument();
void onOpenDocument();
void onSaveDocument();
void onSaveDocumentAs();
void onCloseDocument();
void onAbout();
void onExit();
void onTreeItemClicked(const QModelIndex& index );
void onTabClicked( int index );
protected:
// fixme implement
void showDocumnet( const QString& key ){}
void loadDocument( const QString& fileName );
void saveDocument( const QString& fileName );
QUndoStack _undoStack;
XQDocumentStore _documentStore;
XQMainModel _mainModelView;
};
#endif // XQMAINWINDOW_H

View File

@@ -0,0 +1,290 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>XQMainWindow</class>
<widget class="QMainWindow" name="XQMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>XTree</string>
</property>
<widget class="QWidget" name="_centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="_tabWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="_menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="_fileMenu">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="separator"/>
<addaction name="_actionOpen"/>
<addaction name="_actionSave"/>
<addaction name="_actionSaveAs"/>
<addaction name="_actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="_actionAbout"/>
</widget>
<widget class="QMenu" name="_menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="_actionUndo"/>
<addaction name="_actionRedo"/>
<addaction name="separator"/>
<addaction name="_actionCut"/>
<addaction name="_actionPaste"/>
<addaction name="_actionCopy"/>
</widget>
<addaction name="_fileMenu"/>
<addaction name="_menuEdit"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="_statusBar"/>
<widget class="QDockWidget" name="_treeDockWidget">
<property name="windowTitle">
<string>MainDocument</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="_treeDockLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="XQTreeView" name="_mainTreeView">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="_statusDock">
<property name="toolTipDuration">
<number>7</number>
</property>
<property name="windowTitle">
<string>Status</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="_statusContent">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QUndoView" name="_undoView">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<action name="_actionExit">
<property name="text">
<string>E&amp;xit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="_actionCopy">
<property name="text">
<string>Copy</string>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
</property>
</action>
<action name="_actionCut">
<property name="text">
<string>Cu&amp;t</string>
</property>
<property name="shortcut">
<string>Ctrl+X</string>
</property>
</action>
<action name="_actionNew">
<property name="text">
<string>New</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="_actionPaste">
<property name="icon">
<iconset theme="audio-volume-high"/>
</property>
<property name="text">
<string>Paste</string>
</property>
<property name="shortcut">
<string>Ctrl+V</string>
</property>
</action>
<action name="_actionUndo">
<property name="text">
<string>Undo</string>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
</property>
</action>
<action name="_actionRedo">
<property name="text">
<string>Redo</string>
</property>
<property name="shortcut">
<string>Ctrl+Y</string>
</property>
</action>
<action name="_actionOpen">
<property name="text">
<string>Open</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionCopyAsText">
<property name="text">
<string>Copy As Text</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="_actionDelete">
<property name="text">
<string>Delete</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="_actionFind">
<property name="text">
<string>Find</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="_actionSave">
<property name="text">
<string>Save</string>
</property>
<property name="toolTip">
<string>Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="_actionSaveAs">
<property name="text">
<string>Save as ...</string>
</property>
<property name="toolTip">
<string>Save as ...</string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+S</string>
</property>
</action>
<action name="_actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>XQTreeView</class>
<extends>QTreeView</extends>
<header location="global">xqtreeview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,15 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqgenericitem.h>

33
src/items/xqgenericitem.h Normal file
View File

@@ -0,0 +1,33 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQGENERICITEM_H
#define XQGENERICITEM_H
#include <xqitem.h>
//
// Was sollte das mal werden?
//
template<class T, class U>
class XQGenericItem : public XQItem, public T, public U
{
public:
XQGenericItem() = default;
};
#endif // XQGENERICITEM_H

661
src/items/xqitem.cpp Normal file
View File

@@ -0,0 +1,661 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqitem.h>
#include <xqmodel.h>
#include <xqmaptor.h>
#include <QDateTime>
#include <xqitemtype.h>
#include <QDebug>
XQItem::XQItemFlagMap XQItem::s_ItemFlagMap
{
{ "NoItemFlags", Qt::NoItemFlags },
{ "IsSelectable", Qt::ItemIsSelectable },
{ "IsEditable", Qt::ItemIsEditable },
{ "IsDragEnabled", Qt::ItemIsDragEnabled },
{ "IsDropEnabled", Qt::ItemIsDropEnabled },
{ "IsUserCheckable", Qt::ItemIsUserCheckable },
{ "IsEnabled", Qt::ItemIsEnabled },
{ "IsAutoTristate", Qt::ItemIsAutoTristate },
{ "ItemNeverHasChildren", Qt::ItemNeverHasChildren },
{ "IsUserTristate", Qt::ItemIsUserTristate }
};
XQItem::XQItemDataRoleMap XQItem::s_ItemDataRoleMap
{
{"ItemType", ItemTypeRole},
{"Content", ContentRole},
{"RenderStyle", RenderStyleRole},
{"EditorType", EditorTypeRole},
{"ItemFlags", FlagsRole},
{"UnitType", UnitTypeRole},
{"ContentFormat", ContentFormatRole},
{"FlagsRole", FlagsRole},
{"Icon", IconRole},
{"FixedChoices", FixedChoicesRole},
{"DataNode", ContentNodeRole},
{"SheetNode", SheetNodeRole}
};
// No bi-map needed here, qmap.key() is sufficient for the job
XQItem::XQRenderStyleMap XQItem::s_RenderStyleMap
{
{ "NoRenderStyle", NoRenderStyle },
{ "HiddenStyle", HiddenStyle },
{ "HeaderStyle", HeaderStyle },
{ "PlainStyle", PlainStyle },
{ "CheckBoxStyle", CheckBoxStyle },
{ "ComboBoxStyle", ComboBoxStyle },
{ "TreeHeaderStyle", TreeHeaderStyle },
{ "CustomRenderStyle", CustomRenderStyle },
{ "PickerStyle", PickerStyle },
{ "SpinBoxStyle", SpinBoxStyle },
{ "ProgressBarStyle", ProgressBarStyle},
{ "FormattedStyle", FormattedStyle},
};
XQItem::XQEditorTypeMap XQItem::s_EditorTypeMap
{
{ "NoEditorType", NoEditorType },
{ "LineEditType", LineEditType },
{ "ComboBoxType", ComboBoxType },
{ "PickerType", PickerType },
{ "ProgressBarType", ProgressBarType },
{ "SpinBoxType", SpinBoxType},
{ "CustomEditorType", CustomEditorType}
};
XQItem::XQUnitTypeMap XQItem::s_UnitTypeMap
{
{ NoUnitType, "NoUnitType" },
{ Ampere, "A" },
{ Volt, "V" },
{ Ohm, "Ohm" },
{ Farad, "C" },
{ Watt, "W" },
{ WattPeak, "Wp" },
{ WattHour, "Wh" },
{ Second, "s" },
{ Percent, "%" },
{ Hertz, "Hz" },
{ Meter, "m" },
{ Kg, "kg" },
{ ISODate, "ISODate" }, // fixme: ISO-Date is present, but has no Unit ?!?
};
XQItem::XQPrefixExponentMap XQItem::s_PrefixExponentMap
{
{ "p", -12 }, // pico
{ "n", -9 }, // nano
{ "µ", -6 }, // micro
{ "m", -3 }, // Milli
{ "" , 0 }, // No prefix means multiplier of 1
//{ " ", 0 }, // No prefix means multiplier of 1
{ "k", 3 }, // Kilo
{ "M", 6 }, // Mega
{ "G", 9 }, // Giga
{ "T", 12 }, // Tera
{ "P", 15 }, // Peta
{ "E", 18 }, // Exa
{ "Z", 21 }, // Zetta
{ "Y", 24}, // Yotta
};
XQItem::XQItem()
: XQItem{XQItemType::staticItemType()}
{
setFlags(Qt::NoItemFlags);
//setText("[dummy]");
//XQItemType::setStaticType( this );
//setItemType(NTItemType::defaultItemType());
}
XQItem::XQItem( XQItemType* itemType )
: QStandardItem{}
{
setItemType( itemType );
}
XQItem::XQItem(XQItemType* itemType, const QString *content )
: XQItem{ itemType }
{
setContent(content);
}
// Warum beides?
XQItem::XQItem(XQItemType* itemType, const QString *content, const XQNodePtr& contentNode )
: XQItem{ itemType, content }
{
setContentNode(contentNode);
}
/// ------------------ forbidden fruits ------------------
XQItem::XQItem(const XQItem& other)
: QStandardItem( other )
{
// QStandardItem( other ) koopiert bereits
// die data() struktur
}
//! firz
XQItem::~XQItem()
{
// fixed choices lebt im item type, also
// im parent
//QAbstractItemModel* model = fixedChoices();
//if( model )
// delete model;
}
//! firz
XQItem* XQItem::clone() const
{
//return new XQItem( *this );
// this is used as invisible filling material
//return new XQItem( "", XQItemType::StaticStyle );
// __fix!
return new XQItem();
}
//! firz
bool XQItem::isValid() const
{
// fragwürdig
return QStandardItem::data( XQItem::ItemTypeRole ).value<XQItemType*>() != nullptr;
}
//! firz
XQNodePtr XQItem::contentNode() const
{
return data( ContentNodeRole ).value<XQNodePtr>();
}
//! firz
void XQItem::setContentNode( const XQNodePtr& contentNode )
{
QStandardItem::setData( QVariant::fromValue(contentNode), ContentNodeRole);
}
//! firz
XQNodePtr XQItem::sheetNode() const
{
//
return data( SheetNodeRole ).value<XQNodePtr>();
}
//! firz
void XQItem::setSheetNode(const XQNodePtr& sheetNode )
{
QStandardItem::setData( QVariant::fromValue(sheetNode), SheetNodeRole);
}
//! firz
bool XQItem::hasAttribute( const QString& attribKey ) const
{
return contentNode()->has_attribute( attribKey );
}
//! firz
const QString& XQItem::attribute( const QString& attribKey, const QString& defaultValue ) const
{
if( !hasAttribute(attribKey ) )
return defaultValue;
return contentNode()->attribute( attribKey );
}
//! firz
bool XQItem::testAttribute( const QString& attribKey, const QString& attribValue ) const
{
return contentNode()->test_attribute( attribKey, attribValue );
}
//! firz
XQItemType& XQItem::itemType() const
{
XQItemType* itemTypePtr = QStandardItem::data( XQItem::ItemTypeRole ).value<XQItemType*>();
return *itemTypePtr;
}
//! firz
void XQItem::setItemType( XQItemType* itemTypePtr )
{
// der ItemType wird direkt hier gespeichert
QStandardItem::setData( QVariant::fromValue(itemTypePtr), XQItem::ItemTypeRole );
}
//! firz
void XQItem::addFlag( Qt::ItemFlag newFlag )
{
setFlags( flags() | newFlag );
}
//! firz
void XQItem::clearFlag( Qt::ItemFlag newFlag )
{
setFlags( flags() & ~newFlag);
}
///
/// data() access shortcuts
///
//! firz
XQItem::RenderStyle XQItem::renderStyle() const
{
return data( RenderStyleRole ).value<RenderStyle>();
}
//! firz
QString XQItem::renderStyleToString() const
{
return XQItem::fetchRenderStyleToString( renderStyle() );
}
//! firz
void XQItem::setRenderStyle(RenderStyle renderStyle )
{
setData( QVariant::fromValue(renderStyle), XQItem::RenderStyleRole );
// Der RenderStyle wohnt im ItemType
//itemType().replaceAttribute( this, RenderStyleRole, renderStyle );
}
//! firz
XQItem::EditorType XQItem::editorType() const
{
return data( EditorTypeRole ).value<EditorType>();
}
//! firz
QString XQItem::editorTypeToString() const
{
return XQItem::fetchEditorTypeToString( editorType() );
}
//! firz
void XQItem::setEditorType(EditorType editorType)
{
setData( QVariant::fromValue(editorType), XQItem::EditorTypeRole);
// Der EditorType wohnt im ItemType
//itemType().replaceAttribute( this, EditorTypeRole, editorType );
}
//! firz
XQItem::UnitType XQItem::unitType() const
{
return data( XQItem::UnitTypeRole ).value<UnitType>();
}
//! firz
QString XQItem::unitTypeToString() const
{
return XQItem::fetchUnitTypeToString( unitType() );
}
//! firz
void XQItem::setUnitType(UnitType unitType)
{
setData( QVariant::fromValue(unitType), XQItem::UnitTypeRole);
}
//! firz
const QString& XQItem::content() const
{
const QString* contentPtr = QStandardItem::data( XQItem::ContentRole ).value<const QString*>();
if(contentPtr)
return *contentPtr;
static const QString s_dummyContent("-");
return s_dummyContent;
}
//! firz
void XQItem::setContent( const QString* content )
{
setData( QVariant::fromValue<const QString*>(content), XQItem::ContentRole );
}
//! firz
const QString& XQItem::contentKey() const
{
return contentNode()->attributes().key_of( content() );
}
//! gibt den content-format string zurück
QString XQItem::contentFormat() const
{
return data( XQItem::ContentFormatRole ).toString();
}
void XQItem::setContentFormat(const QString& contentFormat)
{
setData( QVariant::fromValue(contentFormat), XQItem::ContentFormatRole);
}
//! gibt das read-only auswahl-model zurück (wenn dieses item als
//! combobox gerendert wird)
QStandardItemModel* XQItem::fixedChoices() const
{
return data( XQItem::FixedChoicesRole ).value<QStandardItemModel*>();
}
//! erzeugt einen string aus den werten des read-only auswahl-models
QString XQItem::fixedChoicesToString() const
{
QStandardItemModel* model = fixedChoices();
if( !model || model->rowCount() == 0)
return QString("()");
QString result = "(";
int rc = model->rowCount();
for (int row = 0; row < rc; ++row)
{
const QString text = model->item(row)->text();
result += text;
if(row < rc-1)
result += "|";
}
result += ")";
return result;
}
//! setzt das auswahl-model für read-only comboboxes
void XQItem::setfixedChoices( QStandardItemModel* newModel )
{
setData( QVariant::fromValue(newModel), XQItem::FixedChoicesRole);
}
//! true, wenn 'ich' ein header item bin
bool XQItem::isHeaderStyle()
{
return renderStyle() == XQItem::HeaderStyle;
}
//! gibt den namen der datarole zurück
QString XQItem::dataRoleName(int role)
{
if( role < XQItem::NoRole && model() )
return model()->roleNames()[role];
return XQItem::fetchItemDataRoleName(role);
}
//! angespasste variante von qstandarditem::setData. geteilte attribute
//! werden vom xqitemtype geholt
QVariant XQItem::data(int role ) const
{
//emitDataChanged()
switch(role)
{
//
// Die im ItemType ausgelagerten Daten werden
// von da geholt.
//
case FlagsRole: // aka Qt::ItemDataRole(Qt::UserRole - 1),
case IconRole: // aka Qt::DecorationRole,
case RenderStyleRole:
case EditorTypeRole:
case UnitTypeRole:
case ContentFormatRole:
case FixedChoicesRole:
{
return itemType().data(role);
}
// Das ist der Sonderfall, hier dereferenzieren wir den
// Zeiger auf den QString* aus unserem XQNodePtr
case Qt::DisplayRole :
case Qt::EditRole :
case XQItem::ContentRole:
{
return content();
}
case Qt::ToolTipRole:
{
return content() + ":" + itemType().text() + ":" + renderStyleToString();
}
//
// Die lokal verwalteten Resourcen werden über QStandardItem::data(role)
// abgewickelt.
//
case ContentNodeRole:
{
// Das Node-Besitzer-Item wohnt in der ersten Spalte,
// wenn wir also der Node-Besitzer item sind ...
if( column() == 0)
return QStandardItem::data( XQItem::ContentNodeRole );
// sonst: delegieren an den node-Besitzer
QModelIndex pIndex = model()->index( row(), 0 );
XQItem& firstItem = xqItemFromIndex( pIndex );
return firstItem.data( XQItem::ContentNodeRole );
}
case Qt::StatusTipRole:
case Qt::WhatsThisRole:
case Qt::SizeHintRole:
case Qt::FontRole:
case Qt::TextAlignmentRole:
case Qt::BackgroundRole:
case Qt::ForegroundRole:
case Qt::CheckStateRole:
case Qt::InitialSortOrderRole:
default:
break;
}
return QStandardItem::data(role);
}
void XQItem::setData(const QVariant& value, int role )
{
//replaceAttribute( XQItem* item, XQItem::ItemDataRole role, const QVariant& newValue)
//emitDataChanged()
switch(role)
{
// das ist ein pointer auf den original-string aus dem XML
// ContentRole :
// ItemTypeRole :
case RenderStyleRole :
case EditorTypeRole :
case UnitTypeRole:
case ContentFormatRole:
case FlagsRole:
case IconRole:
case FixedChoicesRole:
//qDebug() << " ---call type set Data: " << role << ": " << XQItem::fetchItemDataRoleName(role) << ":" << value.toString();
itemType().replaceAttribute( this, value, role );
break;
case ContentNodeRole:
case SheetNodeRole:
case TypeKeyRole:
case Qt::DisplayRole :
case Qt::EditRole :
// return the raw, unformatted data
case ContentRole:
default:
QStandardItem::setData( value,role);
}
}
XQItem& XQItem::fallBackDummyItem()
{
static XQItem s_fallBackDummyItem;
return s_fallBackDummyItem;
}
XQItem& XQItem::xqItemFromIndex(const QModelIndex& index)
{
if (index.isValid())
{
const XQModel* mdl = dynamic_cast<const XQModel*>(index.model());
if (mdl)
return mdl->xqItemFromIndex(index);
}
return fallBackDummyItem();
}
int XQItem::fetchItemDataRole( const QString& dataRoleKey )
{
if(!dataRoleKey.isEmpty() && s_ItemDataRoleMap.contains(dataRoleKey) )
return s_ItemDataRoleMap[ dataRoleKey ];
return NoRole;
}
QString XQItem::fetchItemDataRoleName( int dataRole )
{
return s_ItemDataRoleMap.key(dataRole);
}
Qt::ItemFlag XQItem::fetchItemFlag( const QString& flagKey )
{
if(!flagKey.isEmpty() && s_ItemFlagMap.contains(flagKey) )
return Qt::ItemFlag( s_ItemFlagMap[ flagKey ] );
return Qt::NoItemFlags;
}
QString XQItem::fetchItemFlagName( int flag )
{
return s_ItemFlagMap.key(flag);
}
XQItem::RenderStyle XQItem::fetchRenderStyle(const QString& styleKey )
{
if(!styleKey.isEmpty() && s_RenderStyleMap.contains(styleKey) )
return s_RenderStyleMap[styleKey];
return NoRenderStyle;
}
QString XQItem::fetchRenderStyleToString(XQItem::RenderStyle renderStyle )
{
return s_RenderStyleMap.key(renderStyle);
}
XQItem::EditorType XQItem::fetchEditorType( const QString& editorTypeKey )
{
if(!editorTypeKey.isEmpty() && s_EditorTypeMap.contains(editorTypeKey) )
return s_EditorTypeMap[editorTypeKey];
return NoEditorType;
}
QString XQItem::fetchEditorTypeToString( EditorType editorType )
{
return s_EditorTypeMap.key(editorType);
}
XQItem::UnitType XQItem::fetchUnitType(const QString& unitTypeKey)
{
return s_UnitTypeMap.key(unitTypeKey);
}
QString XQItem::fetchUnitTypeToString( UnitType unitType)
{
return s_UnitTypeMap[unitType];
}

302
src/items/xqitem.h Normal file
View File

@@ -0,0 +1,302 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQITEM_H
#define XQITEM_H
#include <QVariant>
#include <QVector>
#include <QStandardItem>
#include <xqnode.h>
using XQItemList = QList<QStandardItem*>;
//class QStyleOptionViewItem;
class XQItemFactory;
class XQItemType;
/**
* @brief Extends QStandardItem to hold additional
* settings.
*/
class XQItem : public QStandardItem
{
public:
//friend class XQItemType;
//friend class XQItemFactory;
/// Die data(enum role) Infrastruktur wird sowohl für XQItem als auch
/// für den XQItemType verwendet, deshalb definieren wir hier _alle_
/// notwendigen Roles
enum ItemDataRole
{
NoRole = Qt::UserRole + 1,
// das ist ein pointer auf den original-string aus dem XML
ContentRole,
ItemTypeRole,
RenderStyleRole,
EditorTypeRole,
UnitTypeRole,
ContentFormatRole,
// @see qstandarditemmodel.cpp, stolen from there
FlagsRole = Qt::ItemDataRole(Qt::UserRole - 1),
IconRole = Qt::DecorationRole,
// re-start count
FixedChoicesRole = ContentFormatRole+1,
ContentNodeRole,
//?? werden beide gebraucht?
SheetNodeRole,
// das ist der Schlüssel um den content string aus dem contentNode zu holen
//ContentKeyRole,
// weitere, weniger gebräuchlichen Rollen für XQItemType::data()
TypeKeyRole,
//TypeNameRole, nicht so wichtig
/*
renderStyleToStringRole,
editorTypeToStringRole,
unitTypeToStringRole,
*/
RoleEnd
};
// wie wirds gemalt
enum RenderStyle
{
NoRenderStyle,
HiddenStyle,
HeaderStyle,
PlainStyle,
CheckBoxStyle,
ComboBoxStyle,
PickerStyle,
SpinBoxStyle,
ProgressBarStyle,
FormattedStyle,
TreeHeaderStyle,
CustomRenderStyle,
RenderStyleEnd //!< Not a special editor. Keep at end of this enumeration!
// ...
/*
PickerStyle
LineEditStyle ,
UserDefStyle ,
*/
};
// wie wirds editiert
enum EditorType
{
NoEditorType,
LineEditType,
ComboBoxType,
PickerType,
ProgressBarType,
SpinBoxType,
CustomEditorType,
EditorTypeEnd
};
// typische Einheiten
enum UnitType
{
NoUnitType,
Ampere,
Volt,
Ohm,
Watt,
WattPeak,
WattHour,
Farad,
Tesla,
Henry,
Hertz,
Coulomb,
Kelvin,
Percent,
Second,
Meter,
Kg,
ISODate,
UnitTypeEnd
};
//XQItem(int rows, int columns = 1);
XQItem();
XQItem( XQItemType* itemType );
XQItem( XQItemType* itemType, const QString* content );
XQItem( XQItemType* itemType, const QString* content, const XQNodePtr& contentNode );
/*
XQItem(const QString& text);
XQItem(const QIcon& icon, const QString& text);
XQItem(int rows, int columns);
*/
XQItem(const XQItem& other);
XQItem(const QStandardItem& other);
virtual ~XQItem();
//! creates not a clone but a new default item, \see QStandardItemModel::setItemPrototype()
//! -- not used at the moment --
XQItem* clone() const override;
bool isValid() const;
// shortcuts auf XQNodePtr
virtual XQNodePtr contentNode() const;
virtual void setContentNode(const XQNodePtr& contentNode );
virtual XQNodePtr sheetNode() const;
virtual void setSheetNode( const XQNodePtr& sheetNode );
bool hasAttribute( const QString& attribKey ) const;
const QString& attribute( const QString& attribKey, const QString& defaultValue="" ) const;
bool testAttribute( const QString& attribKey, const QString& attribValue ) const;
XQItemType& itemType() const;
void setItemType( XQItemType* itemTypePtr );
// shortcuts für die itemFlags
// __fix! das können die selber !?
void addFlag( Qt::ItemFlag newFlag );
void clearFlag( Qt::ItemFlag newFlag );
//das ist ein Sonderfall: Ein ist ein dereferenzierter Zeiger auf 'unser' Atrribut in
// XQNodePtr, also unserem contentNode(). Das wird hier direkt aufgelöst und nicht auf
// data() umgeleitet.
const QString& content() const;
const QString& contentKey() const;
void setContent( const QString* content );
//
// Convenience-Funktionen zum Memberzugriff, die Implementierung
// läuft über die data()-Methode wie in den QStandardItems. So
// ist sie für XQItem und auch für XQItemType als Deirvat von XQItem gültig.
//
RenderStyle renderStyle() const;
QString renderStyleToString() const;
void setRenderStyle(RenderStyle renderStyle );
EditorType editorType() const;
QString editorTypeToString() const;
void setEditorType(EditorType editorType);
UnitType unitType() const;
QString unitTypeToString() const;
void setUnitType(UnitType unitType);
QString contentFormat() const;
void setContentFormat(const QString& contentFormat);
QStandardItemModel* fixedChoices() const;
QString fixedChoicesToString() const;
void setfixedChoices( QStandardItemModel* newModel );
//
//shortCuts
//
bool isHeaderStyle();
//! gibt den namen der datarole zurück
QString dataRoleName(int role);
//! angespasste variante von qstandarditem::data
QVariant data(int role = Qt::DisplayRole ) const override;
//! angespasste variante von qstandarditem::setData
void setData(const QVariant &value, int role ) override;
// Das sind die die items im tree links: icon,text, node pointer
//XQItem( const QString& text, XQNodePtr contentNode );
//XQItem( const QString& text, XQItemType::RenderStyle renderStyle );
/*
template<class T>
void setToVariant(T entry, QtExtUserRoles::NTDataRoles role)
{
setData(QVariant::fromValue<T>(entry), role);
}
template<class T>
T getFromVariant(QtExtUserRoles::NTDataRoles role)
{
return data(role).value<T>();
}
*/
///
/// Static convenience methods
///
static XQItem& xqItemFromIndex( const QModelIndex& index );
static XQItem& fallBackDummyItem();
static int fetchItemDataRole( const QString& dataRoleKey );
static QString fetchItemDataRoleName( int dataRole );
static Qt::ItemFlag fetchItemFlag( const QString& flagKey );
static QString fetchItemFlagName( int flag );
static RenderStyle fetchRenderStyle( const QString& styleKey );
static QString fetchRenderStyleToString( RenderStyle renderStyle );
static EditorType fetchEditorType( const QString& editorTypeKey );
static QString fetchEditorTypeToString( EditorType editorType );
static UnitType fetchUnitType( const QString& typeKey );
static QString fetchUnitTypeToString( UnitType );
protected:
using XQItemFlagMap = QMap<QString,int>;
using XQItemDataRoleMap = QMap<QString,int>;
using XQRenderStyleMap = QMap<QString,RenderStyle>;
using XQEditorTypeMap = QMap<QString,EditorType>;
using XQUnitTypeMap = QMap<UnitType, QString>;
using XQPrefixExponentMap = QMap<QString, int>;
static XQItemFlagMap s_ItemFlagMap;
static XQItemDataRoleMap s_ItemDataRoleMap;
static XQRenderStyleMap s_RenderStyleMap;
static XQEditorTypeMap s_EditorTypeMap;
static XQUnitTypeMap s_UnitTypeMap;
static XQPrefixExponentMap s_PrefixExponentMap;
};
Q_DECLARE_METATYPE(XQItem::RenderStyle);
Q_DECLARE_METATYPE(XQItem::EditorType);
Q_DECLARE_METATYPE(XQItem::UnitType);
Q_DECLARE_METATYPE(const QString*);
#endif // XQITEM_H

View File

@@ -0,0 +1,291 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QApplication>
#include <QItemEditorFactory>
#include <QLineEdit>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QProgressBar>
#include <QPainter>
#include <QHeaderView>
#include <QCommonStyle>
#include <xqitemdelegate.h>
#include <xqtreeview.h>
#include <xqitemtype.h>
#include <xqmodel.h>
class XQItemEditorFactory : public QItemEditorFactory
{
public:
XQItemEditorFactory()
{
registerEditor(XQItem::LineEditType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::ComboBoxType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::PickerType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::ProgressBarType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::SpinBoxType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::CustomEditorType, new QStandardItemEditorCreator<QLineEdit>());
/*
registerEditor(XQItem::LineEditStyle, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItemType::ComboBoxStyle, new QStandardItemEditorCreator<QComboBox>());
//registerEditor(XQItemType::ProgressBarStyle, new QStandardItemEditorCreator<QProgressBar>());
registerEditor(XQItemType::SpinBoxStyle, new QStandardItemEditorCreator<QSpinBox>());
*/
/*
registerEditor(XQItem::etDoubleSpinType, new QStandardItemEditorCreator<QDoubleSpinBox>());
registerEditor(XQItemItemTypes::etDoubleSpinType, new QStandardItemEditorCreator<QDoubleSpinBox>());
registerEditor(XQItemItemTypes::etIPAddressType, new QStandardItemEditorCreator<NTIpAddressEdit>());
registerEditor(XQItemItemTypes::etLineEditBrowser, new QStandardItemEditorCreator<NTFileSelectLine>());
*/
}
};
XQItemDelegate::XQItemDelegate( XQModel& modelView)
: _modelView{modelView}
{
static XQItemEditorFactory s_EditorFactory;
setItemEditorFactory(&s_EditorFactory);
}
XQTreeView* XQItemDelegate::treeView() const
{
return _modelView.treeView();
}
XQItem& XQItemDelegate::xqItemFromIndex( const QModelIndex& index ) const
{
return _modelView.xqItemFromIndex( index );
}
QWidget* XQItemDelegate::prepareHeaderOption(const QStyleOptionViewItem& option, const QModelIndex& index, QStyleOptionHeader& headerOption) const
{
// use the header as "parent" for style init
QWidget* srcWidget = treeView()->header();
headerOption.initFrom(srcWidget);
headerOption.text = index.data(Qt::DisplayRole).toString();
headerOption.rect = option.rect;
headerOption.styleObject = option.styleObject;
// __ch: reduce inner offset when painting
headerOption.textAlignment |= Qt::AlignVCenter;
return srcWidget;
}
void XQItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if( !index.isValid() )
qDebug() << " index DEAD!";
XQItem& item = xqItemFromIndex( index );
if( item.isValid() )
{
switch( item.renderStyle() )
{
case XQItem::HeaderStyle :
return drawHeaderStyle( painter, option, index );
case XQItem::ComboBoxStyle :
return drawComboBoxStyle( painter, option, index );
case XQItem::HiddenStyle :
return;
//case XQItem::ProgressBarStyle :
// return drawProgressBarStyle( painter, option, index );
default:
break;
} // switch
}
QStyledItemDelegate::paint(painter, option, index);
}
void XQItemDelegate::drawHeaderStyle(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionHeader headerOption;
QWidget* srcWidget = prepareHeaderOption(option, index, headerOption);
if (srcWidget != nullptr)
{
// save painter
painter->save();
//value = index.data(Qt::ForegroundRole);
//if (value.canConvert<QBrush>())
//headerOption.palette.setBrush(QPalette::Text, Qt::red );
//headerOption.palette.setBrush(QPalette::Window, Qt::red );
QCommonStyle itemStyle;
//headerOption.backgroundBrush()
//srcWidget->style()->drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
itemStyle.drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
// restore painter
painter->restore();
}
}
void XQItemDelegate::drawProgressBarStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
int progress = index.data(XQItem::ContentRole ).toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = option.rect;
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textAlignment = Qt::AlignCenter;
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,&progressBarOption, painter);
}
void XQItemDelegate::drawComboBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* srcWidget = qobject_cast<QWidget*>(option.styleObject);
QStyleOptionComboBox comboOption;
QStyle* comboStyle = srcWidget->style();
comboOption.initFrom(srcWidget);
// set options
comboOption.rect = option.rect;
comboOption.state = option.state | QStyle::State_Selected | QStyle::State_Enabled;
// not editable => only visual, but painter needs to know it
comboOption.editable = false;
comboOption.currentText = index.data(Qt::DisplayRole).toString();
// decoration (if any)
comboOption.currentIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
comboOption.iconSize = comboOption.currentIcon.actualSize(QSize(option.rect.height() - 3, option.rect.height() - 3));
// save painter
painter->save();
// draw combo
comboStyle->drawComplexControl(QStyle::CC_ComboBox, &comboOption, painter, srcWidget);
// and combobox label
comboStyle->drawControl(QStyle::CE_ComboBoxLabel, &comboOption, painter, srcWidget);
// restore painter
painter->restore();
}
void XQItemDelegate::drawSpinBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// xx_fix!
//int value = index.data(XQItem::ContentRole ).toInt();
QStyleOptionSpinBox spinBoxOption;
spinBoxOption.rect = option.rect;
/*
spinBoxOption.text = QString::number(value);
spinBoxOption.textAlignment = Qt::AlignCenter;
spinBoxOption.textVisible = true;
*/
QApplication::style()->drawComplexControl(QStyle::CC_SpinBox,&spinBoxOption, painter);
}
QSize XQItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QStyledItemDelegate::sizeHint(option, index);
}
QWidget* XQItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QStyledItemDelegate::createEditor( parent, option, index );
int editorType = XQItem::xqItemFromIndex(index).editorType();
QWidget* editor = itemEditorFactory()->createEditor(editorType, parent);
if( editor )
{
return editor;
}
}
void XQItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
XQItem& item = xqItemFromIndex( index );
qDebug() << " --- ZZZ: setEditorData: " << item.text();
switch( item.editorType() )
{
case XQItemType::ComboBoxType :
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
comboBox->setModel( item.fixedChoices());
comboBox->setCurrentText( item.data().toString() );
comboBox->showPopup();
return;
}
default:
break;
}
QStyledItemDelegate::setEditorData(editor, index);
}
void XQItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
XQItem& item = xqItemFromIndex( index );
switch( item.editorType() )
{
case XQItem::ComboBoxType :
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
return;
}
default:
break;
}
QStyledItemDelegate::setModelData(editor, model, index);
}
void XQItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
//qDebug() << " --- update Editor Geometry";
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}

View File

@@ -0,0 +1,60 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQITEMDELEGATE_H
#define XQITEMDELEGATE_H
#include <QStyledItemDelegate>
#include <xqappdata.h>
class XQItem;
class XQTreeView;
class XQModel;
/**
* @brief A specialized QItemDelegate class to draw different XQItem styles.
*/
class XQItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit XQItemDelegate(XQModel& modelView);
XQTreeView* treeView() const;
XQItem& xqItemFromIndex( const QModelIndex& index ) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
protected:
QWidget* prepareHeaderOption(const QStyleOptionViewItem& option, const QModelIndex& index, QStyleOptionHeader& headerOption) const;
void drawHeaderStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawProgressBarStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawComboBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawSpinBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
XQModel& _modelView;
};
#endif // XQITEMDELEGATE_H

347
src/items/xqitemfactory.cpp Normal file
View File

@@ -0,0 +1,347 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqitemfactory.h>
#include <xqexception.h>
#include <xqdocumentstore.h>
#include <xqmodel.h>
#include <xqitemtype.h>
#include <znode_factory.h>
void XQItemFactory::initItemFactory( const QString& modelSheetFileName )
{
auto configureItemType = [&, this](XQItemType* itemType, const XQNodePtr& sheetNode )
{
// über alle attribute
for( const auto& [key,value] : sheetNode->attributes() )
{
setItemDataFromString( *itemType, key, value );
}
};
// schritt #1: modelbeschreibung laden
XQNodeFactory treeLoader;
_modelSheet = treeLoader.load_tree( qPrintable(modelSheetFileName) );
// schritt #2: model root testen
if (!_modelSheet)
throw XQException("modelSheet load failed. ", modelSheetFileName);
// schritt #3: itemtype beschreibungen laden ...
_typesSheet = _modelSheet->find_child_by_tag_name( "ItemTypes" );
// ... und testen
if( !_typesSheet )
throw XQException( "initItemFactory typeSheetRoot is null" );
// alle itemtype vorlagen erzeugen
for( const XQNodePtr& typeSheetNode : _typesSheet->children())
{
XQItemType* itemType = new XQItemType;
//const QString& typeName = typeSheetNode->tag_name();
const QString& typeName = typeSheetNode->tag_name();
configureItemType(itemType, typeSheetNode);
itemType->setText( typeName);
s_ItemTypeCache[typeName] = itemType;
}
}
bool XQItemFactory::isValid()
{
return _modelSheet && _typesSheet;
}
XQItemType* XQItemFactory::findItemType(const QString& key ) const
{
if( !key.isEmpty() && s_ItemTypeCache.contains(key))
return s_ItemTypeCache[key];
throw XQException( "itemfactory: findItemType: not found:", key );
}
XQNodePtr XQItemFactory::findModelSheet( const QString& modelName ) const
{
XQNodePtr modelSheet = _modelSheet->find_child_by_tag_name( modelName );
if( !modelSheet )
throw XQException( "model sheet not found: ", modelName );
return modelSheet;
}
XQItem* XQItemFactory::makeContentItem( const XQNodePtr& contentNode, const XQNodePtr& sheetEntry ) const
{
// den itemtype des neuen items rausfinden
QString typeKey = sheetEntry->attribute("ItemType");
XQItemType* itemType = findItemType(typeKey); // throws
const QString* contentPtr = contentNode->attribute_ptr( sheetEntry->tag_name() );
XQItem* newItem = new XQItem( itemType, contentPtr );
return newItem;
}
XQItem* XQItemFactory::makeHeaderItem( const XQNodePtr& sheetEntry ) const
{
// header items are all non-data items:
// - section header row items
// - main tree header items
// - main tree child items
// - also: static items, hidden items
// den itemtype des neuen items rausfinden
QString typeKey = sheetEntry->attribute("HeaderItemType");
XQItemType* itemType = findItemType(typeKey); // throws
// das ist Unterschied zum normalen Item: Der Titel kommt aus der Modelbeschreibung
const QString* contentPtr = sheetEntry->attribute_ptr("HeaderCaption");
XQItem* newHeaderItem = new XQItem( itemType, contentPtr );
return newHeaderItem;
}
void XQItemFactory::setItemDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const
{
int dataRole = XQItem::fetchItemDataRole( roleKey );
if( dataRole != XQItem::NoRole)
{
QVariant variant = makeVariant( dataRole, source );
if( !variant.isNull() && variant.isValid() )
item.setData( variant, dataRole );
}
}
QVariant XQItemFactory::makeVariant( int dataRole, const QString& source ) const
{
QVariant value;
switch(dataRole)
{
// das ist ein pointer auf den original-string aus dem XML
case XQItem::ContentRole:
{
// content() -> QString*
value = QVariant::fromValue(&source);
break;
}
case XQItem::ItemTypeRole:
{
// itemType() -> XQItemType*
XQItemType* itemType = findItemType( source );
value = QVariant::fromValue(itemType);
break;
}
case XQItem::RenderStyleRole:
{
XQItem::RenderStyle renderStyle = XQItem::fetchRenderStyle( source );
value = QVariant::fromValue(renderStyle);
break;
}
case XQItem::EditorTypeRole:
{
XQItem::EditorType editorType = XQItem::fetchEditorType( source );
value = QVariant::fromValue(editorType);
break;
}
case XQItem::UnitTypeRole:
{
XQItem::UnitType unitType = XQItem::fetchUnitType( source );
value = QVariant::fromValue(unitType);
break;
}
case XQItem::ContentFormatRole:
{
// contentFormat() -> QString
value = QVariant::fromValue(source);
break;
}
case XQItem::FlagsRole:
{
QFlags itemFlags = Qt::NoItemFlags;
const QStringList flagKeys = source.split( '|' );
for( const QString& flagKey : flagKeys )
{
Qt::ItemFlag flag = XQItem::fetchItemFlag( flagKey );
itemFlags.setFlag( flag );
}
value = QVariant::fromValue(itemFlags);
break;
}
case XQItem::IconRole:
{
QIcon typeIcon;
if(XQAppData::hasTypeIcon(source))
typeIcon = XQAppData::typeIcon(source);
value = QVariant::fromValue(typeIcon);
break;
}
case XQItem::FixedChoicesRole:
{
const QStringList choices = source.split( '|' );
QStandardItemModel* fixedChoices = new QStandardItemModel();
for( const QString& entry : choices )
fixedChoices->appendRow( new QStandardItem( entry ) );
value = QVariant::fromValue(fixedChoices);
break;
}
/*
case XQItem::ContentNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
case XQItem::XQItem::SheetNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
*/
default:
case XQItem::XQItem::NoRole:
{
break;
}
};
//if( !value.toString().isEmpty())
// setData( value, dataRole);
return value;
}
///
/// ------------------------------------------------
///
XQItemList XQItemFactory::makeHeaderRow( const XQNodePtr& sheetNode )
{
XQItemList list;
// Die Kinder des Knotens beschreiben die einzelnen
// Attribute des XML-Datenknotens
for( const auto& attrNode : sheetNode->children() )
{
// ??
//if(attrNode->has_children() )
// continue;
XQItem* headerItem = makeHeaderItem( attrNode );
list.append( headerItem );
}
if( !list.empty() )
{
// wir merken uns den original content node auch, aber
// im ersten Item.
dynamic_cast<XQItem*>(list[0])->setContentNode(sheetNode);
// brauchen wir den noch?
dynamic_cast<XQItem*>(list[0])->setSheetNode(sheetNode);
}
return list;
}
// no clone here !
XQItemList XQItemFactory::makeContentRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode )
{
XQItemList list;
// - Gehe über alle Einträge der Typbeschreibung:
//
// <Battery>
// <Voltage .../>
// -> <Capacity ../>
//
// - Nimm das dazugehörige Attribut aus dem contentNode
// value = contentNode->attributes["Capacity"];
//
for( const auto& sheetEntry : sheetNode->children() )
{
list.append( makeContentItem( contentNode, sheetEntry ) );
}
if( !list.empty() )
{
// wir merken uns den original content node auch, aber
// im ersten Item.
dynamic_cast<XQItem*>(list[0])->setContentNode(contentNode);
}
return list;
}
XQItemList XQItemFactory::makeEmptyRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode )
{
Q_UNUSED(contentNode)
XQItemList list;
// create a data node for each sheet entry
size_t max = sheetNode->children().size();
for( size_t i=0; i<max; ++i )
{
// __fix
//list.append( new XQItem( "", XQItemType::EmptyStyle ) );
}
return list;
}
XQItemList XQItemFactory::createGenericRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode )
{
// we have a new empty contentNode, so we add attributes first.
for( const auto& sheetEntry : sheetNode->children() )
{
QString value = "[" + sheetEntry->tag_name() + "]";
if( sheetEntry->has_attribute("Unit") )
value = "0";
contentNode->set_attribute( sheetEntry->tag_name(), value );
}
if( sheetNode->has_attribute( c_FriendlyName ) )
contentNode->set_attribute( c_FriendlyName, sheetNode->friendly_name() );
// now, we can create a normal entry row
return makeContentRow(contentNode, sheetNode );
}

68
src/items/xqitemfactory.h Normal file
View File

@@ -0,0 +1,68 @@
/* **************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQITEMFACTORY_H
#define XQITEMFACTORY_H
#include <xqitem.h>
#include <xqitemtype.h>
#include <functional>
class XQModel;
// erzeugt items aus XQNodes
class XQItemFactory : public xsingleton<XQItemFactory>
{
public:
void initItemFactory(const QString& modelSheetFileName );
XQNodePtr findModelSheet( const QString& modelName ) const;
XQItem* makeHeaderItem(const XQNodePtr& typeSheetNode ) const;
XQItem* makeContentItem( const XQNodePtr& contentNode, const XQNodePtr& sheetEntry ) const;
virtual XQItemList makeHeaderRow( const XQNodePtr& sheetNode );
virtual XQItemList makeContentRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode );
virtual XQItemList makeEmptyRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode );
// wozu ist das gut?
virtual XQItemList createGenericRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode );
void setItemDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const;
// __fix! unsinn!
XQItemType* findItemType(const QString& key ) const;
QVariant makeVariant(int dataRole, const QString &value ) const;
protected:
bool isValid();
// shortcuts
using ItemConfigFunc = std::function<void( XQItem* item, const QString& attrValue, XQNodePtr contentNode, XQNodePtr sheetNode )>;
using ItemConfigMap = QMap<QString,ItemConfigFunc>;
XQItemTypeMap s_ItemTypeCache;
// Beschreibung des XQModels
XQNodePtr _modelSheet{};
// Beschreibung der ItemTypes
XQNodePtr _typesSheet{};
};
#endif // XQITEMFACTORY_H

259
src/items/xqitemtype.cpp Normal file
View File

@@ -0,0 +1,259 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
//#include <cmath>
#include <xqitemtype.h>
#include <QDateTime>
#include <QDebug>
XQItemTypeMap XQItemType::s_ItemTypeMap;
size_t XQItemType::s_ItemTypeCount = 0;
///
/// XQItemType
///
XQItemType::XQItemType()
: XQItem(nullptr) // vermeide rekursion
{
}
XQItemType::XQItemType( const XQItemType& other)
: XQItem( other )
{
}
XQItemType::~XQItemType()
{
}
QVariant XQItemType::data( int role ) const
{
return QStandardItem::data(role);
}
void XQItemType::setData(const QVariant &value, int role )
{
return QStandardItem::setData(value,role);
}
void XQItemType::replaceAttribute( XQItem* item, const QVariant& newValue, int role )
{
// hat sich überhaupt was geändert?
QVariant oldValue = data(role);
// ja, es hat
if( oldValue != newValue )
{
XQItemType* myClone = new XQItemType(*this);
// Änderungen übernehmen
myClone->setData( newValue, role );
// Gibt es den geänderten ItemType schon?
QString newKey = myClone->makeItemTypeKey();
// jawoll
if( s_ItemTypeMap.contains( newKey ) )
{
// nur abräumen, sonst nix
delete myClone;
}
else
{
// speichern
s_ItemTypeMap.insert( newKey, myClone );
// und ins item übernehmen
item->setItemType( myClone );
/// Obacht! Der alte, geänderte itemType bleibt erhaltent
/// und verrottet ggf. ohne Daseinszweck
}
}
}
QString XQItemType::formatToSI( const QString& valueTxt ) const
{
/*
if( valueTxt.isEmpty() )
return valueTxt;
if( XQItem::ISODate == _unitType )
{
// format iso date
QDateTime dateTime = QDateTime::fromString(valueTxt, Qt::ISODate);
// fixme! make this configurable!
QString format = "dd.MM.yyyy HH:mm:ss"; // You can customize this format
// Format the QDateTime object into a human-readable string
return dateTime.toString(format);
}
QLocale sysLocale = QLocale::system();
sysLocale.setNumberOptions(sysLocale.numberOptions() | QLocale::OmitGroupSeparator);
double dVal = sysLocale.toDouble(valueTxt);
QString strVal, strPrefix;
int exp = (int)::log10f(dVal);
exp = (exp/3)*3;
double nVal = dVal;
if( !s_PrefixExponentMap.key(exp).isEmpty() )
nVal /= ::pow( 10,exp);
strVal = sysLocale.toString(nVal, 'f', 2);
strPrefix = s_PrefixExponentMap.key(exp);
//qDebug() << " convert: " << dVal << " : " << valueTxt << ": " << strVal << ":" << exp << " : " << strPrefix << ": " << nVal;
return QString("%1 %2%3").arg( strVal, strPrefix, unitTypeToString() );
*/
return "fitze!";
}
QString XQItemType::unFormatFromSI(const QString& formText ) const
{
/*
QString input = formText.simplified();
// #1: strip numeric part
if( input.isEmpty() )
return input;
int idx = 0;
for( auto c : input )
{
if( c.isNumber() || c.toLower() == 'e' || c == '.' || c == ',' ||c == '-' || c == '+' )
idx++;
else
break;
}
if(!idx)
return QString("0");
QString numPart = formText.left(idx);
QString unitPart;
//if(idx + 1 < formText.size() )
unitPart = formText.right(idx - 1).simplified();
QLocale sysLocale = QLocale::system();
double dVal = sysLocale.toDouble(numPart);
if( unitPart.size() > 0 )
{
QString prefix = QString(unitPart[0]);
if( s_PrefixExponentMap.contains(prefix) )
dVal *= std::pow( 10.0, s_PrefixExponentMap[prefix] );
}
sysLocale.setNumberOptions(sysLocale.numberOptions() | QLocale::OmitGroupSeparator);
QString result = sysLocale.toString(dVal, 'f', 2);
//qDebug() << " convert: " << numPart << " : " << unitPart << " : " << dVal << " : " << result;
return result;
*/
return "fitze!";
}
///
/// --- statics --------------------------------------------------------------------------
///
XQItemType* XQItemType::staticItemType()
{
static XQItemType s_DummyItemType;
return &s_DummyItemType;
}
//
// Das ist eigentlich Blödsinn, KISS baby KISS!
// Die ItemTypes sollten statisch bleiben, zusätzliche
// oder geänderte Attribute eines Items landen dann
// halt in G.N. in QStadardItem::data Vector!
//
/*
XQItemType* XQItemType::storeItemType(XQItemType* protoType)
{
// haben wir den prototype schon?
QString itemTypeKey = protoType->makeItemTypeKey();
if(s_ItemTypeMap.contains( itemTypeKey ) )
{
// dann weg damit ...
delete protoType;
// ... und die alte Version zurückgeben
return s_ItemTypeMap[itemTypeKey];
}
// sonst: wir speichern den prototype
s_ItemTypeMap.insert( itemTypeKey, protoType);
s_ItemTypeCount++;
qDebug() << " --- ItemTypes: " << s_ItemTypeCount;
return protoType;
}
*/
QString XQItemType::makeItemTypeKey()
{
QString key("%1:%2:%3:%4:%5:%6:%7");
key = key.arg( renderStyleToString() );
key = key.arg( editorTypeToString() );
key = key.arg( unitTypeToString() );
key = key.arg( contentFormat() );
key = key.arg( data(XQItem::FlagsRole).toString() );
key = key.arg( icon().name() );
key = key.arg( fixedChoicesToString() );
/*
static const QList<XQItem::ItemDataRole> roleList
{
XQItem::RenderStyleRole,
XQItem::EditorTypeRole,
XQItem::UnitTypeRole,
XQItem::ContentFormatRole,
XQItem::FlagsRole,
XQItem::IconRole,
XQItem::FixedChoicesRole
};
QString key;
for( const auto role : roleList )
{
qDebug() << " --- YYY trying: " << XQItem::fetchItemDataRoleName( role );
QVariant entry = data(role);
if( !entry.isNull() && entry.isValid())
{
// fckin' sonderlocke:
key += role == XQItem::IconRole ? entry.value<QIcon>().name() : entry.toString();
}
key += ":";
}
*/
//qDebug() << " --- YES: KEY: " << key;
return key;
}

86
src/items/xqitemtype.h Normal file
View File

@@ -0,0 +1,86 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQITEMTYPE_H
#define XQITEMTYPE_H
#include <QDebug>
#include <QMap>
#include <xqitem.h>
///
/// Ist das Unsinn!? Einfach ein QStandardItem mit data() nehmen?
/// Ok, das erspart die die attribs, aber wo ist der fette gewinn?
/// statt _editorType = x hast Du dann halt setData( QVariant::fromValue<>(x) );
/// Aber: Du kannst T abbilden auf QString ... und dann
///
using XQItemTypeMap = QMap<QString, XQItemType*>;
//class XQItemType : public QObject
class XQItemType : public XQItem// public QObject
{
//
//Q_OBJECT
// wäre auch eine möglichkeit !?
//Q_PROPERTY(XQItem::RenderStyle renderStyle renderStyle getDate WRITE setrenderStyle )
public:
XQItemType();
XQItemType( const XQItemType& other);
virtual ~XQItemType();
QVariant data( int role ) const override;
void setData(const QVariant& value, int role ) override;
// FIX! Das gehört hier nicht her!
QString formatToSI(const QString& rawText ) const;
QString unFormatFromSI(const QString& valueText ) const;
void replaceAttribute( XQItem* item, const QVariant& newValue, int role );
QString makeItemTypeKey();
//static XQItemType* storeItemType( XQItemType* protoType );
static XQItemType* staticItemType();
protected:
static XQItemTypeMap s_ItemTypeMap;
static size_t s_ItemTypeCount;
/*
static void setItemType( XQItem* item, RenderStyle renderStyle, Qt::ItemFlags flags, UnitType unitType, const QString& format );
static void setStaticType( XQItem* item );
*/
// fix __ch
// static void setItemType( XQItem* item, const QString& renderStyle, const QString& unitType, const QString& itemTypeID );
//static XQItemType* addItemType( const QString& key, RenderStyle renderStyle, UnitType unitType);
//static XQItemType* makeItemType( RenderStyle renderStyle, UnitType unitType);
};
Q_DECLARE_METATYPE(XQItemType*);
#endif // XQITEMTYPE_H

70
src/main.cpp Normal file
View File

@@ -0,0 +1,70 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QDebug>
#include <QApplication>
#include <xqmainwindow.h>
/*
TODO:
- kann ich die Enums auto generieren?
- entries einfach abzählen,
- einen enum als type
- auf diesen type drauf-casten, siehe qtglobal.h
- FIX! in reality, we have nested types, also.
- done: reference style
- done: shared_ptr
- try QML
- try 'model->readMore'
doc:
- vorhandenes xnode konzept soll am qt angebunden werden
- datensparsam: flyweight pattern & pointer auf orig
who is who:
- item
-itemtype
- factory
- model
- section
*/
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//qDebug() << " lebt";
//app.setStyle("fusion");
XQMainWindow window;
window.show();
return app.exec();
}

134
src/model/xqcommand.cpp Normal file
View File

@@ -0,0 +1,134 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqcommand.h>
#include <xqmodel.h>
#include <xqtreeview.h>
void XQNodeStore::dumpList( const QString& title ) const
{
if( !title.isEmpty() )
qDebug() << " --- " << title;
for( const auto& entry : *this )
qDebug() << " -- dumpList: itemPos: " << entry.itemPos << " nodePos: " << entry.nodePos << " id: " << ( entry.contentNode ? entry.contentNode->_id : 0 ) << " used: " << ( entry.contentNode ? entry.contentNode.use_count() : 0 );
}
XQCommand::XQCommand(CmdType cmdType, XQModel* modelView )
: _cmdType{ cmdType }, _model(modelView)
{
}
XQCommand::~XQCommand()
{
qDebug() << " --- command destructor: " << toString();
}
XQCommand::CmdType XQCommand::commandType() const
{
return _cmdType;
}
void XQCommand::setCommandType( XQCommand::CmdType cmdType )
{
_cmdType = cmdType;
}
//! ruft 'onCommandRedo' 'meines' models auf.
void XQCommand::redo()
{
_model->onCommandRedo( *this );
}
//! ruft 'onCommandUndo' 'meines' models auf.
void XQCommand::undo()
{
_model->onCommandUndo( *this );
}
//! gibt den urpsrungs-index dieses commands zurück.
const QModelIndex& XQCommand::originIndex() const
{
return _originIndex;
}
//! merkt sich den ersten QModelIndex der von diesem command
//! betroffenen items. zusätzlich wird der command-text erzeugt.
void XQCommand::setOriginIndex( const QModelIndex& origin )
{
QString cmdText("%1: %2 (%3)");
QString name = origin.data().toString();
QString items("%1 item%2");
int mySize = size();
items = items.arg(mySize).arg(mySize > 1 ? "s" : "");
cmdText = cmdText.arg( toString(), name, items );
_originIndex = origin;
setText(cmdText);
}
//! erzeugt aus den 'selected indices' eine liste mit der jewiligen knotenposition,
//! der item-zeile und dem content-knoten.
void XQCommand::saveNodes( const QModelIndexList& list )
{
clear();
// über jede zeil
for( auto entry : list )
{
// knoten holen
const XQNodePtr& contentNode = XQItem::xqItemFromIndex( entry ).contentNode();
// hier speichern wir den original knoten, nicht einen clone, wie im clipboard.
push_back( {entry.row(), contentNode->own_pos(), contentNode } );
}
}
//! erzeugt einen string aus dem command-type, fürs debuggen.
QString XQCommand::toString()
{
static QMap<CmdType,QString> s_CmdTypeMap
{
{ cmdTextEdit, "cmdTextEdit" },
{ cmdInvalid, "cmdInvalid" },
{ cmdCut, "cmdCut" },
{ cmdPaste, "cmdPaste" },
{ cmdPasteSelf, "cmdPasteSelf" },
{ cmdNew, "cmdNew" },
{ cmdUndo, "cmdUndo" },
{ cmdRedo, "cmdRedo" },
{ cmdCopy, "cmdCopy" },
{ cmdMove, "cmdMove" },
{ cmdDelete, "cmdDelete" },
{ cmdToggleSection, "cmdToggleSection" },
{ cmdExtern, "cmdExtern" }
};
if( !s_CmdTypeMap.contains( _cmdType ))
return QString(" cmdType missmatch");
return s_CmdTypeMap[_cmdType];
}

107
src/model/xqcommand.h Normal file
View File

@@ -0,0 +1,107 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQCOMMAND_H
#define XQCOMMAND_H
#include <QUndoCommand>
#include <xqitem.h>
class XQModel;
struct XQNodeBackup
{
int itemPos{-1};
int nodePos{-1};
XQNodePtr contentNode;
};
class XQNodeStore : public QVector<XQNodeBackup>
{
public:
void dumpList( const QString& title="" ) const;
virtual void saveNodes( const QModelIndexList& list ) = 0;
};
// Das command enthält immer auch die betroffenen items
// ist also auch eine SavedNodeList
class XQCommand : public QUndoCommand, public XQNodeStore
{
public:
enum CmdType
{
cmdInvalid,
cmdUndo,
cmdRedo,
cmdTextEdit,
cmdCut,
cmdPaste,
cmdPasteSelf,
cmdCopy,
cmdMove,
cmdNew,
cmdDelete,
cmdToggleSection,
cmdExtern //??
};
XQCommand(CmdType cmdType, XQModel* modelView );
virtual ~XQCommand();
CmdType commandType() const;
void setCommandType( CmdType cmdType );
const QModelIndex& originIndex() const;
void setOriginIndex( const QModelIndex& origin );
void saveNodes( const QModelIndexList& list ) override;
void redo() override;
void undo() override;
QString toString();
protected:
CmdType _cmdType{cmdInvalid};
XQModel* _model{}; // needed for redo() / undo()
QModelIndex _originIndex;
/*
Du hast den item editor vergessen, Du Honk!
NTCompositeModel* m_pModel;
QModelIndex m_index;
QVariant m_value;
QVariant m_oldValue;
bool m_updateIndex;
*/
};
Q_DECLARE_METATYPE(XQCommand::CmdType);
#endif // XQCOMMAND_H

524
src/model/xqmodel.cpp Normal file
View File

@@ -0,0 +1,524 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QMessageBox>
#include <QUndoStack>
#include <xqexception.h>
#include <xqmodel.h>
#include <xqselectionmodel.h>
#include <xqtreeview.h>
#include <xqcommand.h>
#include <xqitemdelegate.h>
#include <xqitemfactory.h>
#include <znode_factory.h>
// create global dummy item as
// fallback return value (klappt nicht)
//Q_GLOBAL_STATIC(XQItem,s_dummyItem)
void showItemList( const XQItemList& list)
{
for(const auto& entry : list )
qDebug() << " --- itemList: " << ((XQItem*)entry)->content();
qDebug();
}
XQModel::~XQModel()
{
}
XQModel::XQModel( QObject* parent )
: QStandardItemModel{ parent }, _itemFactory{ XQItemFactory::instance() }
{
invisibleRootItem()->setData( "[rootItem]", Qt::DisplayRole );
setItemPrototype( new XQItem );
}
const XQItem& XQModel::xqRootItem()
{
// das ist ein hack, denn 'invisibleRootItem()' ist und bleibt ein
// QStandardItem. Trick: keine eigenen members in XQItem, alles
// dynamisch über den ItemData Mechanismus wie in QStandardItem
return *static_cast<XQItem*>(invisibleRootItem());
}
XQItem& XQModel::xqItemFromIndex(const QModelIndex& index) const
{
if( index.isValid() )
{
QStandardItem* xqItem = QStandardItemModel::itemFromIndex(index);
if( xqItem )
return *static_cast<XQItem*>(xqItem);
}
return XQItem::fallBackDummyItem();
}
XQItem& XQModel::xqFirstItem(int row) const
{
return *static_cast<XQItem*>( QStandardItemModel::item(row) );
}
QString XQModel::fetchNodeAttribute(int row, const QString& key )
{
/*
__fix
XQItem* item = fromRow(row);
if( item && item->hasNode() )
return item->contentNode()->attribute( key );
*/
return "";
}
/**
* @brief XQModel::fetchNodeTagName get the tag_name of the contentNode
* in a row
* @param row the row
* @return
*/
QString XQModel::fetchNodeTagName( int row )
{
// __fix
/*
XQItem* item = fromRow(row);
if( item && item->hasNode() )
return item->contentNode()->tag_name();
*/
return "";
}
void XQModel::onActionTriggered(QAction* action)
{
qDebug() << " --- onActionTriggered: count:" << XQNode::s_Count;
// all selected indices
QModelIndexList selectionList = treeView()->selectionModel()->selectedRows();
// extract command type
XQCommand::CmdType cmdType = action->data().value<XQCommand::CmdType>();
switch( cmdType )
{
// just handle undo ...
case XQCommand::cmdUndo :
return _undoStack->undo();
// ... or do/redo
case XQCommand::cmdRedo :
return _undoStack->redo();
// for copy & cut, we create a clone of the dataNodes in the clipboard
case XQCommand::cmdCopy :
case XQCommand::cmdCut :
// don't 'copy' empty selections
if( !selectionList.isEmpty() )
_clipBoard.saveNodes( selectionList );
// for copy, we are done, since copy cannot be undone
if( cmdType == XQCommand::cmdCopy )
return;
default:
break;
}
// we create a command
XQCommand* command = new XQCommand( cmdType, this );
// store the row positions of the selected indices
command->saveNodes( selectionList );
command->setOriginIndex( treeView()->currentIndex() );
// execute command
_undoStack->push( command );
}
/**
* @brief XQModel::onCommandRedo called to execute a command ('do').
* @param command the current command
*/
void XQModel::onCommandRedo( XQCommand& command )
{
try
{
switch (command.commandType())
{
case XQCommand::cmdToggleSection:
return cmdToggleSection( command.originIndex() );
case XQCommand::cmdCut:
return cmdCut( command );
case XQCommand::cmdPaste:
return cmdPaste( command );
case XQCommand::cmdNew:
return cmdNew( command );
case XQCommand::cmdDelete:
return cmdDelete( command );
case XQCommand::cmdMove:
break;
default:
qDebug() << " --- onCommandRedo: default: not handled: " << command.toString();
}
}
catch( XQException& exception )
{
qDebug() << exception.what();
QMessageBox::critical( nullptr, "Failure", QString("Failure: %1").arg(exception.what()) );
}
}
/**
* @brief XQModel::onCommandUndo: called to 'undo' a command.
* @param command the command to be undone.
*/
void XQModel::onCommandUndo( XQCommand& command )
{
qDebug() << " --- onCommandUndo: count: " << XQNode::s_Count;
try
{
switch (command.commandType())
{
case XQCommand::cmdToggleSection:
return cmdToggleSection( command.originIndex() );
break;
// undo Cut -> perform undoCut
case XQCommand::cmdCut:
return cmdCutUndo( command );
// undo Paste -> perform Cut
case XQCommand::cmdPaste:
return cmdPasteUndo( command );
// undo Move -> perform move back
case XQCommand::cmdMove:
// not yet implemented
break;
// undo New -> perform Delete
case XQCommand::cmdNew:
cmdNewUndo( command );
break;
// undo Delete -> perform New
case XQCommand::cmdDelete:
qDebug() << " --- onCommandUndo: delete: " << command.toString();
return cmdDeleteUndo( command );
default:
qDebug() << " --- onCommandUndo: default: not handled: " << command.toString();
}
}
catch( XQException& exception )
{
qDebug() << exception.what();
QMessageBox::critical( nullptr, "Failure", QString("Failure: %1").arg(exception.what()) );
}
}
// undo-/redo-able stuff
//! markierte knoten entfernen, 'command' enthält die liste
void XQModel::cmdCut( XQCommand& command )
{
// wir gehen rückwärts über alle gemerkten knoten ...
for (auto it = command.rbegin(); it != command.rend(); ++it)
{
// ... holen das erste item, das auch den content node enthält
//const XQNodeBackup& entry = *it;
//XQItem& firstItem = xqFirstItem( (*it).itemPos );
//qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row() << " id#" << entry.contentNode->_id;
// jetzt löschen, dabei wird die parent-verbindung entfernt
const XQNodeBackup& entry = *it;
entry.contentNode->unlink_self();
removeRow(entry.itemPos );
}
}
//! entfernte knoten wieder einfügen , 'command' enthält die liste
void XQModel::cmdCutUndo( XQCommand& command )
{
// die anfangsposition
int itmPos = command.first().itemPos;
// die 'zuständige' section rausfinden
const XQModelSection& section = _sections.sectionFromRow( itmPos );
// über alle einträge ...
for (auto& entry : command )
{
const XQNodePtr& savedNode = entry.contentNode;
// __fix! should not be _contentRoot!
savedNode->add_me_at( entry.nodePos, _contentRoot );
XQItemList list = _itemFactory.makeContentRow( savedNode, section.sheetRootNode );
XQItem& firstItem = *((XQItem*)list[0]);
qDebug() << " --- Cut Undo: " << firstItem.text() << " " << firstItem.row() << " id#" << entry.contentNode->_id << " count: " << entry.contentNode.use_count();
insertRow( entry.itemPos, list );
}
}
//! clipboard inhalte einfügen
void XQModel::cmdPaste( XQCommand& command )
{
// selection holen ...
QItemSelectionModel* selectionModel = treeView()->selectionModel();
// ... und löschen
selectionModel->clearSelection();
// aktuelles item finden
const XQItem& item = xqItemFromIndex( command.originIndex() );
// die neue item position ist unter dem akutellen item
int insRow = item.row()+1;
int nodePos = item.contentNode()->own_pos()+1;
// die zugehörige section finden
const XQModelSection& section = _sections.sectionFromRow( insRow-1 );
// wir pasten das clipboard
for (auto& entry : _clipBoard )
{
// noch ein clone vom clone erzeugen ...
XQNodePtr newNode = entry.contentNode->clone(section.contentRootNode );
newNode->clone(section.contentRootNode )->add_me_at( nodePos );
// ... und damit eine frische item-row erzeugen
XQItemList list = _itemFactory.makeContentRow( newNode, section.sheetRootNode );
insertRow( insRow, list );
// die neue item-row selektieren
const QModelIndex& selIdx = list[0]->index();
_treeView->selectionModel()->select(selIdx, QItemSelectionModel::Select | QItemSelectionModel::Rows);
// zur nächsten zeile
insRow++;
nodePos++;
}
// unsere änderungen merken fürs 'undo'
command.saveNodes( selectionModel->selectedRows() );
}
//! einfügen aus dem clipboard wieder rückgängig machen
void XQModel::cmdPasteUndo( XQCommand& command )
{
command.dumpList("Paste UNDO");
// wir gehen rückwärts über alle markieren knoten ...
for (auto it = command.rbegin(); it != command.rend(); ++it)
{
// ... holen das erste item, das auch den content node enthält
const XQNodeBackup& entry = *it;
XQItem& firstItem = xqFirstItem( (*it).itemPos );
qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row();
// jetzt löschen
entry.contentNode->unlink_self();
removeRow(entry.itemPos );
}
}
// don't clone into clipboard, remove items
void XQModel::cmdDelete( XQCommand& command )
{
// wir gehen rückwärts über alle markieren knoten ...
for (auto it = command.rbegin(); it != command.rend(); ++it)
{
// ... holen das erste item, das auch den content node enthält
const XQNodeBackup& entry = *it;
XQItem& firstItem = xqFirstItem( (*it).itemPos );
qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row();
// jetzt löschen
entry.contentNode->unlink_self();
removeRow(entry.itemPos );
}
}
void XQModel::cmdDeleteUndo( XQCommand& command )
{
}
/**
* @brief XQModel::cmdNewRow create one new item row
* @param command the command
*/
void XQModel::cmdNew( XQCommand& command )
{
// __fix
/*
const QModelIndex& origin = command.originIndex();
if( !origin.isValid() )
throw XQException("cmdNewRow failed: index not valid ");
XQItem* target = xqItemFromIndex( origin );
// current data node
XQNodePtr node = target->contentNode();
// we create a new data node
//XQNodePtr newNode = new XQNodePtr( node->tag_name(), node->parent() );
XQNodePtr newNode = XQNode::make_node( node->tag_name(), node->tag_value(), node->parent() );
// store node in node->parent()
//node->add_before_me( newNode );
// store node also in 'command' to enable undo
const XQModelSection& section = _sections.sectionxqItemFromIndex( origin );
// create new item row
XQItemList list = _itemFactory.createGenericRow( newNode, section.sheetRootNode );
// add it to the treeview ...
insertRow( origin.row(), list );
// ... and make it ...
treeView()->setCurrentIndex( list[0]->index() );
// ... editable
treeView()->edit( list[0]->index() );
*/
}
void XQModel::cmdNewUndo( XQCommand& command )
{
}
void XQModel::cmdToggleSection( const QModelIndex& index )
{
Q_ASSERT(index.isValid());
int fstRow = _sections.firstRow( index );
int lstRow = _sections.lastRow( index );
bool hidden =_treeView->isRowHidden( fstRow, _treeView->rootIndex() );
for (int row = fstRow; row < lstRow; ++row )
_treeView->setRowHidden( row, _treeView->rootIndex(), !hidden );
}
XQTreeView* XQModel::treeView()
{
return _treeView;
}
void XQModel::setTreeView(XQTreeView* mainView )
{
// store view for direct access: the maintree
_treeView = mainView;
// connect myself as model to the mainview
_treeView->setModel(this);
XQItemDelegate* delegate = new XQItemDelegate( *this );
_treeView->setItemDelegate( delegate );
_contextMenu = new XQContextMenu( mainView );
connect( _treeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onShowContextMenu(QPoint)));
//connect( _treeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex)) );
connect(_contextMenu, SIGNAL(triggered(QAction*)), this, SLOT(onActionTriggered(QAction*)));
// __fixme, die view soll über das modelsheet konfiguriert werden!
setupViewProperties();
}
/**
* @brief XQModel::setupViewProperties set the tree views' properties: context menu policy,
* edit triggers and so on.
*/
void XQModel::setupViewProperties()
{
_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
_treeView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
//_treeView->setSelectionMode(QAbstractItemView::ContiguousSelection);
_treeView->setSelectionModel( new XQSelectionModel(this) );
}
void XQModel::addSection( const XQItemList& list, const XQNodePtr& sheetNode )
{
appendRow(list);
_sections.addSectionEntry( list[0]->index(), sheetNode );
}
QUndoStack* XQModel::undoStack()
{
return _undoStack;
}
void XQModel::setUndoStack( QUndoStack* undoStack )
{
_undoStack = undoStack;
}
void XQModel::onShowContextMenu(const QPoint& point)
{
initContextMenu();
_contextMenu->popup(_treeView->mapToGlobal(point));
}
QHash<int, QByteArray> XQModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[XQItem::ContentRole] = "content";
roles[XQItem::ItemTypeRole] = "itemType";
roles[XQItem::RenderStyleRole] = "renderStyle";
roles[XQItem::EditorTypeRole] = "editorType";
roles[XQItem::UnitTypeRole] = "unitType";
roles[XQItem::FixedChoicesRole] = "fixedChoices";
roles[XQItem::ContentNodeRole] = "contentNode";
roles[XQItem::SheetNodeRole] = "sheetNode";
roles[XQItem::TypeKeyRole] = "typeKey";
return roles;
}

143
src/model/xqmodel.h Normal file
View File

@@ -0,0 +1,143 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMODEL_H
#define XQMODEL_H
#include <QUndoStack>
#include <QMenu>
#include <QStandardItemModel>
#include <xqsimpleclipboard.h>
#include <xqmodelsections.h>
#include <xqitemfactory.h>
#include <xqcontextmenu.h>
class XQTreeView;
class XQItem;
class XQCommand;
/**
* @brief Abstract baseclass of all modelviews: Extends QStandardItemModel with a treeview.
*/
// might be own implementation of QAbstractItemModel, not done yet.
//using QStandardItemModel = XQSimpleItemModel;
using QStandardItemModel = QStandardItemModel;
/**
* @brief The XQModel class: An extendend QStandardItem model
* containing its own view.
*/
class XQModel : public QStandardItemModel
{
Q_OBJECT
public:
XQModel(QObject* parent = nullptr);
virtual ~XQModel();
XQTreeView* treeView();
virtual void setTreeView( XQTreeView* mainView );
QUndoStack* undoStack();
void setUndoStack( QUndoStack* undoStack );
QHash<int, QByteArray> roleNames() const override;
//! create the own model structure
virtual void initModel( const QString& modelName) = 0;
//little helpers
const XQItem& xqRootItem();
XQItem& xqItemFromIndex(const QModelIndex& index) const;
XQItem& xqFirstItem(int row) const;
// was ist das?
QString fetchNodeAttribute(int row, const QString& key );
QString fetchNodeTagName(int row );
// undo-/redo-able stuff
virtual void cmdToggleSection( const QModelIndex& index );
virtual void cmdCut( XQCommand& command );
virtual void cmdCutUndo( XQCommand& command );
virtual void cmdPaste( XQCommand& command );
virtual void cmdPasteUndo( XQCommand& command );
virtual void cmdDelete( XQCommand& command );
virtual void cmdDeleteUndo( XQCommand& command );
virtual void cmdNew( XQCommand& command );
virtual void cmdNewUndo( XQCommand& command );
/*
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override
{
qDebug() << " --- setData: " << value.toString();
return QStandardItemModel::setData( index, value, role );
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
return QStandardItemModel::data( index, role );
}
*/
public slots:
virtual void onShowContextMenu(const QPoint& point);
virtual void onActionTriggered(QAction* action);
// handle XQCommands ( == UndoCommand )
virtual void onCommandRedo( XQCommand& command );
virtual void onCommandUndo( XQCommand& command );
signals:
void xqItemCreated( XQItem* newItem );
protected:
void addSection(const XQItemList& list, const XQNodePtr& sheetNode );
protected:
virtual void initContextMenu() = 0;
// __fixme: should be created from xml
virtual void setupViewProperties();
protected:
// das eine reference auf ein globales singleton
XQItemFactory& _itemFactory;
XQSimpleClipBoard _clipBoard;
XQModelSections _sections;
XQTreeView* _treeView{};
QUndoStack* _undoStack{};
XQContextMenu* _contextMenu{};
//! Die Modelbeschreibung
XQNodePtr _modelSheet{};
//! Der eigentliche Inhalt
XQNodePtr _contentRoot{};
};
#endif // XQMODEL_H

View File

@@ -0,0 +1,146 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqmodelsections.h>
#include <xqitem.h>
XQModelSection::XQModelSection(const QModelIndex& aModelIndex, XQNodePtr aSheetNode)
: modelIndex{ aModelIndex }, sheetRootNode{ aSheetNode }
{
}
bool XQModelSection::operator==(const XQModelSection& other) const
{
return modelIndex == other.modelIndex && sheetRootNode == other.sheetRootNode;
}
bool XQModelSection::isValid() const
{
return modelIndex.isValid() && sheetRootNode;
}
int XQModelSection::XQModelSection::row() const
{
return modelIndex.row();
}
XQItem& XQModelSection::XQModelSection::headerItem() const
{
return XQItem::xqItemFromIndex( modelIndex );
}
//
//
// -------------------------------------------------------------------------------------------------------------------------------------------------------
//
//
void XQModelSections::addSectionEntry(const QModelIndex& idx, XQNodePtr sheetNode)
{
XQModelSection section(idx, sheetNode);
addAtKey(sheetNode->tag_name(), section);
}
bool XQModelSections::hasValidSection(const QString& sectionKey) const
{
if (!contains(sectionKey) )
return false;
return at(sectionKey).isValid();
}
const XQModelSection& XQModelSections::sectionxqItemFromIndex( const QModelIndex& index ) const
{
return sectionFromRow( index.row() );
}
const XQModelSection& XQModelSections::sectionFromRow(int itemRow ) const
{
int i = size() - 1;
for (; i >= 0; --i)
{
if ( at(i).modelIndex.row() < itemRow )
return at(i);
}
static XQModelSection s_DummySection;
return s_DummySection;
}
int XQModelSections::firstRow(const QModelIndex& idx) const
{
return sectionFromRow(idx.row() ).row();
}
int XQModelSections::lastRow(const QModelIndex& idx) const
{
return lastRow(sectionFromRow(idx.row()));
}
int XQModelSections::lastRow(const XQModelSection& section ) const
{
//qDebug() << " -- last row in section: " << section.modelIndex.data().toString() << " --> " << section.modelIndex.row();
// row() der section unterhalb dieser
// __fix? index mit speichern?
int index = indexOf(section);
if (index > -1)
{
// last section? return last row of model
if (index == size() - 1)
return section.modelIndex.model()->rowCount();// - 1;
// return row above the row of the next section -> last row of given section
return at(index+1).row();
}
return -1;
}
void XQModelSections::dump() const
{
qDebug() << " --- sections dump(): " <<size() << " entries.";
for( int i = 0; i<size(); ++i )
{
QModelIndex idx = at(i).modelIndex;
qDebug() << " --- sections:" << i << "row: " << idx.row() << " keyOf(i): " << keyOf(i) << " indexData: "<< idx.data().toString() << " itemData: " << XQItem::xqItemFromIndex(idx).data(Qt::DisplayRole).toString();
}
}

View File

@@ -0,0 +1,71 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMODELSECTIONS_H
#define XQMODELSECTIONS_H
#include <QPersistentModelIndex>
#include <xqnode.h>
#include <xqmaptor.h>
class XQItem;
/**
* @brief Struct containing data for a header section
*/
struct XQModelSection
{
QPersistentModelIndex modelIndex;
XQNodePtr sheetRootNode{};
XQNodePtr contentRootNode{};
XQModelSection() = default;
XQModelSection(const QModelIndex& aModelIndex, XQNodePtr aSheetNode);
bool operator==(const XQModelSection& other) const;
bool isValid() const;
int row() const;
XQItem& headerItem() const;
};
/**
* @brief Maptor containing all header sections.
*/
class XQModelSections : public XQMaptor<XQModelSection>
{
public:
XQModelSections() = default;
virtual ~XQModelSections() = default;
void addSectionEntry(const QModelIndex& idx, XQNodePtr sheetNode );
bool hasValidSection(const QString& sectionKey) const;
const XQModelSection& sectionFromRow( int row ) const;
const XQModelSection& sectionxqItemFromIndex( const QModelIndex& index ) const;
int firstRow(const QModelIndex& idx) const;
int lastRow(const QModelIndex& idx) const;
int lastRow(const XQModelSection& section) const;
void dump()const override;
};
#endif // XQMODELSECTIONS_H

79
src/model/xqnode.cpp Normal file
View File

@@ -0,0 +1,79 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqnode.h>
#include <xqitem.h>
void inspect( const XQNodePtr& node, int indent )
{
qDebug() << std::string(indent * 2, ' ').c_str() << node.use_count() << ": " << node->to_string();
if (node->has_children())
{
for (const auto& child : node->children())
{
inspect( child, indent + 1 );
}
}
}
// Overload the operator<< for MyClass and std::ostream
std::ostream& operator<<(std::ostream& os, const QString& obj)
{
// Simply call the getter and insert the string into the stream
os << obj.toStdString();
return os; // Return the stream for chaining
}
template<>
bool znode::zpayload<QString>::xstr_split_by(const QString& entry, const QString& sep, QString& key, QString& value )
{
int index = entry.indexOf(sep);
if(index < 0)
return false;
key = entry.left(index);
value = entry.mid( index+sep.length() );
return true;
}
template<>
QString znode::zpayload<QString>::xstr_sub_str( const QString& entry, int pos ) const
{
return entry.mid(pos);
}
template<>
bool znode::zpayload<QString>::xstr_is_empty(const QString& entry ) const
{
return entry.isEmpty();
}
template<>
const QString znode::zpayload<QString>::cType = "Type";
template<>
const QString znode::zpayload<QString>::cName = "Name";
template<>
const QString znode::zpayload<QString>::cValue = "Value";
template<>
const QString znode::zpayload<QString>::cFriendlyName = "FriendlyName";

172
src/model/xqnode.h Normal file
View File

@@ -0,0 +1,172 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQNODE_H
#define XQNODE_H
#include <iostream>
#include <memory>
#include <stack>
#include <iterator>
#include <QDebug>
#include <QModelIndex>
#include <znode.h>
#include <xqappdata.h>
#include <znode_factory.h>
// Overload the operator<< for QString and std::ostream
std::ostream& operator<<(std::ostream& os, const QString& obj);
// raw node
using XQNode = znode::zbasic_node<QString>;
// default shared node
using XQNodePtr = std::shared_ptr<znode::zbasic_node<QString>>;
// const ref on XQNodePtr, ok but what its good for?
//using const XQNodePtr& = const XQNodePtr&;
/*
class XQBasicSheetNode : public znode::zbasic_node<QString>
{
public:
//...
};
using XQSheetNode = std::shared_ptr<const XQBasicSheetNode>;
using XQSheetNodeCRef = const XQSheetNode&;
*/
///
/// __fix
/// sollte man einen XQNodePtr einführen, um 'setAttr<XQNodePtr>()' von
/// setAttr<XQNodePtr>() unterscheiden zu können?
///
/// nice try. (siehe oben). Haut aber so nicht hin, node.children() gibt wieder
/// Pointer auf die Basisklasse zurück, ( vgl. XQItemList -> QList<QStandardItem*>.
/// Erst dass behandeln!
///
// weak pointer to znode, used for parent()
using XQWeakNode = std::weak_ptr<znode::zbasic_node<QString>>;
// the node factory
using XQNodeFactory = znode::znode_factory<QString>;
class XQNodeList : public znode::zbasic_node<QString>::znode_list
{
friend class XQSimpleClipBoard;
public:
XQNodeList() = default;
virtual ~XQNodeList() = default;
};
class XNodeIterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = XQNode;
using difference_type = std::ptrdiff_t;
using pointer = XQNode*;
using reference = XQNode&;
XNodeIterator() = default;
XNodeIterator(XQNodePtr root)
{
if (root) {
_stack.push(root);
}
}
reference operator*() const {
return *_stack.top();
}
pointer operator->() const {
return _stack.top().get();
}
XNodeIterator& operator++() {
auto node = _stack.top();
_stack.pop();
for (auto it = node->children().rbegin(); it != node->children().rend(); ++it)
{
_stack.push(*it);
}
return *this;
}
XNodeIterator operator++(int) {
XNodeIterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const XNodeIterator& other) const {
return _stack == other._stack;
}
bool operator!=(const XNodeIterator& other) const {
return !(*this == other);
}
private:
std::stack<XQNodePtr> _stack;
};
// Define the tree class with begin and end methods
class XTree
{
public:
XTree(XQNodePtr root)
: _root(root)
{}
XNodeIterator begin()
{
return XNodeIterator(_root);
}
XNodeIterator end()
{
return XNodeIterator();
}
private:
XQNodePtr _root;
};
//void inspect( XQNodePtr node, int offSet=0 );
void inspect( const XQNodePtr& node, int indent=0 );
Q_DECLARE_METATYPE(XQNodePtr);
#endif // XQNODE_H

View File

@@ -0,0 +1,59 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqnodewriter.h>
#include <QFile>
#include <QXmlStreamWriter>
#include <xqnode.h>
void XQNodeWriter::dumpTree( XQNodePtr rootNode, const QString& fileName ) const
{
QFile treeFile( fileName );
if (!treeFile.open(QIODevice::WriteOnly | QIODevice::Text))
throw XQException("can't open", fileName);
QXmlStreamWriter writer(&treeFile);
writer.setAutoFormatting(true); // Makes the output more readable
writer.writeStartDocument();
dumpNode( writer, rootNode );
writer.writeEndDocument();
treeFile.close();
}
void XQNodeWriter::dumpNode( QXmlStreamWriter& writer, XQNodePtr node ) const
{
//qDebug() << " --- dumpNode: id:" << node._id;
writer.writeStartElement(node->tag_name() );
if( !node->attributes().empty() )
{
for( const auto& attrEntry : node->attributes() )
writer.writeAttribute( attrEntry.first , attrEntry.second );
}
if( node->has_children() )
{
for (auto& child : node->children())
dumpNode( writer, child );
}
writer.writeEndElement();
}

42
src/model/xqnodewriter.h Normal file
View File

@@ -0,0 +1,42 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQNODEWRITER_H
#define XQNODEWRITER_H
#include <Qt>
#include "qxmlstream.h"
#include <xqnode.h>
class QString;
class XQNodeWriter
{
public:
XQNodeWriter() = default;
virtual ~XQNodeWriter() = default;
void dumpTree( XQNodePtr rootNode, const QString& fileName ) const;
protected:
void dumpNode( QXmlStreamWriter& writer, XQNodePtr node ) const;
};
#endif // XQNODEWRITER_H

View File

@@ -0,0 +1,65 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqselectionmodel.h>
#include <xqitem.h>
XQSelectionModel::XQSelectionModel(QAbstractItemModel* model)
: QItemSelectionModel(model)
{
}
XQSelectionModel::XQSelectionModel(QAbstractItemModel* model, QObject* parent)
: QItemSelectionModel(model, parent)
{
}
void XQSelectionModel::select(const QItemSelection& selection, QItemSelectionModel::SelectionFlags command)
{
// step #0: fetch selected indices.
QModelIndexList list = selection.indexes();
if (list.isEmpty() || selectedRows().isEmpty() )
return QItemSelectionModel::select(selection, command);
// fetch first index
QModelIndex firstValid = list.first();
if (hasSelection() )
firstValid = selectedRows().first();
//XQItem& firstItem = XQItem::xqItemFromIndex(firstValid);
//if( firstItem.isValid() )
{
XQNodePtr firstNode = XQItem::xqItemFromIndex(firstValid).contentNode();
QItemSelection newSelection;
// __fixme! das crasht!
for (const QModelIndex& idx : list)
{
XQNodePtr nextNode = XQItem::xqItemFromIndex(idx).contentNode();
if (!nextNode || idx.data().toString().isEmpty() || nextNode->tag_name() != firstNode->tag_name() )
break;
newSelection.select(idx, idx);
}
return QItemSelectionModel::select(newSelection, command);
}
QItemSelectionModel::select(selection, command);
}

View File

@@ -0,0 +1,41 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQSELECTIONMODEL_H
#define XQSELECTIONMODEL_H
#include <QItemSelectionModel>
#include <QObject>
/**
* @brief Extends QItemSelectionModel so that the selection is limited to a single section entry.
*/
class XQSelectionModel : public QItemSelectionModel
{
Q_OBJECT
public:
XQSelectionModel(QAbstractItemModel* model = nullptr);
XQSelectionModel(QAbstractItemModel* model, QObject* parent);
virtual ~XQSelectionModel() = default;
public slots:
void select(const QItemSelection& selection, QItemSelectionModel::SelectionFlags command) override;
};
#endif // XQSELECTIONMODEL_H

View File

@@ -0,0 +1,49 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqsimpleclipboard.h>
#include <xqmodel.h>
bool XQSimpleClipBoard::canPaste( const QModelIndex& curIdx ) const
{
bool pasteOk = false;
if( !isEmpty() )
{
XQItem& item = XQItem::xqItemFromIndex(curIdx);
// __fixme! header items haben keinen ZNode!
qDebug() << " --- can paste: " << item.contentNode()->tag_name() << " nodelist: " << front().contentNode->tag_name();
// paste is only allowed for the same component.type, which
// is coded in the tag_type
pasteOk = item.contentNode()->tag_name() == front().contentNode->tag_name();
}
else
{
qDebug() << " -- ClipBoard: nodelist empty!";
}
return pasteOk;
}
void XQSimpleClipBoard::saveNodes( const QModelIndexList& list )
{
clear();
for( auto entry : list )
{
XQNodePtr contentNode = XQItem::xqItemFromIndex( entry ).contentNode();
// im clipboard brauchen wir eine eltern-lose kopie des knotens
push_back( {entry.row(), contentNode->own_pos(), contentNode->clone() } );
}
}

View File

@@ -0,0 +1,36 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQSIMPLECLIPBOARD_H
#define XQSIMPLECLIPBOARD_H
#include <xqitem.h>
#include <xqcommand.h>
class XQSimpleClipBoard : public XQNodeStore
{
public:
XQSimpleClipBoard() = default;
virtual ~XQSimpleClipBoard() = default;
bool canPaste( const QModelIndex& curIdx ) const;
void saveNodes( const QModelIndexList& list ) override;
};
#endif // XQSIMPLECLIPBOARD_H

22
src/nodes/znode.cpp Normal file
View File

@@ -0,0 +1,22 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <znode.h>
namespace znode
{
int zid::s_Count{0};
} // namespace znode

413
src/nodes/znode.h Normal file
View File

@@ -0,0 +1,413 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef ZNODE_H
#define ZNODE_H
#include <vector>
#include <string>
#include <memory>
//#include <typeinfo>
#include <QString>
#include <QDebug>
#include <xqmaptor.h>
#include <xsingleton.h>
#include <znode_iterator.h>
//#include <znode_vector.h>
#include <znode_id.h>
#include <znode_payload.h>
namespace znode
{
/*
- die payload attributes wird 'drangeerbt'. sollte die nicht besser ge-templatet sein?
- die finder sollten besser ausgelagert sein
*/
// forward declaration of base class zbasic_node ...
//template<class str_t>
//class zbasic_node;
// ... used here for conveniance typedef
//template<class str_t>
//using zshared_node = std::shared_ptr<zbasic_node<str_t>>;
//! einfache tree-klasse, besonderheit: der nutzlast-string-type ist templated.
template<class str_t>
class zbasic_node : public zid, public zpayload<str_t>, public std::enable_shared_from_this<zbasic_node<str_t>>
{
public:
using str_cref = const str_t&;
using str_list = std::vector<str_t>;
//using zshared_node = std::shared_ptr<zbasic_node>;
//using znode_list = znode_vector<zbasic_node>;
//using znode_list = znode_vector<zshared_node<str_t>>;
//using znode_list = std::vector<zshared_node<str_t>>;
using zweak_node = std::weak_ptr<zbasic_node>;
using zshared_node = std::shared_ptr<zbasic_node>;
using znode_list = std::vector<zshared_node>;
using zshared_cref = const std::shared_ptr<zbasic_node>&;
using ziterator = znode_iterator<zbasic_node>;
protected:
zweak_node _parent;
znode_list _children;
struct match_node
{
match_node( zbasic_node* match )
: _match{match}
{}
bool operator()( const zshared_node& node )
{
return _match == node.get();
}
zbasic_node* _match{};
};
public:
//! shortcut auf std::make_shared...
static zshared_node make_node( str_cref arg1, str_cref arg2 = "" , zshared_cref parent = nullptr )
{
return std::make_shared<zbasic_node>( arg1, arg2, parent );
}
//! leerer konstruktor
zbasic_node() = default;
//! konstruktor mit tag_name und optionalem elternknoten
zbasic_node( str_cref tag_name, zshared_cref parent = nullptr )
: zpayload<str_t>{tag_name}, _parent{parent}
{
}
//! konstruktor mit tag_name, value und optionalem elternknoten
zbasic_node( str_cref tag_name, str_cref value, zshared_cref parent = nullptr )
: zpayload<str_t>{tag_name,value}, _parent{parent}
{
}
//! konstruktor mit tag_name, attributlist und optionalem elternknoten
zbasic_node( str_cref tag_name, const str_list& attributes, zshared_cref parent = nullptr )
: zbasic_node{tag_name, parent}
{
for( str_cref entry : attributes )
{
str_t key_value, data_value;
if( xstr_split_by( entry, "=", key_value, data_value) )
set_attribute( key_value, data_value );
}
}
//! default destruktor
virtual ~zbasic_node() = default;
//! kopieren ist hier nicht sinnvoll
zbasic_node(const zbasic_node&) = delete;
zbasic_node& operator=(const zbasic_node&) = delete;
// 'move' geht (shared_from_this bleibt gültig)
zbasic_node(zbasic_node&&) noexcept = default;
zbasic_node& operator=(zbasic_node&&) noexcept = default;
//! erzeugt eine deep-copy von 'mir' _mit_ allen kindknoten
virtual zshared_node clone(const zshared_node& parent = nullptr ) const
{
// copy als shared ptr erzeugen
zshared_node new_me = std::make_shared<zbasic_node>(this->_tag_name, this->_value );
// auch die attribute kopieren ...
new_me->_attributes = this->_attributes;
// ... und, so vorhanden, den parent-node.
new_me->_parent = parent;
// kinder kopieren
for (const zshared_node& child : _children)
new_me->_children.push_back( child->clone( new_me ) );
return new_me;
}
//! stl-ish traverse iterators
auto begin()
{
return ziterator(this);
}
//! stl-ish traverse iterators
auto end()
{
return ziterator(nullptr);
}
// ... set_int
// ... as_int
//! erzeugt einen shared_ptr
zshared_node parent() const
{
return _parent.lock();
}
zshared_node sibling()
{
if( !parent() )
//return zshared_node( make_node("WTF1") );
return zshared_node();
znode_list& childs = _parent->_children;
auto it = std::find( childs.begin(), childs.end(), this->shared_from_this() );
if( ++it != childs.end())
return *(it);
//return zshared_node( make_node("WTF?") );
return zshared_node();
}
inline const znode_list& children() const
{
return _children;
}
bool has_children() const
{
return !children().empty();
}
zshared_node first_child()
{
if(!children().empty())
return children().front();
return nullptr;
}
zshared_node last_child()
{
if(!children().empty())
return children().back();
return nullptr;
}
zshared_node child( int idx )
{
if(idx>-1 && idx<children().size())
return _children[idx];
return nullptr;
}
int add_child( const zshared_node& node )
{
_children.push_back( node );
node->_parent = this->shared_from_this();
return int(children().size() - 1);
}
//! fügt einen knoten in meine kinderliste ein und macht mich
//! zu dessen elternknoten.
int add_child_at( int idx, const zshared_node& node )
{
// _fixme! was ist, wenn da schon ein elternknoten ist?
_children.insert(children().begin() + idx, node );
node->_parent = this->shared_from_this();
return int(children().size() - 1);
}
//! fügt einen shard_ptr von 'mir' in die kinderliste meines elternknotens ein.
void add_me_at( int offset )
{
if( parent() )
parent()->add_child_at( offset, this->shared_from_this() );
else
throw std::runtime_error("add_me_at(offset): no parent node");
}
//! fügt einen shard_ptr von 'mir' in die kinderliste des übergebenen knotens ein
//! und macht diesen zu meinem elternknoten.
void add_me_at( int offset, const zshared_node& parent_node )
{
if( parent_node )
{
_parent = parent_node;
parent_node->add_child_at( offset, this->shared_from_this() );
}
else
{
throw std::runtime_error("add_me_at(offset,parent): no parent node");
}
}
int own_pos()
{
if( parent())
return parent()->child_pos( this->shared_from_this() );
return -1;
}
//int child_pos(zbasic_node* child)
int child_pos(const zshared_node& child)
{
//auto pos = std::find_if(children().begin(), children().end(), match_node(child) );
auto pos = std::find(children().begin(), children().end(), child );
if ( pos != children().end() )
return std::distance( children().begin(), pos );
return -1;
}
//zshared_node unlink_child( zbasic_node* node )
zshared_node unlink_child( const zshared_node& node )
{
auto it = std::find(_children.begin(), _children.end(), node);
if (it == _children.end())
return nullptr;
zshared_node removed = *it;
// Parent-Zeiger im Kind zurücksetzen
removed->_parent.reset();
_children.erase(it);
return removed;
}
//! entfernt 'mich' aus der kinderliste des elternknotens.
void unlink_self()
{
if(parent())
parent()->unlink_child( this->shared_from_this() );
else
throw std::runtime_error("unlink_self(): no parent node");
}
//! findet den ersten kind-knoten mit dem attribut 'attrkey' welches den
//! wert 'attrvalue' hat.
zshared_node find_child_by_attribute(str_cref attrkey, str_cref attrvalue )
{
for( auto child : _children )
{
qDebug() << " --#- " << child->name() << " : " << child->has_attribute( attrkey, attrvalue );
if( child->has_attribute( attrkey, attrvalue ))
return child;
}
return zshared_node();
}
//
zshared_node find_child_by_tag_name(str_cref tagname )
{
for( auto child : _children )
{
if( child->tag_name() == tagname )
return child;
}
return zshared_node();
}
zshared_node find_child_by_id( int id )
{
for (auto child : _children )
{
if (child->_id == id)
return child;
}
return zshared_node();
}
void dump(int indent = 0) const
{
// fix_me!
qDebug() << std::string(indent * 2, ' ').c_str() << this->to_string();
//qDebug() << to_string();
//qDebug() << '\n';// std::endl;
if (!children().empty())
{
for (auto child : _children)
{
//qDebug() << " --- type: " << typeid(child).name();
child.get()->dump( indent + 1 );
}
}
}
template<typename T>
bool for_each_x( T func, int depth = 0 )
//bool for_each( auto func ) const
{
if( !apply( func, depth ) )
return false;
if( !children().empty() )
{
for( auto child : _children )
{
if( !child->for_each( func, depth+1 ) )
return false;
}
}
return true;
}
// find ...
// depth first ...
// by attr
// by child
// by value
// by find( auto xxx )
// ...
};
class zbasic_node_walker
{
public:
virtual void begin()
{}
template<class str_t>
void for_each_node( zbasic_node<str_t>* node );
virtual void end()
{}
};
} //namespace znode
#endif // znode_H

123
src/nodes/znode_factory.h Normal file
View File

@@ -0,0 +1,123 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef ZNODE_FACTORY_H
#define ZNODE_FACTORY_H
#include <pugixml.hpp>
#include <znode.h>
class xtree;
/*
nodes: nackt also ungekaspselt?
-> children()
-> attributes()
attributes: ja was seid ihr denn? als string gelesen, als string geschrieben, aber dazwischen?
-> std::variant<bool...> mit enum type{t_bool = 0 ...};
-> conversion mit boost::lexical_cast<> bzw. std::to_string() und std::stoi(), std::stod()
-> beim parsen konvertieren? contains '.'? is_number?
-> betrifft tag_values und tag_attributes
-> vorher definieren? attribute 'voltage' == double, oder gar mehr: unit V, double, range
-> über xs:double?
model: muss ich wirklich jeden attibute node einzeln angeben?
*/
namespace znode
{
template<class str_t>
class znode_factory
{
public:
using str_cref = const str_t&;
using zshared_node = std::shared_ptr<zbasic_node<str_t>>;
znode_factory() = default;
virtual ~znode_factory() = default;
zshared_node load_tree( const std::string& filename )
{
pugi::xml_document tree_document;
// load document
pugi::xml_parse_result result = tree_document.load_file( filename.c_str() );
if( !result )
{
throw XQException( "znode_factory::load_tree: parse error: ", result.description() );
}
//MemberFunc func = &znode_factory::process_node;
//xtreewalker<znode_factory, MemberFunc> parser(this,func);
//_current_depth = -1;
//tree_document.traverse(parser);
//zbasic_node<str_t>* root_node = new zbasic_node<str_t>*("root!");
zshared_node root_node = zbasic_node<str_t>::make_node("root!");
//T root_node = T::make_node( "root!" );
// prepare root == model !?
pugi::xml_node tmp_node = tree_document.first_child();
while(tmp_node)
{
//qDebug() << " --- znode_factory building: " << tmp_node.name();
create_node( tmp_node, root_node );
tmp_node = tmp_node.next_sibling();
}
//znode::zmeas_para<QString>* xxx = new znode::zmeas_para<QString>("mookoo!");
//xxx->apply_command(42);
// _root_node->apply_command(42);
//size_t idx = _root_node->add_child( xxx);
//qDebug() << " jo: " << _root_node->child(idx)->tag_name();
return root_node;
}
protected:
void create_node(const pugi::xml_node& xml_node, zshared_node parent)
{
//T* new_node = new T( node.name(), node.child_value(), parent );
//parent->add_child( new_node );
//zbasic_node<str_t>* new_node = new zbasic_node<str_t>( node.name(), node.child_value(), parent );
zshared_node new_node = zbasic_node<str_t>::make_node( xml_node.name(), xml_node.child_value(), parent );
parent->add_child( new_node );
if( !xml_node.attributes().empty() )
{
for (pugi::xml_attribute attr: xml_node.attributes())
new_node->set_attribute( attr.name(), attr.value() );
}
if( !xml_node.children().empty() && std::string( xml_node.child_value() ).empty() )
{
for (pugi::xml_node child : xml_node.children())
create_node( child, new_node );
}
}
};
}
#endif // ZNODE_FACTORY_H

33
src/nodes/znode_id.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef ZNODE_ID_H
#define ZNODE_ID_H
namespace znode
{
struct zid
{
zid()
{
static int s_ID{0};
_id = s_ID++;
s_Count++;
//qDebug() << " --- con ID: " << _id << " count: " << s_Count;
}
virtual ~zid()
{
s_Count--;
//qDebug() << " --- del ID: " << _id << " count: " << s_Count;
}
static int s_Count;
int _id{0};
};
}
#endif // ZNODE_ID_H

124
src/nodes/znode_iterator.h Normal file
View File

@@ -0,0 +1,124 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef znode_iterator_H
#define znode_iterator_H
#include <iterator>
namespace znode
{
template<class T>
struct znode_iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*; // or also value_type*
using reference = T&; // or also value_type&
znode_iterator()
: _root{},_node{}
{}
znode_iterator(pointer node)
: _root{node},_node{node}
{}
void set_node(pointer node)
{
_root = node;
_node = node;
}
pointer get() // const
{
return _node;
}
int level() // const
{
return _level;
}
pointer operator->()
{
return _node;
}
reference operator*()
{
return *(operator->());
}
operator bool() const
{
return _node != nullptr;
}
// Prefix increment
znode_iterator& operator++()
{
if( !_node )
return *this;
// depth first: do we have children?
if( _node->has_children() )
{
_node = _node->first_child().get();
_level++;
return *this;
}
// no children, so we take siblings
pointer tmp = _node->sibling().get();
pointer nxt = _node->parent().get();
while( !tmp && nxt )
{
tmp = nxt->sibling().get();
if( nxt == _root )
{
//qDebug() << " ouch ";
_node = nullptr;
return *this;
}
_level--;
nxt = nxt->parent().get();
}
_node = tmp;
return *this;
}
// Postfix increment
znode_iterator operator++(int) { znode_iterator tmp = *this; ++(*this); return tmp; }
friend bool operator== (const znode_iterator& a, const znode_iterator& b) { return a._node == b._node; };
friend bool operator!= (const znode_iterator& a, const znode_iterator& b) { return a._node != b._node; };
protected:
pointer _root{};
pointer _node{};
int _level{0};
};
}
#endif // znode_iterator_H

264
src/nodes/znode_payload.h Normal file
View File

@@ -0,0 +1,264 @@
#ifndef ZNODE_PAYLOAD_H
#define ZNODE_PAYLOAD_H
#include <vector>
#include <map>
//#include <znode_stringmap.h>
//#include <znode_attributes.h>
namespace znode
{
template<class str_t>
class zstringmap : public std::map<str_t,str_t>
{
public:
using str_cref = const str_t&;
using str_ptr = str_t*;
using str_list = std::vector<str_t>;
using zvalue = str_t; // could be any or variant
// __fixme: should this be a vector? or a maptor?
//using zattributes = std::map<str_t, zvalue>;
using zattributes = zstringmap<str_t>;
str_cref key_of(str_cref value) const
{
for( const auto& pair : *this )
{
if (pair.second == value)
return pair.first;
}
static str_t s_dummy;
return s_dummy;
}
};
template<class str_t>
class zpayload
{
public:
using str_cref = const str_t&;
using str_ptr = str_t*;
using str_list = std::vector<str_t>;
using zvalue = str_t; // could be any or variant
// __fixme: should this be a vector? or a maptor?
//using zattributes = std::map<str_t, zvalue>;
using zattributes = zstringmap<str_t>;
static const str_t cType;// = "Type";
static const str_t cName;// = "Name";
static const str_t cValue;// = "Value";
static const str_t cFriendlyName;
protected:
str_t _tag_name;
str_t _value;
zattributes _attributes;
static zvalue s_dummy_value;
public:
zpayload( str_cref tag_name )
: _tag_name{tag_name}
{}
// ...
zpayload( str_cref tag_name, str_cref value )
: _tag_name{tag_name}, _value{value}
{}
zpayload( str_cref tag_name, const str_list& attriblist )
: zpayload(tag_name)
{
for( str_cref entry : attriblist )
{
str_t key_value, data_value;
if( xstr_split_by( entry, "=", key_value, data_value) )
set_attribute( key_value, data_value );
}
}
str_cref tag_name() const
{
return _tag_name;
}
str_ptr tag_name_ptr()
{
return &_tag_name;
}
void set_tag_name( str_cref tag_name )
{
_tag_name = tag_name;
}
str_cref tag_value() const
{
return _value;
}
str_ptr tag_value_ptr()
{
return &_value;
}
void set_tag_value( str_cref value )
{
_value = value;
}
str_cref friendly_name() const
{
return attribute(cFriendlyName);
}
str_ptr friendly_name_ptr()
{
return &attribute(cFriendlyName);
}
str_cref type() const
{
return attribute(cType);
}
str_ptr type_ptr() const
{
return &attribute(cType);
}
void set_type( str_cref type )
{
set_attribute( cType, type);
}
str_cref name() const
{
return attribute(cName);
}
str_ptr name_ptr()
{
return &attribute(cName);
}
void set_name( str_cref name )
{
set_attribute( cType, name);
}
const zattributes& attributes() const
{
return _attributes;
}
bool has_attribute(str_cref key ) const
{
if( xstr_is_empty( key ) )
return false;
typename zattributes::const_iterator pos = _attributes.find(key);
if( pos == _attributes.end() )
return false;
return true;
}
bool test_attribute(str_cref key, str_cref value ) const
{
if( xstr_is_empty( key ) )
return false;
typename zattributes::const_iterator pos = _attributes.find(key);
if( pos == _attributes.end() )
return false;
if( !xstr_is_empty( value ) )
return (*pos).second == value;
return true;
}
const zvalue& attribute(str_cref key ) const
{
if( !xstr_is_empty( key ) )
{
typename zattributes::const_iterator pos = _attributes.find(key);
if (pos != _attributes.end())
{
const zvalue& result = (*pos).second;
if (!xstr_is_empty(result) && result[0] == '@')
return attribute( xstr_sub_str(result, 1) );
return result;
}
}
return s_dummy_value;
}
const zvalue* attribute_ptr(str_cref key ) const
{
if( !xstr_is_empty( key ) )
{
typename zattributes::const_iterator pos = _attributes.find(key);
if (pos != _attributes.end())
{
const zvalue& result = (*pos).second;
if (!xstr_is_empty(result) && result[0] == '@')
return attribute_ptr( xstr_sub_str(result, 1) );
return &result;
}
}
return &s_dummy_value;
}
void set_attribute(str_cref key, zvalue value )
{
if( !xstr_is_empty( key ) )
_attributes[key] = value;
}
// dumper
str_t to_string() const
{
str_t result = '<' +_tag_name +'>';
if( !xstr_is_empty(_value) )
result += ": " + _value;
if( !_attributes.empty() )
{
result += " [";
bool first = true;
for (const auto& entry : _attributes)
{
if(first)
{
result += entry.first + "=" + entry.second;
first = false;
continue;
}
result += ", " + entry.first + "=" + entry.second;
}
result += "]";
}
return result;
}
// forward helpers
bool xstr_split_by(str_cref entry, str_cref sep, str_t& key, str_t& value );
str_t xstr_sub_str(str_cref entry, int pos) const;
bool xstr_is_empty(str_cref key ) const;
};
// init statics
template<class str_t>
str_t zpayload<str_t>::s_dummy_value;
}
#endif // ZNODE_PAYLOAD_H

50
src/nodes/znode_vector.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef ZNODE_VECTOR_H
#define ZNODE_VECTOR_H
#include <vector>
//using znode_vector = std::vector<zplain_node*>;
template<class T>
class znode_vector : public std::vector<T*>
{
public:
// FIX! implement this!
// why delete??
znode_vector() = default;
znode_vector(const znode_vector&) = delete;
znode_vector& operator=(const znode_vector&) = delete;
virtual ~znode_vector()
{
}
znode_vector* clone() const
{
znode_vector* new_list = new znode_vector;
clone_into( *new_list );
return new_list;
}
void clone_into( znode_vector& new_list ) const
{
new_list.clear();
for(const auto entry : *this )
new_list.push_back( entry->clone() );
}
void clone_from( const znode_vector& src_list )
{
this->clear();
for(const auto entry : src_list )
this->push_back( entry->clone() );
}
};
#endif // ZNODE_VECTOR_H

View File

@@ -0,0 +1,77 @@
/**
* pugixml parser - version 1.14
* --------------------------------------------------------
* Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
// #define PUGIXML_WCHAR_MODE
// Uncomment this to enable compact mode
// #define PUGIXML_COMPACT
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Tune this constant to adjust max nesting for XPath queries
// #define PUGIXML_XPATH_DEPTH_LIMIT 1024
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2023 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

13237
src/pugixml/pugixml.cpp Normal file

File diff suppressed because it is too large Load Diff

1516
src/pugixml/pugixml.hpp Normal file

File diff suppressed because it is too large Load Diff

20
src/util/xqexception.cpp Normal file
View File

@@ -0,0 +1,20 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqexception.h>
XQException::XQException(const QString& what, const QString& param )
: std::runtime_error( param.isEmpty() ? what.toStdString() : QString( "%1: %2" ).arg(what,param).toStdString( ) )
{}

34
src/util/xqexception.h Normal file
View File

@@ -0,0 +1,34 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQEXCEPTION_H
#define XQEXCEPTION_H
#include <QString>
#include <stdexcept>
/**
* @brief Simple exception class
*/
class XQException : public std::runtime_error
{
public:
XQException( const QString& what, const QString& param="" );
};
#endif // XQEXCEPTION_H

76
src/util/xqmapindex.h Normal file
View File

@@ -0,0 +1,76 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMAPINDEX_H
#define XQMAPINDEX_H
#include <QMap>
/**
* @brief Holds the string to index mapping for the QXMaptor classes
*/
class XQMapIndex : public QMap<QString, int>
{
public:
virtual ~XQMapIndex() = default;
void addKey(const QString& key, int index)
{
(*this)[key] = index;
}
int indexOf(const QString& key) const
{
if (contains(key))
return (*this)[key];
return -1;
}
void update(int index)
{
XQMapIndex newindex;
QMapIterator<QString, int> iter(*this);
while (iter.hasNext())
{
iter.next();
// item idx == kill-index: continue
// item idx < kill-index: store item
// item idx > kill-index: decrement & store item
int idx = iter.value();
if (idx == index)
continue;
if (idx > index)
idx--;
// schlüssel auch sichern
newindex[(iter.key())] = idx;
}
swap(newindex);
}
};
#endif // XQMAPINDEX_H

305
src/util/xqmaptor.h Normal file
View File

@@ -0,0 +1,305 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQMAPTOR_H
#define XQMAPTOR_H
#include <xqmapindex.h>
#include <xqexception.h>
/**
* @brief map + vector = XQMaptor, a template storage class whose data
* items can be accessed via string keys and int indices.
*/
template<class T>
class XQMaptor
{
public:
XQMaptor()
{
}
XQMaptor( int itemsize )
{
if( itemsize )
_data.resize( itemsize );
}
XQMaptor( const XQMaptor& src )
{
*this=src;
}
virtual ~XQMaptor()
{
}
XQMaptor& operator=( const XQMaptor& src )
{
if( this == &src )
return *this;
_data = src._data;
_index = src._index;
return *this;
}
// STL-like iterators
auto begin()
{
return _data.begin();
}
auto end()
{
return _data.end();
}
inline int size() const
{
return (int) _data.size();
}
inline bool isEmpty() const
{
return (_data.size()==0);
}
inline bool contains( int index ) const
{
return index < size() && index > -1;
}
inline bool contains( const QString& key ) const
{
return mapIndex().contains(key);
}
inline const XQMapIndex& mapIndex() const
{
return _index;
}
int indexOf( const QString& key ) const
{
return mapIndex().indexOf(key);
}
int indexOf(const T& entry) const
{
for (int i=0; i<_data.size(); ++i)
{
if (_data[i] == entry)
return i;
}
return -1;
}
virtual QString keyOf( int index ) const
{
return mapIndex().key( index );
}
T& operator[]( int index )
{
if( contains(index) )
return _data[index];
throw XQException("XQMaptor operator[ int index ]: out of range");
}
const T& operator[]( int index ) const
{
if ( contains(index) )
return _data[index];
throw XQException("XQMaptor const operator[ int index ]: out of range");
}
T& at( int index )
{
return (*this)[index];
}
const T& at( int index ) const
{
return (*this)[index];
}
T& operator[]( const QString& key )
{
if( key.isEmpty() || !contains(key) )
throw XQException("maprow operator[]: key empty || not found: " + key);
return _data[ _index[key] ];
}
const T& operator[]( const QString& key ) const
{
if (key.isEmpty() || !contains(key))
throw XQException("maprow operator[]: key empty || not found: " + key);
return _data[_index[key]];
}
T& at( const QString& key )
{
return (*this)[key];
}
const T& at( const QString& key ) const
{
return (*this)[key];
}
virtual int add( const T& item )
{
_data.push_back( item );
return _data.size()-1;
}
virtual void addAtIndex( int index, const T& item )
{
if(contains(index))
throw XQException( "QStringrow::add: index out of range!" );
_data[index] = item;
}
// convenience method to mimic QMap<T,QString>
virtual void insert( const T& item, const QString& key )
{
addAtKey(key, item );
}
virtual void addAtKey( const QString& key, const T& item )
{
XQMapIndex::iterator pos = _index.find( key );
if( pos == _index.end() )
{
_data.push_back( item );
_index[key] = _data.size()-1;
}
else
{
_data[pos.value()] = item;
}
}
bool addAlias( const QString& key, const QString& alias )
{
// look for 'original' key
int key_idx = indexOf(key);
// quit if not found
if( key_idx < 0 )
return false;
// look for alias
int alias_idx = indexOf(alias);
// quit if found: don't overwrite anything
if( alias_idx > -1 )
return false;
// store alias
_index[ alias ] = key_idx;
return true;
}
void addKey( const QString& key, int index )
{
_index.addKey( key, index );
}
virtual void clear()
{
_data.clear();
_index.clear();
}
virtual bool killEntry( const QString& key )
{
int idx = indexOf( key );
if( idx<0 )
return false;
return killEntry( (int) idx );
}
virtual bool killEntry( int index )
{
if( index >= this->_data.size() )
return false;
// eintrag lschen
this->_data.erase( this->_data.begin()+index );
// index updaten
_index.update( index );
return true;
}
virtual QString toString() const
{
return join( ";" );
}
virtual void dump() const
{
throw XQException("XQMaptor: dump not implemented!" );
}
virtual QString join( const QString& sep, int from=0, int to=-1) const
{
Q_UNUSED(sep)
Q_UNUSED(from)
Q_UNUSED(to)
throw XQException("XQMaptor: join not implemented!" );
return "--";
}
int replaceKey( const QString& oldkey, const QString& newkey )
{
int idx = indexOf( oldkey );
if( idx<0 || oldkey == newkey )
return idx;
_index.remove( oldkey );
_index[ newkey ] = idx;
return idx;
}
protected:
QVector<T> _data;
XQMapIndex _index;
};
#endif // XQMAPTOR_H

128
src/util/xqptrmaptor.h Normal file
View File

@@ -0,0 +1,128 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQPTRMAPTOR_H
#define XQPTRMAPTOR_H
#include <xqmaptor.h>
/**
* @brief A XQMaptor implementation for pointers to to data entries.
*/
template<class T>
class XQPtrMaptor : public XQMaptor<T*>
{
public:
virtual ~XQPtrMaptor()
{
for (int i = 0; i < this->_data.size(); ++i)
delete this->_data[i];
}
T& entry(const QString& key)
{
int i = this->indexOf(key);
if (i < 0)
throw XQException("XQPtrMaptor: entry: not found: " + key);
return *(this->_data[i]);
}
const T& entry(const QString& key) const
{
int i = this->indexOf(key);
if (i < 0)
throw XQException("XQPtrMaptor: entry: not found: " + key);
return *(this->_data[i]);
}
T& entry(int index)
{
if ((int)index < this->_data.size())
return *(this->_data[index]);
throw XQException("ddmapptr entry( int index ): out of range");
}
const T& entry(int index) const
{
if (index > 0 && index < this->_data.size())
return *(this->_data[index]);
throw XQException("ddmapptr const entry( int index ): out of range");
}
bool killEntry(const QString& key) override
{
int idx = this->indexOf(key);
// warum keine exception?
if (idx < 0)
return false;
return killEntry((int)idx);
}
bool killEntry(int index) override
{
T* item = this->_data[index];
if (XQMaptor<T*>::killEntry(index))
{
delete item;
return true;
}
return false;
}
bool removeEntry(const QString& key)
{
int idx = this->indexOf(key);
// warum keine exception?
if (idx < 0)
return false;
return removeEntry((int)idx);
}
bool removeEntry(int index)
{
if (index >= this->_data.size())
return false;
// eintrag löschen
this->_data.erase(this->_data.begin() + index);
// index updaten
this->_index.update(index);
return true;
}
void replace(int index, T* entry)
{
if ((int)index >= this->_data.size())
throw XQException("ddmapptr replace( int index ): out of range");
delete(this->_data[index]);
this->_data[index] = entry;
}
void replace(const QString& key, T* entry)
{
replace(this->indexOf(key), entry);
}
};
ö
#endif // XQPTRMAPTOR_H

48
src/util/xsingleton.h Normal file
View File

@@ -0,0 +1,48 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XSINGLETON_H
#define XSINGLETON_H
/**
* @brief The classic singleton template interface.
*/
template <typename T>
class xsingleton
{
public:
//! Get the singleton instance.
static T& instance()
{
static T Instance;
return Instance;
}
protected:
xsingleton() {}
virtual ~xsingleton() {}
private:
xsingleton(xsingleton const &) = delete;
xsingleton& operator=(xsingleton const &) = delete;
};
#endif // XSINGLETON_H

47
src/util/xtreewalker.h Normal file
View File

@@ -0,0 +1,47 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XTREEWALKER_H
#define XTREEWALKER_H
#include <pugixml.hpp>
#include <functional>
/**
* @brief A implentation of the pugi::xml treewalker class.
*/
template <typename O,typename M>
class xtreewalker : public pugi::xml_tree_walker
{
public:
xtreewalker( O* object, M member )
{
_call = std::bind(member, object, std::placeholders::_1, std::placeholders::_2);
}
virtual bool for_each(pugi::xml_node& node) override
{
return _call(node,depth());
}
protected:
std::function<bool(pugi::xml_node& node,int)> _call;
};
#endif // XTREEWALKER_H

View File

@@ -0,0 +1,68 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqcontextmenu.h>
XQContextMenu::XQContextMenu(const QString& title, QWidget* parent )
: QMenu( title, parent )
{
/*
QAction* titleDummy = new QAction(title,this);
QWidget::addAction(titleDummy);
addSeparator();
titleDummy->setEnabled(false);
*/
}
XQContextMenu::XQContextMenu(QWidget* parent)
: QMenu( parent )
{
}
void XQContextMenu::addAction(const QString& text, XQCommand::CmdType commandType, bool enabled)
{
QAction* newAction = new QAction(text, this);
newAction->setData(commandType);
_actionMap[commandType] = newAction;
QWidget::addAction(newAction);
setActionEnabled( commandType, enabled );
}
void XQContextMenu::addAction(const QString& iconKey, const QString& name, XQCommand::CmdType commandType, bool enabled)
{
addAction(XQAppData::typeIcon( iconKey), name, commandType, enabled );
}
void XQContextMenu::addAction(const QIcon& icon, const QString& text, XQCommand::CmdType commandType, bool enabled)
{
QAction* newAction = new QAction(icon, text, this);
newAction->setData(commandType);
_actionMap[commandType] = newAction;
QWidget::addAction(newAction);
setActionEnabled( commandType, enabled );
}
void XQContextMenu::setActionEnabled(XQCommand::CmdType commandType, bool enabled)
{
if( _actionMap.contains(commandType) )
_actionMap[commandType]->setEnabled( enabled );
}

View File

@@ -0,0 +1,51 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQCONTEXTMENU_H
#define XQCONTEXTMENU_H
#include <QMenu>
#include <QMap>
#include <xqcommand.h>
/**
* @brief A specialized QMenu for @see XQCommands
*/
class XQContextMenu : public QMenu
{
Q_OBJECT
public:
XQContextMenu(const QString& title, QWidget* parent = nullptr );
XQContextMenu(QWidget* parent = nullptr );
virtual ~XQContextMenu() = default;
void addAction(const QString& name, XQCommand::CmdType commandType, bool enabled );
void addAction(const QString& iconKey, const QString& name, XQCommand::CmdType commandType, bool enabled );
void addAction(const QIcon& icon, const QString& name, XQCommand::CmdType commandType, bool enabled );
void setActionEnabled(XQCommand::CmdType commandType, bool enabled);
protected:
QMap<int, QAction*> _actionMap;
};
#endif //; XQContextMenu_H

196
src/widgets/xqtreeview.cpp Normal file
View File

@@ -0,0 +1,196 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QPainter>
#include <QTime>
#include "qheaderview.h"
#include <xqtreeview.h>
#include <xqmodel.h>
#define DB_TIMESTAMP QTime::currentTime().toString(" -- HH:mm:ss.zzz")
XQTreeView::XQTreeView(QWidget* parent)
: QTreeView(parent)
{
/*
setDragEnabled(true);
setAcceptDrops(true);
setDragDropOverwriteMode(true);
setDropIndicatorShown(false);
*/
setHeaderHidden(true);
setMouseTracking(true);
}
XQTreeView::~XQTreeView()
{
}
XQModel* XQTreeView::modelView()
{
return static_cast<XQModel*>(model());
}
// __fixme: necessary?
XQItem& XQTreeView::xqItemFromIndex(const QModelIndex& index )
{
return modelView()->xqItemFromIndex( index );
}
void XQTreeView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
{
QTreeView::currentChanged(current, previous);
// edit comboboxes directly
//XQItem& currentItem = xqItemFromIndex(current);
// xx_fix!
/*
if (curreXQItem && curreXQItem->renderStyle() == XQItem::ComboBoxStyle )
{
QTreeView::edit(current);
}
*/
}
void XQTreeView::mouseResizeHeaderEntry(int xpos)
{
// resize colummn: minimal vertical margin in pixels
static const int s_MinimalMargin = 50;
QRect itemRect = visualRect(_indexToResize);
// new position different to item rect outer right position
int newPos = xpos - itemRect.right();
// current section size from header
int column = _indexToResize.column();
int sectionSize = columnWidth( column );
int maxPos = s_MinimalMargin + xpos;
// resize header and update view
if (maxPos <= width())
{
header()->resizeSection(column, sectionSize + newPos);
}
}
void XQTreeView::mouseMoveEvent(QMouseEvent* event)
{
// pixel Grenzwert zur Anzeige des Splitcursors
static const int s_CatchOffset = 5;
bool leftBtn = (event->buttons() & Qt::LeftButton);
QPoint eventPos = event->pos();
// splitcursor ist active
bool splitCursor = (cursor().shape() == Qt::SplitHCursor);
// sind wir schon am 'draggen'?
if (_indexToResize.isValid() && splitCursor && leftBtn)
{
return mouseResizeHeaderEntry(eventPos.x());
}
// nein, nocht nicht
QModelIndex idxFromPos = indexAt(eventPos);
// mousepointer is inside a header section
if ( xqItemFromIndex(idxFromPos).isHeaderStyle() )
{
QRect itemRect = visualRect(idxFromPos);
int crX = itemRect.topRight().x() - s_CatchOffset;
QRect catchRect = QRect(crX, itemRect.y(), s_CatchOffset, itemRect.height());
if (catchRect.contains(eventPos))
{
return setCursor(QCursor(Qt::SplitHCursor));
}
}
setCursor(QCursor(Qt::ArrowCursor));
QTreeView::mouseMoveEvent(event);
}
void XQTreeView::mouseDoubleClickEvent(QMouseEvent* event)
{
/*
QModelIndex idxFromPos = indexAt(event->pos());
if (idxFromPos.isValid())
{
if ( NTItem::isHeaderType(idxFromPos) && cursor().shape() == Qt::SplitHCursor)
{
return resizeColumnToContents(idxFromPos.column());
}
}
*/
QTreeView::mouseDoubleClickEvent(event);
}
void XQTreeView::mousePressEvent(QMouseEvent* event)
{
// case #1:
// Handle header resiszing
QPoint pos = event->pos();
QModelIndex index = indexAt(pos);
// set index for resize column if cursor for split is active
if (cursor().shape() == Qt::SplitHCursor)
{
_indexToResize = index;
}
QTreeView::mousePressEvent(event);
}
void XQTreeView::mouseReleaseEvent(QMouseEvent* event)
{
// reset index for resize column
_indexToResize = QModelIndex();
setCursor(QCursor(Qt::ArrowCursor));
QTreeView::mouseReleaseEvent(event);
}
void XQTreeView::wheelEvent(QWheelEvent* event)
{
// Ctrl-key down?
if (!event->modifiers().testFlag(Qt::ControlModifier))
// default processing
return QTreeView::wheelEvent(event);
// current size
int curSize = fontInfo().pointSize();
// increase?
if (event->angleDelta().y() > 0)
{
// "zoom in"
curSize += (curSize < 40) ? (1) : (0);
}
else
{
// "zoom out"
curSize -= (curSize > 8) ? (1) : (0);
}
// adjust size via stylesheet
setStyleSheet(QString("font: %1pt;").arg(curSize));
}

62
src/widgets/xqtreeview.h Normal file
View File

@@ -0,0 +1,62 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#ifndef XQTREEVIEW_H
#define XQTREEVIEW_H
#include <QTreeView>
#include <QMouseEvent>
#include <QDebug>
class XQItem;
class XQModel;
/**
* @brief A specialized QTreeView that will handle the drawing of
* empty or non-existing items in the future.
*/
class XQTreeView : public QTreeView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QTreeView)
public:
XQTreeView(QWidget* parent = nullptr );
virtual ~XQTreeView();
XQModel* modelView();
XQItem& xqItemFromIndex(const QModelIndex& index );
protected:
void currentChanged(const QModelIndex& current, const QModelIndex& previous) override;
void mouseMoveEvent(QMouseEvent* event) override;
//! Mouse press event.used to emulate the behavior of a QHeaderView in the first line of the view
void mouseDoubleClickEvent(QMouseEvent* event) override;
//! Mouse release event.used to emulate the behavior of a QHeaderView in the first line of the view
void mouseReleaseEvent(QMouseEvent* event) override;
//! Mouse press event.used to emulate the behavior of a QHeaderView in the first line of the view
void mousePressEvent(QMouseEvent* event) override;
void mouseResizeHeaderEntry(int xpos);
//! Mouse wheel event.
void wheelEvent(QWheelEvent* event) override;
//! used by the mouse events
QModelIndex _indexToResize;
};
#endif // XQTREEVIEW_H

95
src/xtree.pro Normal file
View File

@@ -0,0 +1,95 @@
QT += core gui widgets
# widgets-private
CONFIG += c++20
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
INCLUDEPATH += pugixml widgets nodes model application datatypes util items
HEADERS += \
application/xqchildmodel.h \
application/xqdocumentstore.h \
application/xqmainmodel.h \
application/xqmainwindow.h \
application/xqappdata.h \
items/xqgenericitem.h \
items/xqitem.h \
items/xqitemfactory.h \
items/xqitemtype.h \
items/xqitemdelegate.h \
model/xqcommand.h \
model/xqmodel.h \
model/xqmodelsections.h \
model/xqnode.h \
model/xqnodewriter.h \
model/xqselectionmodel.h \
model/xqsimpleclipboard.h \
nodes/znode.h \
nodes/znode_factory.h \
nodes/znode_id.h \
nodes/znode_iterator.h \
nodes/znode_payload.h \
#nodes/znode_stringmap.h \
#nodes/znode_attributes.h \
nodes/znode_vector.h \
pugixml/pugiconfig.hpp \
pugixml/pugixml.hpp \
util/xqexception.h \
util/xqmapindex.h \
util/xqmaptor.h \
util/xqptrmaptor.h \
util/xsingleton.h \
util/xtreewalker.h \
widgets/xqcontextmenu.h \
widgets/xqtreeview.h
SOURCES += \
application/xqchildmodel.cpp \
application/xqdocumentstore.cpp \
application/xqmainmodel.cpp \
application/xqmainwindow.cpp \
application/xqappdata.cpp \
items/xqgenericitem.cpp \
items/xqitem.cpp \
items/xqitemfactory.cpp \
items/xqitemtype.cpp \
items/xqitemdelegate.cpp \
main.cpp \
model/xqcommand.cpp \
model/xqmodel.cpp \
model/xqmodelsections.cpp \
model/xqnode.cpp \
model/xqnodewriter.cpp \
model/xqselectionmodel.cpp \
model/xqsimpleclipboard.cpp \
nodes/znode.cpp \
pugixml/pugixml.cpp \
util/xqexception.cpp \
widgets/xqcontextmenu.cpp \
widgets/xqtreeview.cpp
FORMS += \
application/xqmainwindow.ui
RESOURCES = xtree.qrc
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
README.md \
xml/modelsheets.xml \
xml/modeldata1.xtr \
xml/modeldata2.xtr \
xml/modeldata3.xtr

8
src/xtree.qrc Normal file
View File

@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>../xml/modeldata1.xtr</file>
<file>../xml/modeldata2.xtr</file>
<file>../xml/modeldata3.xtr</file>
<file>../xml/modelsheets.xml</file>
</qresource>
</RCC>