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 }