File indexing completed on 2024-06-23 05:48:59

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2009, 2010, 2011, 2013 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "datainformation.hpp"
0010 
0011 #include "topleveldatainformation.hpp"
0012 #include "additionaldata.hpp"
0013 #include "structureviewpreferences.hpp"
0014 #include "../script/scriptlogger.hpp"
0015 #include "../script/safereference.hpp"
0016 
0017 #include <QApplication>
0018 #include <QScriptValue>
0019 #include <QScriptEngine>
0020 #include <QIcon>
0021 
0022 #include <KLocalizedString>
0023 #include <KColorScheme>
0024 
0025 DataInformation::DataInformation(const QString& name, DataInformationBase* parent)
0026     : mParent(parent)
0027     , mName(name)
0028     , mValidationSuccessful(false)
0029     , mHasBeenValidated(false)
0030     , mHasBeenUpdated(false)
0031     , mWasAbleToRead(false)
0032     , mWasAbleToReadBefore(false)
0033     , mLoggedData(ScriptLogger::LogInvalid)
0034 {
0035 }
0036 
0037 DataInformation::DataInformation(const DataInformation& d)
0038     : mAdditionalData(d.mAdditionalData)
0039     , mParent(nullptr)
0040     , mName(d.mName)
0041     , mValidationSuccessful(false)
0042     , mHasBeenValidated(false)
0043     , mHasBeenUpdated(false)
0044     , mWasAbleToRead(false)
0045     , mWasAbleToReadBefore(false)
0046     , mByteOrder(d.mByteOrder)
0047     , mLoggedData(ScriptLogger::LogInvalid)
0048 {
0049 }
0050 
0051 DataInformation::~DataInformation()
0052 {
0053     // references to this are no longer valid
0054     SafeReferenceHolder::instance.invalidateAll(this);
0055 }
0056 
0057 QString DataInformation::sizeString(BitCount32 size)
0058 {
0059     if (size % 8 == 0) { // no bits remaining
0060         return i18np("1 byte", "%1 bytes", size / 8);
0061     }
0062 
0063     const QString bytes = i18np("1 byte", "%1 bytes", size / 8);
0064     const QString bits = i18np("1 bit", "%1 bits", size % 8);
0065     return i18nc("number of bytes, then number of bits", "%1 %2", bytes, bits);
0066 }
0067 
0068 QString DataInformation::sizeString() const
0069 {
0070     return sizeString(size());
0071 }
0072 
0073 QString DataInformation::valueStringImpl() const
0074 {
0075     return {};
0076 }
0077 
0078 BitCount64 DataInformation::positionInFile(Okteta::Address start) const
0079 {
0080     Q_CHECK_PTR(mParent);
0081     if (mParent->isTopLevel()) {
0082         return start * 8; // this is the root of the structure
0083 
0084     }
0085     const DataInformation* par = mParent->asDataInformation();
0086     return par->childPosition(this, start);
0087 }
0088 
0089 DataInformation* DataInformation::mainStructure()
0090 {
0091     Q_CHECK_PTR(mParent);
0092     if (mParent->isTopLevel()) {
0093         return this;
0094     }
0095 
0096     return mParent->asDataInformation()->mainStructure();
0097 }
0098 
0099 void DataInformation::setValidationError(const QString& errorMessage)
0100 {
0101     if (errorMessage.isEmpty()) {
0102         mAdditionalData.remove(AdditionalData::AdditionalDataType::ValidationError);
0103     } else {
0104         mAdditionalData.set(AdditionalData::AdditionalDataType::ValidationError, errorMessage);
0105     }
0106 }
0107 
0108 void DataInformation::setCustomTypeName(const QString& customTypeName)
0109 {
0110     if (customTypeName.isEmpty()) {
0111         mAdditionalData.remove(AdditionalData::AdditionalDataType::CustomTypeName);
0112     } else {
0113         mAdditionalData.set(AdditionalData::AdditionalDataType::CustomTypeName, customTypeName);
0114     }
0115 }
0116 
0117 void DataInformation::resetValidationState()
0118 {
0119     mAdditionalData.remove(AdditionalData::AdditionalDataType::ValidationError);
0120     mHasBeenValidated = false;
0121     mValidationSuccessful = false;
0122 }
0123 
0124 void DataInformation::beginRead()
0125 {
0126     // remember old state
0127     // TODO: perhaps just do in some way (virtual method) for those subclasses which use this flag?
0128     mWasAbleToReadBefore = mWasAbleToRead;
0129 
0130     mHasBeenUpdated = false;
0131     mWasAbleToRead = false;
0132     mHasBeenValidated = false;
0133     mLoggedData = ScriptLogger::LogInvalid;
0134     const uint numChildren = childCount();
0135     if (numChildren > 0 && childAt(0)->isDummy()) {
0136         return; // first child is dummy -> all are
0137     }
0138     // otherwise start read for all children
0139     for (uint i = 0; i < childCount(); ++i) {
0140         childAt(i)->beginRead();
0141     }
0142 }
0143 
0144 ScriptLogger* DataInformation::logger() const
0145 {
0146     return topLevelDataInformation()->logger();
0147 }
0148 
0149 void DataInformation::setAdditionalFunction(AdditionalData::AdditionalDataType entry, const QScriptValue& value,
0150                                             const char* name)
0151 {
0152     if (!value.isValid() || value.isNull() || value.isUndefined()) {
0153         // removal requested
0154         mAdditionalData.remove(entry);
0155         return;
0156     }
0157     if (!value.isFunction()) {
0158         logError() << "cannot set" << name << "since" << value.toString() << "is not a function!";
0159         return;
0160     }
0161     mAdditionalData.set(entry, QVariant::fromValue(value));
0162 }
0163 
0164 QVariant DataInformation::data(int column, int role) const
0165 {
0166     if (role == Qt::DisplayRole) {
0167         if (column == ColumnName) {
0168             if (mParent && mParent->isArray()) {
0169                 return QString(QLatin1Char('[') + QString::number(row()) + QLatin1Char(']'));
0170             }
0171 
0172             return name();
0173         }
0174         if (column == ColumnType) {
0175             return typeName();
0176         }
0177         if (column == ColumnValue) {
0178             return mWasAbleToRead ? valueString() : eofReachedData(Qt::DisplayRole);
0179         }
0180         if (column == ColumnSize) {
0181             return sizeString();
0182         }
0183     } else if (role == Qt::ToolTipRole) {
0184         return tooltipString();
0185     } else if (role == Qt::DecorationRole && column == ColumnName) {
0186         // XXX better icons?
0187         if (mHasBeenValidated) {
0188             return QIcon::fromTheme(mValidationSuccessful ? QStringLiteral("task-complete") : QStringLiteral("dialog-warning"));
0189         }
0190         if (mLoggedData != ScriptLogger::LogInvalid) {
0191             return ScriptLogger::iconForLevel(mLoggedData);
0192         }
0193     } else if (!mWasAbleToRead && column == ColumnValue) {
0194         return eofReachedData(role);
0195     }
0196     return {};
0197 }
0198 
0199 QVariant DataInformation::eofReachedData(int role)
0200 {
0201     // whenever color scheme changes application must be restarted to have the correct color
0202     static KColorScheme scheme(QPalette::Active);
0203 
0204     if (role == Qt::DisplayRole) {
0205         return i18nc("invalid value (End of file reached)", "<EOF reached>");
0206     }
0207     if (role == Qt::ForegroundRole) {
0208         return scheme.foreground(KColorScheme::NegativeText);
0209     }
0210     return {};
0211 }
0212 
0213 QString DataInformation::tooltipString(const QString& nameString, const QString& valueString,
0214                                        const QString& typeString, const QString& sizeString,
0215                                        unsigned int childCount,
0216                                        const QString& validationMessage)
0217 {
0218     const bool textIsLeftToRight = (QApplication::layoutDirection() == Qt::LeftToRight);
0219 
0220     const QString boldStyle = QStringLiteral(";font-weight:bold");
0221     const QString one = QStringLiteral("1");
0222     const QString two = QStringLiteral("2");
0223     const QString line = QStringLiteral(
0224         "<tr>"
0225           "<td align=\"right\" style=\"white-space:nowrap%1\">%%2</td>"
0226           "<td align=\"left\" style=\"white-space:nowrap%3\">%%4</td>"
0227         "</tr>").arg(
0228             (textIsLeftToRight ? boldStyle : QString()),
0229             (textIsLeftToRight ? one : two),
0230             (textIsLeftToRight ? QString() : boldStyle),
0231             (textIsLeftToRight ? two : one)
0232         );
0233 
0234     const QString nameLabel = i18n("Name:").toHtmlEscaped();
0235     const QString typeLabel = i18n("Type:").toHtmlEscaped();
0236     const QString valueLabel = i18n("Value:").toHtmlEscaped();
0237     const QString sizeLabel = i18n("Size:").toHtmlEscaped();
0238 
0239     const QString name = nameString.toHtmlEscaped();
0240     const QString type = typeString.toHtmlEscaped();
0241     const QString value = valueString.toHtmlEscaped();
0242     const QString size = (childCount == 0) ?  sizeString.toHtmlEscaped() :
0243         i18ncp("size (elements)", "%2 (%1 child)", "%2 (%1 children)", childCount, sizeString).toHtmlEscaped();
0244 
0245     QString result =
0246         QLatin1String("<table>") +
0247         line.arg(nameLabel, name) +
0248         line.arg(typeLabel, type) +
0249         line.arg(valueLabel, value) +
0250         line.arg(sizeLabel, size) +
0251         QLatin1String("</table>");
0252 
0253     if (!validationMessage.isEmpty()) {
0254         const QString validation = validationMessage.toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("<br/>"));
0255         result += QLatin1String("<p>") + validation + QLatin1String("</p>");
0256     }
0257     return result;
0258 }
0259 
0260 QString DataInformation::tooltipString() const
0261 {
0262     QString valueStr = mWasAbleToRead ? valueString() : eofReachedData(Qt::DisplayRole).toString();
0263     QString validationMsg;
0264     if (mHasBeenValidated && !mValidationSuccessful) {
0265         validationMsg = validationError();
0266         if (validationMsg.isEmpty()) {
0267             validationMsg = i18nc("not all values in this structure"
0268                                   " are as they should be", "Validation failed.");
0269         } else {
0270             validationMsg = i18nc("not all values in this structure are as they should be",
0271                                   "Validation failed: \"%1\"", validationMsg);
0272         }
0273     }
0274     return DataInformation::tooltipString(name(), valueStr, typeName(), sizeString(), 0,
0275                                           validationMsg);
0276 }
0277 
0278 DataInformation* DataInformation::child(const QString& name) const
0279 {
0280     int size = childCount();
0281     for (int i = 0; i < size; ++i) {
0282         DataInformation* child = childAt(i);
0283         if (child->name() == name) {
0284             return child;
0285         }
0286     }
0287 
0288     return nullptr;
0289 }
0290 
0291 TopLevelDataInformation* DataInformation::topLevelDataInformation() const
0292 {
0293     Q_CHECK_PTR(mParent);
0294     if (mParent->isTopLevel()) {
0295         return mParent->asTopLevel();
0296     }
0297 
0298     return mParent->asDataInformation()->topLevelDataInformation();
0299 }
0300 
0301 int DataInformation::row() const
0302 {
0303     Q_CHECK_PTR(mParent);
0304     if (mParent->isTopLevel()) {
0305         return mParent->asTopLevel()->indexOf(this);
0306     }
0307 
0308     return mParent->asDataInformation()->indexOf(this);
0309 }
0310 
0311 QString DataInformation::fullObjectPath() const
0312 {
0313     DataInformationBase* par = parent();
0314     if (!par || par->isTopLevel()) {
0315         return name();
0316     }
0317     QString result;
0318     if (par->isArray()) {
0319         result = QLatin1Char('[') + QString::number(par->asDataInformation()->indexOf(this))
0320                  + QLatin1Char(']');
0321     } else {
0322         result = QLatin1Char('.') + name();
0323     }
0324     result.prepend(par->asDataInformation()->fullObjectPath());
0325     return result;
0326 }
0327 
0328 QScriptValue DataInformation::toScriptValue(QScriptEngine* engine, ScriptHandlerInfo* handlerInfo)
0329 {
0330     return engine->newObject(scriptClass(handlerInfo),
0331                              engine->newVariant(QVariant::fromValue(SafeReference(this))));
0332 }
0333 
0334 QSysInfo::Endian DataInformation::byteOrderFromSettings() const
0335 {
0336     return Kasten::StructureViewPreferences::byteOrder();
0337 }
0338 
0339 QScriptValue DataInformation::toScriptValue(TopLevelDataInformation* top)
0340 {
0341     return toScriptValue(top->scriptEngine(), top->scriptHandler()->handlerInfo());
0342 }
0343 
0344 QString DataInformation::customToString(const QScriptValue& func) const
0345 {
0346     if (!wasAbleToRead()) {
0347         logError() << "Attempting to call custom to string function, but element could not be read";
0348         return valueStringImpl();
0349     }
0350     Q_ASSERT(func.isFunction());
0351     return topLevelDataInformation()->scriptHandler()->customToString(this, func);
0352 }