first reCommit

This commit is contained in:
2025-08-13 18:30:47 +02:00
commit a946c7dd58
73 changed files with 23368 additions and 0 deletions

134
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 XQSavedNodesList::dumpSavedNodesList( const QString& title )
{
if( !title.isEmpty() )
qDebug() << " --- " << title;
for( const auto& entry : *this )
qDebug() << " -- dumpSavedNodesList: 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 }, _modelView(modelView)
{
}
XQCommand::~XQCommand()
{
qDebug() << " --- command destructor: " << toString() << " : " << _posList.size();
clearSavedNodes();
}
XQCommand::CmdType XQCommand::commandType() const
{
return _cmdType;
}
void XQCommand::setCommandType( XQCommand::CmdType cmdType )
{
_cmdType = cmdType;
}
void XQCommand::redo()
{
_modelView->onCommandRedo( *this );
}
void XQCommand::undo()
{
_modelView->onCommandUndo( *this );
}
XQSavedNodesList& XQCommand::savedNodesList()
{
return _posList;
}
void XQCommand::saveNodes( const QModelIndexList& list )
{
_posList.clear();
for( auto entry : list )
{
XQNodePtr contentNode = XQItem::xqItemFromIndex( entry ).contentNode();
_posList.push_back( {entry.row(), contentNode->own_pos(), contentNode } );
}
}
void XQCommand::clearSavedNodes()
{
_posList.clear();
}
const QModelIndex& XQCommand::originIndex() const
{
return _originIndex;
}
void XQCommand::setOriginIndex( const QModelIndex& origin )
{
QString cmdText("%1: %2 (%3)");
QString name = origin.data().toString();
QString items("%1 item%2");
int size = _posList.size();
items = items.arg(size).arg(size > 1 ? "s" : "");
cmdText = cmdText.arg( toString(), name, items );
_originIndex = origin;
setText(cmdText);
}
/*
XQNodeList& XQCommand::nodeList()
{
return _nodeList;
}
*/
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];
}

121
model/xqcommand.h Normal file
View File

