Files
xtree/src/items/xqitemfactory.cpp

239 lines
6.9 KiB
C++
Raw Normal View History

2025-08-22 22:57:06 +02:00
/***************************************************************************
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 <xqviewmodel.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() )
{
//qDebug() << " --- conf item Type: " << key << " : " << value;
2025-09-10 23:32:00 +02:00
setItemTypeDataFromString( *itemType, key, value );
2025-08-22 22:57:06 +02:00
}
};
// 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 ...
2025-09-27 17:21:36 +02:00
_typesSheet = _modelSheet->child( "ItemTypes" );
2025-08-22 22:57:06 +02:00
// ... und testen
if( !_typesSheet )
throw XQException( "initItemFactory <ItemTypes> is null" );
// alle itemtype vorlagen erzeugen
for( const XQNodePtr& typeSheetNode : _typesSheet->children())
{
XQItemType* itemType = new XQItemType;
const QString& typeName = typeSheetNode->tag_name();
configureItemType(itemType, typeSheetNode);
itemType->setText( typeName);
s_ItemTypeTemplates[typeName] = itemType;
}
}
bool XQItemFactory::isValid()
{
return _modelSheet && _typesSheet;
}
//! Es reicht nicht, einen itemType aus den itemType-templates zu
//! holen: möglicherweise muss der noch mit zusätzlichen attributen
//! ergänzt werden.
//! Vom aufwand ist es auch egal, ob ich daten aus dem XML in das
//! item oder den itemtyp schreiben muss.
XQItemType* XQItemFactory::makeItemType(const XQNodePtr& sheetEntry )
{
QString typeKey = sheetEntry->attribute( c_ItemType );
XQItemType* itemType = findItemTypeTemplate(typeKey);
// wir prüfen, ob im sheetEntry noch zusätzliche attribute vorhanden
// sind, die wir in dem itemType müssen
//qDebug() << " --- makeItemType: " << sheetEntry->to_string();
// über alle attribute
for (const auto& attrEntry : sheetEntry->attributes())
{
// prüfen, ob der itemType des attribute schon hat
int role = itemType->roleForAttributeKey( attrEntry.first );
// wenn ja, überschreiben
if( role != XQItem::NoRole )
{
2025-09-10 23:32:00 +02:00
QVariant newValue = XQItem::makeVariant( itemType, role, attrEntry.second );
2025-08-22 22:57:06 +02:00
itemType = itemType->replaceAttribute( newValue, role );
}
}
return itemType;
}
2025-08-27 14:06:31 +02:00
2025-08-22 22:57:06 +02:00
//! sucht einen item typ aus der map mit 'vorgefertigen' itemtypen.
XQItemType* XQItemFactory::findItemTypeTemplate(const QString& key ) const
{
if( !key.isEmpty() && s_ItemTypeTemplates.contains(key))
2025-09-12 15:38:06 +02:00
return s_ItemTypeTemplates[key];
2025-08-22 22:57:06 +02:00
throw XQException( "itemfactory: findItemTypeTemplate: not found:", key );
}
//! sucht eine model-beschreibung
XQNodePtr XQItemFactory::findModelSheet( const QString& modelName ) const
{
2025-09-27 17:21:36 +02:00
XQNodePtr modelSheet = _modelSheet->child( modelName );
2025-08-22 22:57:06 +02:00
if( !modelSheet )
throw XQException( "model sheet not found: ", modelName );
return modelSheet;
}
//! erzeugt eine QVariant aus dem gegebenen string und setzt diese dann via role im item.
2025-09-10 23:32:00 +02:00
void XQItemFactory::setItemTypeDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const
2025-08-22 22:57:06 +02:00
{
int dataRole = XQItem::fetchItemDataRole( roleKey );
if( dataRole != XQItem::NoRole)
{
2025-09-10 23:32:00 +02:00
QVariant variant = XQItem::makeVariant( &item, dataRole, source );
2025-08-22 22:57:06 +02:00
if( !variant.isNull() && variant.isValid() )
item.setData( variant, dataRole );
}
}
//! erzeugt eine item-row.
2025-08-27 14:06:31 +02:00
XQItemList XQItemFactory::makeRow(const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
2025-08-22 22:57:06 +02:00
{
XQItemList list;
// - Gehe über alle Einträge der Typbeschreibung:
//
// <Battery>
// <Voltage .../>
// -> <Capacity ../>
//
// - Nimm das dazugehörige Attribut aus dem contentNode
// value = contentNode->attributes["Capacity"];
//
2025-09-28 10:59:05 +02:00
// __fix! Obacht! das setzt das vorhandensein des Contents voraus!
2025-08-22 22:57:06 +02:00
for( const auto& sheetEntry : sheetNode->children() )
2025-10-01 11:28:58 +02:00
{
2025-08-27 14:06:31 +02:00
list.append( makeItem( sheetEntry, contentNode ) );
2025-10-01 11:28:58 +02:00
}
2025-08-22 22:57:06 +02:00
Q_ASSERT(!list.empty());
2025-08-22 23:07:56 +02:00
// wir merken uns den original content node auch, aber
2025-08-27 14:06:31 +02:00
// im ersten Item. Kann null sein, macht aber erstmal nix.
2025-08-22 23:07:56 +02:00
dynamic_cast<XQItem*>(list[0])->setContentNode(contentNode);
2025-08-22 22:57:06 +02:00
return list;
}
2025-09-12 15:38:06 +02:00
//! Erzeugt kind-items zu einem section-eintrag.
2025-09-29 23:47:59 +02:00
XQItemList XQItemFactory::makeChildRow( QStandardItem* parent, const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
{
XQItemList list;
for (const auto& contentChild : contentNode->children())
{
const QString& contentKey = contentChild->tag_name();
// wir brauchen ein beschreibenden sheetnode für diesen content-child knoten
if( sheetNode->has_child( contentKey ))
{
const XQNodePtr& sheetChild = sheetNode->child(contentKey);
list = makeRow( sheetChild, contentChild );
2025-09-29 23:47:59 +02:00
// als kinder einfügen
//insertRow( parent->row()+1, list );
parent->appendRow( list );
}
}
return list;
2025-09-12 15:38:06 +02:00
}
2025-08-22 22:57:06 +02:00
2025-08-27 14:06:31 +02:00
//! Erzeugt ein XQItem aus einer typ-beschreibung ('sheetNode') und einem daten-knoten ('contentNode').
//! Wenn der content node nicht gesetzt ist, wird stattdess das attribut 'Caption' aus der typ-beschreibung
//! verwendet: es handelt sich dann um ein header item
2025-08-22 22:57:06 +02:00
2025-08-27 14:06:31 +02:00
XQItem* XQItemFactory::makeItem(const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
2025-08-22 22:57:06 +02:00
{
// den itemtype des neuen items rausfinden
XQItemType* itemType = makeItemType(sheetNode); // throws
2025-08-22 23:07:56 +02:00
const QString* contentPtr{};
2025-08-22 22:57:06 +02:00
2025-08-23 19:33:29 +02:00
// das ist Unterschied vom HeaderItem zum normalen Item: Der Titel kommt aus der Modelbeschreibung,
// sonst wird der content indirekt über den tag-name des sheetnode geholt
2025-08-27 14:06:31 +02:00
if( !contentNode )
contentPtr = sheetNode->attribute_ptr(c_Caption);
else
contentPtr = contentNode->attribute_ptr( sheetNode->tag_name() );
2025-08-23 19:33:29 +02:00
2025-08-27 14:06:31 +02:00
XQItem* newItem = new XQItem( itemType, contentPtr);
2025-08-23 19:33:29 +02:00
2025-08-27 14:06:31 +02:00
// __fixme!
if( newItem->isCheckable() )
{
newItem->setCheckState( Qt::Checked );
2025-08-23 19:33:29 +02:00
}
2025-08-22 22:57:06 +02:00
2025-08-27 14:06:31 +02:00
return newItem;
}
2025-08-22 22:57:06 +02:00
2025-08-27 14:06:31 +02:00
//! Erzeugt ein Item _ohne_ internen content node, sondern
XQItem* XQItemFactory::makeSingleItem( const XQNodePtr& sheetNode, const QString& caption )
{
// den itemtype des neuen items rausfinden
XQItemType* itemType = makeItemType(sheetNode); // throws
XQItem* newItem = new XQItem( itemType, caption);
2025-08-22 22:57:06 +02:00
// __fixme!
if( newItem->isCheckable() )
{
newItem->setCheckState( Qt::Checked );
}
return newItem;
}