/*************************************************************************** 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 #include #include #include #include #include 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; 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 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_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 ) { QVariant newValue = makeVariant(role, attrEntry.second ); itemType = itemType->replaceAttribute( newValue, role ); } } return itemType; } //! sucht einen item typ aus der map mit 'vorgefertigen' itemtypen. XQItemType* XQItemFactory::findItemTypeTemplate(const QString& key ) const { if( !key.isEmpty() && s_ItemTypeTemplates.contains(key)) return s_ItemTypeTemplates[key]; throw XQException( "itemfactory: findItemTypeTemplate: not found:", key ); } //! sucht eine model-beschreibung 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; } //! erzeugt eine QVariant aus dem gegebenen string und setzt diese dann via role im item. 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 ); } } //! erzeugt eine QVariant aus dem gegebenen string 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* qDebug() << " --- makeVariant: make ItemType: " << source; XQItemType* itemType = findItemTypeTemplate( 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: { qDebug() << " --- make unit type: " << source; 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 = 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; } /// /// ------------------------------------------------ /// //! 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, das erzeugt wurde. XQItem* XQItemFactory::makeItem( const XQNodePtr& sheetNode, const XQNodePtr& contentNode ) { // den itemtype des neuen items rausfinden QString typeKey = sheetNode->attribute(c_ItemType); XQItemType* itemType = makeItemType(sheetNode); // throws // fallunterscheidung beim inhalt: const QString* contentPtr{}; // das ist Unterschied zum normalen Item: Der Titel kommt aus der Modelbeschreibung if(!contentNode) contentPtr = sheetNode->attribute_ptr(c_Caption); else // der content wird indirect über den tag-name des sheetnode geholt contentPtr = contentNode->attribute_ptr( sheetNode->tag_name() ); return new XQItem( itemType, contentPtr ); } //! erzeugt eine item-row. XQItemList XQItemFactory::makeContentRow( const XQNodePtr& sheetNode, const XQNodePtr& contentNode ) { XQItemList list; // - Gehe über alle Einträge der Typbeschreibung: // // // // -> // // - Nimm das dazugehörige Attribut aus dem contentNode // value = contentNode->attributes["Capacity"]; // for( const auto& sheetEntry : sheetNode->children() ) { list.append( makeItem( sheetEntry, contentNode ) ); } if( !list.empty() ) { // wir merken uns den original content node auch, aber // im ersten Item. dynamic_cast(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; ichildren() ) { 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 ); } */