@@ -0,0 +1,121 @@
/***************************************************************************
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>
#include <xqsimpleclipboard.h>
class XQModel;
struct XQSavedNode
{
int itemPos{-1};
int nodePos{-1};
XQNodePtr contentNode;
};
class XQSavedNodesList : public QVector<XQSavedNode>
{
public:
void dumpSavedNodesList( const QString& title="" );
};
/**
* @brief Holds command type marker and involed data items.
*/
class XQCommand : public QUndoCommand
{
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 );
/*
const QModelIndexList& indexList() const;
void setIndexList( const QModelIndexList& list );
XQNodeList& nodeList();
*/
XQSavedNodesList& savedNodesList();
void saveNodes( const QModelIndexList& list );
void clearSavedNodes();
void redo() override;
void undo() override;
QString toString();
protected:
CmdType _cmdType{cmdInvalid};
XQModel* _modelView{}; // needed for redo() / undo()
QModelIndex _originIndex;
XQSavedNodesList _posList;
/*
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

549
model/xqmodel.cpp Normal file
View File

@@ -0,0 +1,549 @@
/***************************************************************************
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)
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();
}
const XQItem& XQModel::xqConstItemFromIndex(const QModelIndex& index) const
{
QStandardItem* xqItem = QStandardItemModel::itemFromIndex(index);
if( xqItem )
return *static_cast<XQItem*>(xqItem);
return XQItem::fallBackDummyItem();
}
XQItem& XQModel::xqItemFromRow(int row) const
{
return *static_cast<XQItem*>( QStandardItemModel::item(row) );
}
const XQItem& XQModel::xqConstItemFromRow(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 "";
}
/**
* @brief XQModel::onActionTriggered: called, when a context menu action is triggered.
* @param action
*/
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.createContentNodeList( 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 )
{
qDebug() << " --- onCommandRedo: count:" << XQNode::s_Count;
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();
}
}
/**
* @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;
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();
}
}
// undo-/redo-able stuff
/**
* @brief XQModel::cmdCutRows Cut the rows whose positions have been store in the command.
* @param command
*/
void XQModel::cmdCut( XQCommand& command )
{
command.savedNodesList().dumpSavedNodesList(" command CUT");
for (auto it = command.savedNodesList().rbegin(); it != command.savedNodesList().rend(); ++it)
{
const XQSavedNode& entry = *it;
XQItem& myItem = xqItemFromRow( entry.itemPos );
qDebug() << " --- AJAJA: " << myItem.text() << myItem.row();
}
XQItemList list;
for (auto it = command.savedNodesList().rbegin(); it != command.savedNodesList().rend(); ++it)
{
const XQSavedNode& entry = *it;
list = takeRow(entry.itemPos );
}
// here, we have a cloned selection in the clipboard. The
// clipboard does not store item position by nature, so we
// cannot use it for 'undo'.
// To preverse Positions, we take the nodes in reverse order.
// wir ändern hier den inhalt von command:
//
// vorher: command entält die arbeits liste mit positionen, die ausgeschitten werden sollen
// nachher: ...
//
/*
for (auto it = command.savedNodesList().rbegin(); it != command.savedNodesList().rend(); ++it)
{
XQSavedNode& entry = *it;
XQItem* myItem = fromRow( entry.itemPos );
XQNodePtr contentNode = myItem->contentNode();
qDebug() << " -- cmd cut row: " << entry.itemPos << " : " << contentNode->friendly_name() << " : " << contentNode->_id << " parent: " << contentNode->parent()->_id;
// FIX: could we put this into on call?
// delete visual item
removeRow( entry.itemPos );
// delete data node
//contentNode->delete_self();
contentNode->unlink_self();
}
*/
}
// clone to clipboard, remove items
void XQModel::cmdCutUndo( XQCommand& command )
{
XQSavedNodesList& posList = command.savedNodesList();
//dumpSavedNodesList( posList );
// we need the concerned section to get the sheet node
const XQModelSection& section = _sections.sectionFromRow( posList.first().itemPos );
for (auto& entry : posList )
{
// we create another clone here since our command will be deleted.
XQNodePtr newNode = entry.contentNode;//->clone();
qDebug() << " -- cmd undo cut row: " << entry.itemPos << " : " << newNode->friendly_name() << " : " << newNode->_id << " parent: " << newNode->parent()->_id;
// parent of newNode does not change here
XQItemList list = _itemFactory.createDataRow( newNode, section.sheetRootNode );
// insert clonde node copy
newNode->add_me_at_pos( entry.nodePos );
insertRow( entry.itemPos, list );
}
command.savedNodesList().dumpSavedNodesList();
}
/**
* @brief XQModel::cmdPasteRows Create new XQItemLists by cloning the dataNodes from the clipboard.
* @param origin The start index
*/
void XQModel::cmdPaste( XQCommand& command )
{
command.clearSavedNodes();
QModelIndex origin = command.originIndex();
// fetch the first item target position
int itemPos = origin.row();
Q_ASSERT( itemPos != -1 );
XQSavedNodesList& posList = command.savedNodesList();
// we also need the id of the preceding nodes id as insert position
XQNodePtr predecessor = xqItemFromIndex(origin).contentNode();
int nodePos = predecessor->own_pos();
// we need the concerned section to get the sheet node
const XQModelSection& section = _sections.sectionFromRow( itemPos );
qDebug() << " --- paste nodes at node ID: " << predecessor->_id << " node pos: " << nodePos;
for( auto contentNode : _clipBoard.dataNodeList() )
{
XQNodePtr newNode = contentNode;//->clone();
// parent of newNode does not change here
XQItemList list = _itemFactory.createDataRow( newNode, section.sheetRootNode );
// we have to store the current data for undo, so we need
// another clone, since the node will be deleted when the command is destroyed.
//posList.push_back( {itemPos, nodePos, contentNode->clone() } );
posList.push_back( {itemPos, nodePos, contentNode } );
qDebug() << " -- cmd paste clipboard row: " << newNode->friendly_name() << " : " << newNode->_id << " parent: " << newNode->parent()->_id;
newNode->add_me_at_pos( nodePos++ );
insertRow( itemPos++, list );
}
command.savedNodesList().dumpSavedNodesList();
}
void XQModel::cmdPasteUndo( XQCommand& command )
{
}
// don't clone into clipboard, remove items
void XQModel::cmdDelete( XQCommand& command )
{
cmdCutUndo( command );
}
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;
}

