Files
xtree.ng/src/items/xqitem.cpp
2025-08-24 09:44:51 +02:00

707 lines
16 KiB
C++

/***************************************************************************
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 <xqviewmodel.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()}
{
}
XQItem::XQItem( XQItemType* itemType )
: QStandardItem{}
{
setItemType( itemType );
}
XQItem::XQItem(XQItemType* itemType, const QString *content )
: XQItem{ itemType }
{
setContent(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<XQItemType*>() != dummyType;
}
//! gibt den content-node zurück.
XQNodePtr XQItem::contentNode() const
{
XQNodePtr node = data( ContentNodeRole ).value<XQNodePtr>();
if( node )
return node;
throw XQException("XQItem::contentNode() nullptr");
}
//! 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<XQNodePtr>();
}
//! 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<XQItemType*>();
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<RenderStyle>();
}
//! 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<EditorType>();
}
//! 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<UnitType>();
}
//! 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, ws draufsteht: gibt einen string* zurück
return data( XQItem::ContentRole ).value<QString*>();
}
//! set den content()-string pointer. (als leihgabe)
void XQItem::setContent( const QString* content )
{
setData( QVariant::fromValue<const QString*>(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();
}
//! setz 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<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) const
{
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);
}
// Zugriffe auf den sichtbaren inhalt geben den inhalt des string pointer
// auf ein feld in content node wieder.
// DisplayRole gibt den formatieren inhalt wieder. die formatierung übernimmt
// der item type
// auf den original inhalt im content node zurückgeben.
case Qt::DisplayRole :
{
if( itemType().renderStyle() == XQItem::FormattedStyle)//return "display:"+content();
return itemType().formatText( *this );
[[fallthrough]];
}
// EditRole & ContentRole sollen den 'rohen' inhalt unseres string-pointers
// auf den original inhalt im content node zurückgeben.
case Qt::EditRole :
case XQItem::ContentRole:
{
const QString* contentPtr = QStandardItem::data( XQItem::ContentRole ).value<const QString*>();
if(contentPtr)
return *contentPtr;
static const QString s_dummyContent("-");
return s_dummyContent;
}
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;
}
// set the raw, unformatted data
case ContentRole:
{
// string ptr setzen kann die basis.
break;
}
case Qt::EditRole :
{
qDebug() << " --- setting EDITrole: " << value.toString();
break;
}
case Qt::DisplayRole :
{
// what will happen? value is a string ptr ?!
qDebug() << " --- setting DISPLAYrole: " << value.toString();
break;
}
// 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<const XQViewModel*>(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];
}
/// ---
/// ---
/// ---