// Copyright (C) 2022 The Qt Company Ltd // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "objectsmapdocument.h" #include "objectsmaptreeitem.h" #include "squishconstants.h" #include "squishplugin.h" #include "squishsettings.h" #include "squishtr.h" #include #include #include namespace Squish { namespace Internal { static const char kItemSeparator = '\n'; static const char kPropertySeparator = '\t'; ObjectsMapDocument::ObjectsMapDocument() : m_contentModel(new ObjectsMapModel(this)) , m_isModified(false) { setMimeType(Constants::SQUISH_OBJECTSMAP_MIMETYPE); setId(Constants::OBJECTSMAP_EDITOR_ID); connect(m_contentModel, &ObjectsMapModel::modelChanged, this, [this]() { setModified(true); }); } Core::IDocument::OpenResult ObjectsMapDocument::open(QString *errorString, const Utils::FilePath &fileName, const Utils::FilePath &realFileName) { OpenResult result = openImpl(errorString, fileName, realFileName); if (result == OpenResult::Success) { setFilePath(fileName); setModified(fileName != realFileName); } return result; } bool ObjectsMapDocument::save(QString *errorString, const Utils::FilePath &fileName, bool autoSave) { const Utils::FilePath actual = fileName.isEmpty() ? filePath() : fileName; if (actual.isEmpty()) return false; const bool writeOk = writeFile(actual); if (!writeOk) { if (errorString) *errorString = Tr::tr("Failed to write \"%1\"").arg(actual.toUserOutput()); return false; } if (!autoSave) { setModified(false); setFilePath(actual); } return true; } Utils::FilePath ObjectsMapDocument::fallbackSaveAsPath() const { return Utils::FilePath(); } QString ObjectsMapDocument::fallbackSaveAsFileName() const { return "objects.map"; } void ObjectsMapDocument::setModified(bool modified) { m_isModified = modified; emit changed(); } bool ObjectsMapDocument::reload(QString *errorString, Core::IDocument::ReloadFlag flag, Core::IDocument::ChangeType type) { Q_UNUSED(type); if (flag == FlagIgnore) return true; emit aboutToReload(); const bool success = (openImpl(errorString, filePath(), filePath()) == OpenResult::Success); if (success) setModified(false); emit reloadFinished(success); return success; } bool ObjectsMapDocument::buildObjectsMapTree(const QByteArray &contents) { QMap itemForName; // get names and their properties as we don't have correct (creation) order inside objects.map const QList lines = contents.split(kItemSeparator); for (const QByteArray &line : lines) { if (line.isEmpty()) continue; const int tabPosition = line.indexOf(kPropertySeparator); const QString objectName = QLatin1String(line.left(tabPosition).trimmed()); if (!objectName.startsWith(ObjectsMapTreeItem::COLON)) { qDeleteAll(itemForName); return false; } ObjectsMapTreeItem *item = new ObjectsMapTreeItem(objectName, Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); item->setPropertiesContent(line.mid(tabPosition + 1).trimmed()); itemForName.insert(objectName, item); item->initPropertyModelConnections(m_contentModel); } // now build the tree ObjectsMapTreeItem *root = new ObjectsMapTreeItem(QString()); QMap::iterator end = itemForName.end(); for (ObjectsMapTreeItem *item : std::as_const(itemForName)) { const QString &parentName = item->parentName(); auto parent = itemForName.find(parentName); if (parent != end) parent.value()->appendChild(item); else root->appendChild(item); } m_contentModel->changeRootItem(root); return true; } bool ObjectsMapDocument::setContents(const QByteArray &contents) { return buildObjectsMapTree(contents); } QByteArray ObjectsMapDocument::contents() const { QByteArray result; QMap objects; m_contentModel->forAllItems([&objects](ObjectsMapTreeItem *item) { if (item->parent()) objects.insert(item->data(0, Qt::DisplayRole).toString(), item->properties()); }); const QStringList &keys = objects.keys(); for (const QString &objName : keys) { result.append(objName.toUtf8()); result.append(kPropertySeparator); const PropertyList properties = objects.value(objName); // ensure to store invalid properties content as is instead of an empty {} if (properties.isEmpty()) { if (Utils::TreeItem *item = m_contentModel->findItem(objName)) { ObjectsMapTreeItem *objMapItem = static_cast(item); if (!objMapItem->isValid()) { result.append(objMapItem->propertiesContent()).append(kItemSeparator); continue; } } } result.append('{'); for (const Property &property : properties) { result.append(property.toString().toUtf8()); result.append(' '); } // remove the last space added by the last property if (result.at(result.size() - 1) == ' ') result.chop(1); result.append('}'); result.append(kItemSeparator); } return result; } Core::IDocument::OpenResult ObjectsMapDocument::openImpl(QString *error, const Utils::FilePath &fileName, const Utils::FilePath &realFileName) { if (fileName.isEmpty()) return OpenResult::CannotHandle; QByteArray text; if (realFileName.fileName() == "objects.map") { Utils::FileReader reader; if (!reader.fetch(realFileName, QIODevice::Text, error)) return OpenResult::ReadError; text = reader.data(); } else { const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath.filePath(); if (base.isEmpty()) { if (error) error->append(Tr::tr("Incomplete Squish settings. " "Missing Squish installation path.")); return OpenResult::ReadError; } const Utils::FilePath exe = base.pathAppended("lib/exec/objectmaptool").withExecutableSuffix(); if (!exe.isExecutableFile()) { if (error) error->append(Tr::tr("objectmaptool not found.")); return OpenResult::ReadError; } Utils::QtcProcess objectMapReader; objectMapReader.setCommand({exe, {"--scriptMap", "--mode", "read", "--scriptedObjectMapPath", realFileName.toUserOutput()}}); objectMapReader.setCodec(QTextCodec::codecForName("UTF-8")); objectMapReader.start(); objectMapReader.waitForFinished(); text = objectMapReader.cleanedStdOut().toUtf8(); } if (!setContents(text)) { if (error) error->append(Tr::tr("Failure while parsing objects.map content.")); return OpenResult::ReadError; } return OpenResult::Success; } bool ObjectsMapDocument::writeFile(const Utils::FilePath &fileName) const { if (fileName.endsWith("objects.map")) { Utils::FileSaver saver(fileName); return saver.write(contents()) && saver.finalize(); } // otherwise we need the objectmaptool to write the scripted object map again const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath.filePath(); if (base.isEmpty()) return false; const Utils::FilePath exe = base.pathAppended("lib/exec/objectmaptool").withExecutableSuffix(); if (!exe.isExecutableFile()) return false; Utils::QtcProcess objectMapWriter; objectMapWriter.setCommand({exe, {"--scriptMap", "--mode", "write", "--scriptedObjectMapPath", fileName.toUserOutput()}}); objectMapWriter.setWriteData(contents()); objectMapWriter.start(); objectMapWriter.waitForFinished(); return objectMapWriter.result() == Utils::ProcessResult::FinishedWithSuccess; } } // namespace Internal } // namespace Squish