145
model/xqmodel.h Normal file
View File

@@ -0,0 +1,145 @@
/***************************************************************************
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;
const XQItem& xqConstItemFromIndex(const QModelIndex& index) const;
XQItem& xqItemFromRow(int row) const;
const XQItem& xqConstItemFromRow(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 );
// wtf!?
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

146
model/xqmodelsections.cpp Normal file
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 arow) const
{
static XQModelSection s_DummySection;
int i = size() - 1;
for (; i >= 0; --i)
{
if ( at(i).modelIndex.row() <= arow )
return at(i);
}
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 << " keyOf(i): " << keyOf(i) << " indexData: "<< idx.data().toString() << " itemData: " << XQItem::xqItemFromIndex(idx).data(Qt::DisplayRole).toString();
}
}

72
model/xqmodelsections.h Normal file
View File

@@ -0,0 +1,72 @@
/***************************************************************************
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;
// müsse die hier sein?
XQNodePtr sheetRootNode{};
XQNodePtr dataRootNode{};
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
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
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

59
model/xqnodewriter.cpp Normal file
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
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);
}

41
model/xqselectionmodel.h Normal file
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

107
model/xqsimpleclipboard.cpp 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.
***************************************************************************/
#include <xqsimpleclipboard.h>
#include <xqmodel.h>
/**
* @brief XQSimpleClipBoard::~XQSimpleClipBoard Destroys this object and deletes
* the XQDataNodes stored here.
*/
XQSimpleClipBoard::~XQSimpleClipBoard()
{
// we have clones here, so deletion is needed.
_nodeList.clear();
}
/**
* @brief XQSimpleClipBoard::isEmpty
* @return True for an empty node list.
*/
bool XQSimpleClipBoard::isEmpty() const
{
return _nodeList.empty();
}
/**
* @brief XQSimpleClipBoard::canPaste Test if the clipboard contents can be pasted in the
* given index position.
* @param curIdx The index position
* @return true if pasting is possible
*/
bool XQSimpleClipBoard::canPaste( const QModelIndex& curIdx ) const
{
bool pasteOk = false;
if( !isEmpty() )
{
XQItem& item = XQItem::xqItemFromIndex(curIdx);
// __fixme! header items haben keinen ZNode!
// paste is only allowed for the same component.type, which
// is coded in the tag_type
pasteOk = item.contentNode()->tag_name() == _nodeList.front()->tag_name();
}
return pasteOk;
}
/**
* @brief XQSimpleClipBoard::createContentNodeList Fill the internal list of
* XQDataNodes by cloning the nodes in the given index list. (@see dataNodeList() )
*
* @param indexList The list with QModelIndices.
*
*/
void XQSimpleClipBoard::createContentNodeList( QModelIndexList& indexList )
{
//_nodeList.createContentNodeList( indexList);
}
/*
void XQNodeList::createContentNodeList(QModelIndexList& indexList )
{
Q_ASSERT( !indexList.isEmpty() );
// clean up first
clear();
for( auto& idx : indexList )
{
XQNodePtr node = XQItem::xqItemFromIndex(idx).contentNode();
qDebug() << " -- nodelist add node: "<< node->friendly_name() << " id: " << node->_id << " use count: " << node.use_count();
push_back( node );
}
}*/
/**
* @brief XQSimpleClipBoard::dataNodeList
* @return The internal list XQDataNodes
*/
const XQNodeList& XQSimpleClipBoard::dataNodeList()
{
return _nodeList;
}

42
model/xqsimpleclipboard.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 XQSIMPLECLIPBOARD_H
#define XQSIMPLECLIPBOARD_H
#include <xqitem.h>
using XQItemRowList = QVector<XQItemList>;
class XQSimpleClipBoard
{
public:
XQSimpleClipBoard() = default;
virtual ~XQSimpleClipBoard();
bool isEmpty() const;
bool canPaste( const QModelIndex& curIdx ) const;
void createContentNodeList( QModelIndexList& indexList );
const XQNodeList& dataNodeList();
protected:
XQNodeList _nodeList;
};
#endif // XQSIMPLECLIPBOARD_H