/*************************************************************************** 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 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 }; //! Default konstruktor, setzt einen ungültigen (dummy) //! itemType. XQItem::XQItem() : XQItem{XQItemType::staticItemType()} { } //! Default konstruktor mit einem vorhandenen itemType. XQItem::XQItem( XQItemType* itemType ) : QStandardItem{} { setItemType( itemType ); } //! konstruiert ein daten-item mit zeiger auf 'unser' attribut //! im übergeordneten content-node. XQItem::XQItem(XQItemType* itemType, const QString *content ) : XQItem{ itemType } { setContent(content); } XQItem::XQItem( XQItemType* itemType, const QString& content ) : XQItem{ itemType } { setText(content); } //! ruft den copy-konstruktor auf. XQItem* XQItem::clone() const { return new XQItem( *this ); } //! false für ein ungültiges item. 'ungültig' heisst hier, dass nur ein //! mockup-itemtype gesetzt ist. bool XQItem::isValid() const { XQItemType* dummyType = XQItemType::staticItemType(); return QStandardItem::data( XQItem::ItemTypeRole ).value() != dummyType; } //! testet, ob es einen content-node gibt. bool XQItem::hasContentNode() const { return contentNode() != nullptr; } //! gibt den content-node zurück. XQNodePtr XQItem::contentNode() const { return data( ContentNodeRole ).value(); } //! setzt den content node. void XQItem::setContentNode( const XQNodePtr& contentNode ) { QStandardItem::setData( QVariant::fromValue(contentNode), ContentNodeRole); } //! gibt den sheet-node zurück. XQNodePtr XQItem::sheetNode() const { return data( SheetNodeRole ).value(); } //! setzt den sheet-node void XQItem::setSheetNode(const XQNodePtr& sheetNode ) { QStandardItem::setData( QVariant::fromValue(sheetNode), SheetNodeRole); } //! gibt eine referenz auf den itemType dieses items zurück. XQItemType& XQItem::itemType() const { // __fix: wir gehen hier davon aus, das der itemType immer existiert, // nur weil er in jeden konstruktor gesetzt wird, das muss aber nicht // so sein! XQItemType* itemTypePtr = QStandardItem::data( XQItem::ItemTypeRole ).value(); return *itemTypePtr; // should_throw } //! speichert einen neuen itemType. void XQItem::setItemType( XQItemType* itemTypePtr ) { // der ItemType wird direkt hier gespeichert QStandardItem::setData( QVariant::fromValue(itemTypePtr), XQItem::ItemTypeRole ); } //! set ein einzelnes itemFlag. void XQItem::addFlag( Qt::ItemFlag newFlag ) { setFlags( flags() | newFlag ); } //! löscht ein einzelnes itemFlag. void XQItem::clearFlag( Qt::ItemFlag newFlag ) { setFlags( flags() & ~newFlag); } //! gibt die itemFlags als string zurück. QString XQItem::itemFlagsToString() const { return'(' + data(XQItem::FlagsRole).toString() +')'; } /// /// data() access shortcuts /// //! gibt den renderStyle zurück. XQItem::RenderStyle XQItem::renderStyle() const { return data( RenderStyleRole ).value(); } //! gibt den renderStyle als string zurück. QString XQItem::renderStyleToString() const { return XQItem::fetchRenderStyleToString( renderStyle() ); } //! setzt den editorType. wird im itemType gespeichert. void XQItem::setRenderStyle(RenderStyle renderStyle ) { setData( QVariant::fromValue(renderStyle), XQItem::RenderStyleRole ); } //! gibt den editorType zurück. XQItem::EditorType XQItem::editorType() const { return data( EditorTypeRole ).value(); } //! gibt den renderStyle als string zurück. QString XQItem::editorTypeToString() const { return XQItem::fetchEditorTypeToString( editorType() ); } //! setzt den editorType. wird im itemType gespeichert. void XQItem::setEditorType(EditorType editorType) { setData( QVariant::fromValue(editorType), XQItem::EditorTypeRole); } //! setzt den unitType. XQItem::UnitType XQItem::unitType() const { return data( XQItem::UnitTypeRole ).value(); } //! gibt den unitType als string zurück. QString XQItem::unitTypeToString() const { return XQItem::fetchUnitTypeToString( unitType() ); } //! setzt den editorType. wird im itemType gespeichert. void XQItem::setUnitType(UnitType unitType) { setData( QVariant::fromValue(unitType), XQItem::UnitTypeRole); } //! Verweist auf data(Qt::EditRole). Das ist der unformatierte text. QString XQItem::rawText() const { return data( Qt::EditRole ).toString(); } //! Gibt den string-zeiger auf das attribut aus unseren XQNodePtr zurück. /* QString* XQItem::content() const { // macht jetzt das, was draufsteht: gibt einen string* zurück return data( XQItem::ContentRole ).value(); } */ //! Setzt den content()-string pointer. (als leihgabe) void XQItem::setContent( const QString* content ) { setData( QVariant::fromValue(content), XQItem::ContentRole ); } //! Holt den schlüssel bzw. bezeicher des content() string aus 'unserem' content knoten. QString XQItem::contentKey() const { return contentNode()->attributes().key_of( rawText() ); } //! Gibt den content-format string zurück QString XQItem::contentFormat() const { return data( XQItem::ContentFormatRole ).toString(); } //! Setzt den den content format-string. wird im itemType gespeichert. 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). wird im itemType gespeichert. QStandardItemModel* XQItem::fixedChoices() const { return data( XQItem::FixedChoicesRole ).value(); } //! 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) const { if( role < XQItem::NoRole && model() ) return model()->roleNames()[role]; return XQItem::fetchItemDataRoleName(role); } //! Gibt den content()-String zurück, sofern vorhanden. //! sonst: gibt der ihnalt der Qt::DisplayRole als fallback //! zurück. QString XQItem::contentFallBackText() const { const QString* contentPtr = QStandardItem::data( XQItem::ContentRole ).value(); if(contentPtr) return *contentPtr; // wenn wir keinen contentPtr haben, benutzen wir als fallback // die basis-text() role return QStandardItem::data( Qt::DisplayRole ).toString(); } //! 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); } case XQItem::ContentRole: { qDebug() << " --- data(XQItem::ContentRole) should NOT be called!"; return *QStandardItem::data( XQItem::ContentRole ).value(); } // EditRole & ContentRole sollen den 'rohen' inhalt unseres string-pointers // auf den original inhalt im content node zurückgeben. case Qt::EditRole : { // Zugriffe auf den text-inhalt geben den inhalt des string pointer // auf ein feld in content-node wieder. Wenn kein content-node vorhanden // ist (single-items), wird Qt::DisplayRole zurückgeliefert. return contentFallBackText(); //[[fallthrough]]; } // DisplayRole gibt den formatierten inhalt wieder. die formatierung übernimmt // der item type case Qt::DisplayRole : { QString plainText = contentFallBackText(); if( renderStyle() == XQItem::FormattedStyle) return XQItemType::formatToSI( plainText, unitType() ); return plainText; } case Qt::ToolTipRole: { return itemType().text() + ":: " + rawText() + ":" + unitTypeToString() + ":" + renderStyleToString() + ":" + unitTypeToString() + ":" + itemFlagsToString(); } // // 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); } /*! Überschreibt setData() vom QStandardItem. Es wird unterschieden zwischen den data roles, die von 'unserem' itemType verwaltet und gespeichert werden und unseren eigenen. ein XQItem enthält nur folgende Felder: // - die ItemFlags (geerbt vom QStandardItem) stimmt nicht! - den pointer auf den ItemType - den pointer auf den inhalts-string: content() Die anderen werte werden vom itemType verwaltet: - der EditorType - der RenderStyle - der UnitType - das ContentFormat - das Icon - ggf. das choiceModel, falls dieses Item als combobox gerendert wird. */ void XQItem::setData(const QVariant& value, int role ) { switch(role) { case RenderStyleRole : case EditorTypeRole : case UnitTypeRole: case ContentFormatRole: case FlagsRole: // Stimmt das? case IconRole: case FixedChoicesRole: { //qDebug() << " ---call type set Data: " << role << ": " << XQItem::fetchItemDataRoleName(role) << ":" << value.toString(); XQItemType* oldType = &itemType(); XQItemType* newType = oldType->replaceAttribute(value, role ); if( oldType != newType ) setItemType( newType ); emitDataChanged(); // no break, return! return; } case Qt::DisplayRole: case Qt::EditRole: case XQItem::ContentRole: { QVariant plainText; if( itemType().renderStyle() == XQItem::FormattedStyle) plainText = XQItemType::unFormatFromSI( value.toString() ); else plainText = value; // fallback: wenns keinen content node gibt, dann nehmen wir // das standardverfahren. int role = XQItem::ContentRole; if( !hasContentNode() ) role = Qt::DisplayRole; QStandardItem::setData( plainText, role ); return; } // alles andere wie gehabt case ContentNodeRole: case SheetNodeRole: //case TypeKeyRole: not used default: break; } // hier: behandlung wie gehabt QStandardItem::setData( value,role); } //! gibt ein statisches invalid-item zurück, anstelle von nullptr XQItem& XQItem::fallBackDummyItem() { static XQItem s_fallBackDummyItem; return s_fallBackDummyItem; } //! gibt das item für den gegebenen index aus. XQItem& XQItem::xqItemFromIndex(const QModelIndex& index) { if (index.isValid()) { const XQViewModel* mdl = dynamic_cast(index.model()); if (mdl) return mdl->xqItemFromIndex(index); } return fallBackDummyItem(); } //! gibt den enum-type für den gegebenen datarole-key aus. int XQItem::fetchItemDataRole( const QString& dataRoleKey ) { if(!dataRoleKey.isEmpty() && s_ItemDataRoleMap.contains(dataRoleKey) ) return s_ItemDataRoleMap[ dataRoleKey ]; return NoRole; } //! gibt die bezeichung für die gegebene datarole aus. QString XQItem::fetchItemDataRoleName( int dataRole ) { return s_ItemDataRoleMap.key(dataRole); } //! gibt das flag für den gegebenen itemflag-key aus. Qt::ItemFlag XQItem::fetchItemFlag( const QString& flagKey ) { if(!flagKey.isEmpty() && s_ItemFlagMap.contains(flagKey) ) return Qt::ItemFlag( s_ItemFlagMap[ flagKey ] ); return Qt::NoItemFlags; } //! gibt die bezeichung für das gegebene itemFlag aus. QString XQItem::fetchItemFlagName( int flag ) { return s_ItemFlagMap.key(flag); } //! gibt den enum-type für den gegebenen renderStyle-key aus. XQItem::RenderStyle XQItem::fetchRenderStyle(const QString& styleKey ) { if(!styleKey.isEmpty() && s_RenderStyleMap.contains(styleKey) ) return s_RenderStyleMap[styleKey]; return NoRenderStyle; } //! gibt die bezeichung für den gegebenen renderStyle aus. QString XQItem::fetchRenderStyleToString(XQItem::RenderStyle renderStyle ) { return s_RenderStyleMap.key(renderStyle); } //! gibt den renderstyle enum-type für den gegebenen editorType-key aus. XQItem::EditorType XQItem::fetchEditorType( const QString& editorTypeKey ) { if(!editorTypeKey.isEmpty() && s_EditorTypeMap.contains(editorTypeKey) ) return s_EditorTypeMap[editorTypeKey]; return NoEditorType; } //! gibt die bezeichung für den gegebenen editorType aus. QString XQItem::fetchEditorTypeToString( EditorType editorType ) { return s_EditorTypeMap.key(editorType); } //! gibt den unitType für den gegebenen unitType-key aus. XQItem::UnitType XQItem::fetchUnitType(const QString& unitTypeKey) { return s_UnitTypeMap.key(unitTypeKey); } //! gibt die bezeichung für den gegebenen unitType aus. QString XQItem::fetchUnitTypeToString( UnitType unitType) { return s_UnitTypeMap[unitType]; } /// --- /// --- /// ---