diff options
Diffstat (limited to 'src/Authoring/Studio/Application/ProjectFile.cpp')
-rw-r--r-- | src/Authoring/Studio/Application/ProjectFile.cpp | 1549 |
1 files changed, 0 insertions, 1549 deletions
diff --git a/src/Authoring/Studio/Application/ProjectFile.cpp b/src/Authoring/Studio/Application/ProjectFile.cpp deleted file mode 100644 index b4c18c50..00000000 --- a/src/Authoring/Studio/Application/ProjectFile.cpp +++ /dev/null @@ -1,1549 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt 3D Studio. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "ProjectFile.h" -#include "Qt3DSFileTools.h" -#include "Exceptions.h" -#include "DataInputDlg.h" -#include "StudioApp.h" -#include "Qt3DSDMStudioSystem.h" -#include "ClientDataModelBridge.h" -#include "Core.h" -#include "Doc.h" -#include "IDocumentEditor.h" -#include "PresentationFile.h" -#include "IStudioRenderer.h" -#include "StudioUtils.h" -#include "Dispatch.h" -#include "MainFrm.h" -#include <QtCore/qdiriterator.h> -#include <QtCore/qsavefile.h> -#include <QtCore/qtimer.h> -#include <QtWidgets/qmessagebox.h> - -ProjectFile::ProjectFile() -{ - -} - -// find the 1st .uia file in the current or parent directories and assume this is the project file, -// as a project should have only 1 .uia file -void ProjectFile::ensureProjectFile() -{ - if (!m_fileInfo.exists()) { - QFileInfo uipInfo(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath()); - QString uiaPath(PresentationFile::findProjectFile(uipInfo.absoluteFilePath())); - - if (uiaPath.isEmpty()) { - // .uia not found, create a new one in the same folder as uip. Creation sets file info. - create(uipInfo.absoluteFilePath().replace(QLatin1String(".uip"), - QLatin1String(".uia"))); - addPresentationNode(uipInfo.absoluteFilePath()); - updateDocPresentationId(); - } else { - // .uia found, set project file info - m_fileInfo.setFile(uiaPath); - } - } -} - -void ProjectFile::initProjectFile(const QString &presPath) -{ - QFileInfo uipFile(presPath); - QString uiaPath(PresentationFile::findProjectFile(uipFile.absoluteFilePath())); - - if (uiaPath.isEmpty()) { - // .uia not found, clear project file info - m_fileInfo = QFileInfo(); - } else { - // .uia found, set project file info - m_fileInfo.setFile(uiaPath); - } -} - -/** - * Add a presentation or presentation-qml node to the project file - * - * @param pPath the absolute path to the presentation file, it will be saved as relative - * @param pId presentation Id - */ -void ProjectFile::addPresentationNode(const QString &pPath, const QString &pId) -{ - addPresentationNodes({{pPath, pId}}); -} - -// Add a list of presentation or presentation-qml nodes to the project file -void ProjectFile::addPresentationNodes(const QHash<QString, QString> &nodeList) -{ - ensureProjectFile(); - - QDomDocument doc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, doc)) - return; - - QDomElement rootElem = doc.documentElement(); - QDomElement assetsElem = rootElem.firstChildElement(QStringLiteral("assets")); - - // create the <assets> node if it doesn't exist - bool initial = false; - if (assetsElem.isNull()) { - assetsElem = doc.createElement(QStringLiteral("assets")); - rootElem.insertBefore(assetsElem, {}); - initial = true; - } - - QHash<QString, QString> changesList; - QHashIterator<QString, QString> nodesIt(nodeList); - while (nodesIt.hasNext()) { - nodesIt.next(); - const QString presPath = nodesIt.key(); - const QString presId = nodesIt.value(); - QString relativePresentationPath - = QDir(getProjectPath()).relativeFilePath(presPath); - - // make sure the node doesn't already exist - bool nodeExists = false; - for (QDomElement p = assetsElem.firstChild().toElement(); !p.isNull(); - p = p.nextSibling().toElement()) { - if ((p.nodeName() == QLatin1String("presentation") - || p.nodeName() == QLatin1String("presentation-qml")) - && p.attribute(QStringLiteral("src")) == relativePresentationPath) { - nodeExists = true; - break; - } - } - - if (!nodeExists) { - const QString presentationId - = ensureUniquePresentationId(presId.isEmpty() - ? QFileInfo(presPath).completeBaseName() - : presId); - - if (assetsElem.attribute(QStringLiteral("initial")).isEmpty()) { - assetsElem.setAttribute(QStringLiteral("initial"), presentationId); - m_initialPresentation = presentationId; - } - - // add the presentation node - bool isQml = presPath.endsWith(QLatin1String(".qml")); - QDomElement pElem = isQml ? doc.createElement(QStringLiteral("presentation-qml")) - : doc.createElement(QStringLiteral("presentation")); - pElem.setAttribute(QStringLiteral("id"), presentationId); - pElem.setAttribute(isQml ? QStringLiteral("args") : QStringLiteral("src"), - relativePresentationPath); - assetsElem.appendChild(pElem); - changesList.insert(relativePresentationPath, presentationId); - - if (!initial) { - g_StudioApp.m_subpresentations.push_back( - SubPresentationRecord(isQml ? QStringLiteral("presentation-qml") - : QStringLiteral("presentation"), - presentationId, relativePresentationPath)); - } - } - } - - if (initial || changesList.size() > 0) - StudioUtils::commitDomDocumentSave(file, doc); - - if (changesList.size() > 0) { - g_StudioApp.getRenderer().RegisterSubpresentations(g_StudioApp.m_subpresentations); - - QHashIterator<QString, QString> changesIt(changesList); - while (changesIt.hasNext()) { - changesIt.next(); - Q_EMIT presentationIdChanged(changesIt.key(), changesIt.value()); - } - } -} - -// Get the src attribute (relative path) to the initial presentation in a uia file, if no initial -// presentation exists, the first one is returned. Returns empty string if file cannot be read. -QString ProjectFile::getInitialPresentationSrc(const QString &uiaPath) -{ - QDomDocument domDoc; - if (!StudioUtils::readFileToDomDocument(uiaPath, domDoc)) - return {}; - - QString firstPresentationSrc; - QDomElement assetsElem = domDoc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - QString initialId = assetsElem.attribute(QStringLiteral("initial")); - if (!initialId.isEmpty()) { - QDomNodeList pNodes = assetsElem.elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < pNodes.count(); ++i) { - QDomElement pElem = pNodes.at(i).toElement(); - if (pElem.attribute(QStringLiteral("id")) == initialId) - return pElem.attribute(QStringLiteral("src")); - - if (i == 0) - firstPresentationSrc = pElem.attribute(QStringLiteral("src")); - } - } - } - - return firstPresentationSrc; -} - -/** - * Write a presentation id to the project file. - * If the presentation id doesn't exist yet in project, it's added. - * - * This also updates the Doc presentation Id if the src param is empty - * or same as current presentation. - * - * @param id presentation Id - * @param src source node, if empty the current document node is used - */ -void ProjectFile::writePresentationId(const QString &id, const QString &src) -{ - ensureProjectFile(); - - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - QString theSrc = src.isEmpty() ? doc->getRelativePath() : src; - QString theId = id.isEmpty() ? doc->getPresentationId() : id; - bool isQml = theSrc.endsWith(QLatin1String(".qml")); - - if (theSrc == doc->getRelativePath()) - doc->setPresentationId(id); - - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - QDomElement assetsElem = domDoc.documentElement().firstChildElement(QStringLiteral("assets")); - QDomNodeList pqNodes = isQml ? assetsElem.elementsByTagName(QStringLiteral("presentation-qml")) - : assetsElem.elementsByTagName(QStringLiteral("presentation")); - - QString oldId; - if (!pqNodes.isEmpty()) { - for (int i = 0; i < pqNodes.count(); ++i) { - QDomElement pqElem = pqNodes.at(i).toElement(); - QString srcOrArgs = isQml ? pqElem.attribute(QStringLiteral("args")) - : pqElem.attribute(QStringLiteral("src")); - if (srcOrArgs == theSrc) { - oldId = pqElem.attribute(QStringLiteral("id")); - pqElem.setAttribute(QStringLiteral("id"), theId); - - if (assetsElem.attribute(QStringLiteral("initial")) == oldId) { - assetsElem.setAttribute(QStringLiteral("initial"), theId); - m_initialPresentation = theId; - } - break; - } - } - } - - if (!src.isEmpty() && oldId.isEmpty()) { // new presentation, add it - StudioUtils::commitDomDocumentSave(file, domDoc); - QDir projectDir(getProjectPath()); - addPresentationNode(QDir::cleanPath(projectDir.absoluteFilePath(theSrc)), theId); - } else if (!oldId.isEmpty()) { // the presentation id changed - StudioUtils::commitDomDocumentSave(file, domDoc); - - // update m_subpresentations - auto *sp = std::find_if(g_StudioApp.m_subpresentations.begin(), - g_StudioApp.m_subpresentations.end(), - [&theSrc](const SubPresentationRecord &spr) -> bool { - return spr.m_argsOrSrc == theSrc; - }); - if (sp != g_StudioApp.m_subpresentations.end()) - sp->m_id = theId; - - // update current doc instances (layers and images) that are using this presentation Id - qt3dsdm::TInstanceHandleList instancesToRefresh; - auto *bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); - qt3dsdm::IPropertySystem *propSystem = doc->GetStudioSystem()->GetPropertySystem(); - std::function<void(qt3dsdm::Qt3DSDMInstanceHandle)> - parseChildren = [&](qt3dsdm::Qt3DSDMInstanceHandle instance) { - Q3DStudio::CGraphIterator iter; - GetAssetChildren(doc, instance, iter); - - while (!iter.IsDone()) { - qt3dsdm::Qt3DSDMInstanceHandle child = iter.GetCurrent(); - if (bridge->GetObjectType(child) & (OBJTYPE_LAYER | OBJTYPE_IMAGE)) { - bool add = false; - if (bridge->GetSourcePath(child) == oldId) { - propSystem->SetInstancePropertyValue(child, bridge->GetSourcePathProperty(), - qt3dsdm::SValue(QVariant(theId))); - add = true; - } - if (bridge->getSubpresentation(child).toQString() == oldId) { - propSystem->SetInstancePropertyValue(child, - bridge->getSubpresentationProperty(), - qt3dsdm::SValue(QVariant(theId))); - add = true; - } - if (add) - instancesToRefresh.push_back(child); - } - parseChildren(child); - ++iter; - } - }; - parseChildren(doc->GetSceneInstance()); - - // update changed presentation Id in all .uip files if in-use - QDomNodeList pNodes = assetsElem.elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < pNodes.count(); ++i) { - QDomElement pElem = pNodes.at(i).toElement(); - QString path = QDir(getProjectPath()) - .absoluteFilePath(pElem.attribute(QStringLiteral("src"))); - PresentationFile::updatePresentationId(path, oldId, theId); - } - Q_EMIT presentationIdChanged(theSrc, theId); - - g_StudioApp.getRenderer().RegisterSubpresentations(g_StudioApp.m_subpresentations); - if (instancesToRefresh.size() > 0) { - g_StudioApp.GetCore()->GetDispatch()->FireImmediateRefreshInstance( - &(instancesToRefresh[0]), long(instancesToRefresh.size())); - for (auto &instance : instancesToRefresh) - doc->getSceneEditor()->saveIfMaterial(instance); - } - } -} - -// Set the doc PresentationId from the project file, this is called after a document is loaded. -// If there is no project file, it simply clears the id. -void ProjectFile::updateDocPresentationId() -{ - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - doc->setPresentationId({}); - - if (!m_fileInfo.exists()) - return; - - QFile file(getProjectFilePath()); - if (!file.open(QFile::Text | QFile::ReadOnly)) { - qWarning() << file.errorString(); - return; - } - - QXmlStreamReader reader(&file); - reader.setNamespaceProcessing(false); - - while (!reader.atEnd()) { - if (reader.readNextStartElement() && reader.name() == QLatin1String("presentation")) { - const auto attrs = reader.attributes(); - if (attrs.value(QLatin1String("src")) == doc->getRelativePath()) { - // current presentation node - doc->setPresentationId(attrs.value(QLatin1String("id")).toString()); - return; - } - } - } -} - -// get a presentationId that match a given src attribute -QString ProjectFile::getPresentationId(const QString &src) const -{ - if (!m_fileInfo.exists()) - return {}; - - if (src == g_StudioApp.GetCore()->GetDoc()->getRelativePath()) { - return g_StudioApp.GetCore()->GetDoc()->getPresentationId(); - } else { - auto *sp = std::find_if(g_StudioApp.m_subpresentations.begin(), - g_StudioApp.m_subpresentations.end(), - [&src](const SubPresentationRecord &spr) -> bool { - return spr.m_argsOrSrc == src; - }); - if (sp != g_StudioApp.m_subpresentations.end()) - return sp->m_id; - } - - return {}; -} - -// create the project .uia file -void ProjectFile::create(const QString &uiaPath) -{ - QDomDocument domDoc; - domDoc.setContent(QStringLiteral("<?xml version=\"1.0\" encoding=\"utf-8\"?>" - "<application xmlns=\"http://qt.io/qt3dstudio/uia\">" - "</application>")); - - QSaveFile file(uiaPath); - if (StudioUtils::openTextSave(file)) { - StudioUtils::commitDomDocumentSave(file, domDoc); - m_fileInfo.setFile(uiaPath); - } -} - -/** - * Clone the project file with a preview suffix and set the initial attribute to the currently - * open document - * - * @return path to the preview project file. Return path to .uip or preview .uip file if there - * is no project file. - */ -QString ProjectFile::createPreview() -{ - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - QString uipPrvPath = doc->GetDocumentPath(); - - // Commit all open transactions - doc->forceCloseTransaction(); - - // create a preview uip if doc modified - if (doc->isModified()) { - uipPrvPath.replace(QLatin1String(".uip"), QLatin1String("_@preview@.uip")); - g_StudioApp.GetCore()->OnSaveDocument(uipPrvPath, true); - } - - // if no project file exist (.uia) just return the preview uip path - if (!m_fileInfo.exists()) - return uipPrvPath; - - // create a preview project file - QString prvPath = getProjectFilePath(); - prvPath.replace(QLatin1String(".uia"), QLatin1String("_@preview@.uia")); - - if (QFile::exists(prvPath)) - QFile::remove(prvPath); - - if (QFile::copy(getProjectFilePath(), prvPath)) { - QDomDocument domDoc; - QSaveFile file(prvPath); - if (StudioUtils::openDomDocumentSave(file, domDoc)) { - QDomElement assetsElem = domDoc.documentElement() - .firstChildElement(QStringLiteral("assets")); - assetsElem.setAttribute(QStringLiteral("initial"), doc->getPresentationId()); - - if (doc->isModified()) { - // Set the preview uip path in the uia file - QDomNodeList pNodes = assetsElem.elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < pNodes.count(); ++i) { - QDomElement pElem = pNodes.at(i).toElement(); - if (pElem.attribute(QStringLiteral("id")) == doc->getPresentationId()) { - QString src = QDir(getProjectPath()).relativeFilePath(uipPrvPath); - pElem.setAttribute(QStringLiteral("src"), src); - break; - } - } - } - StudioUtils::commitDomDocumentSave(file, domDoc); - } - - return prvPath; - } else { - qWarning() << "Couldn't clone project file"; - } - - return {}; -} - -void ProjectFile::parseDataInputElem(const QDomElement &elem, - QMap<QString, CDataInputDialogItem *> &dataInputs) -{ - if (elem.nodeName() == QLatin1String("dataInput")) { - CDataInputDialogItem *item = new CDataInputDialogItem(); - item->name = elem.attribute(QStringLiteral("name")); - QString type = elem.attribute(QStringLiteral("type")); - if (type == QLatin1String("Ranged Number")) { - item->type = EDataType::DataTypeRangedNumber; - item->minValue = elem.attribute(QStringLiteral("min")).toFloat(); - item->maxValue = elem.attribute(QStringLiteral("max")).toFloat(); - } else if (type == QLatin1String("String")) { - item->type = EDataType::DataTypeString; - } else if (type == QLatin1String("Float")) { - item->type = EDataType::DataTypeFloat; - } else if (type == QLatin1String("Boolean")) { - item->type = EDataType::DataTypeBoolean; - } else if (type == QLatin1String("Vector4")) { - item->type = EDataType::DataTypeVector4; - } else if (type == QLatin1String("Vector3")) { - item->type = EDataType::DataTypeVector3; - } else if (type == QLatin1String("Vector2")) { - item->type = EDataType::DataTypeVector2; - } else if (type == QLatin1String("Variant")) { - item->type = EDataType::DataTypeVariant; - } -#ifdef DATAINPUT_EVALUATOR_ENABLED - else if (type == QLatin1String("Evaluator")) { - item->type = EDataType::DataTypeEvaluator; - item->valueString = elem.attribute(QStringLiteral("evaluator")); - } -#endif - - auto metadata = elem.attribute(QStringLiteral("metadata")); - if (!metadata.isEmpty()) { - auto metadataList = metadata.split(QLatin1Char('$')); - - if (metadataList.size() & 1) { - qWarning("Malformed datainput metadata for datainput %s, cannot parse key" - " - value pairs. Stop parsing metadata.", qUtf8Printable(item->name)); - } else { - for (int i = 0; i < metadataList.size(); i += 2) { - if (metadataList[i].isEmpty()) { - qWarning("Malformed datainput metadata for datainput %s - metadata" - " key empty. Stop parsing metadata.", qUtf8Printable(item->name)); - break; - } - item->metadata.insert(metadataList[i], metadataList[i+1]); - } - } - } - dataInputs.insert(item->name, item); - } -} - -void ProjectFile::loadDataInputs(const QString &projFile, - QMap<QString, CDataInputDialogItem *> &dataInputs) -{ - QFileInfo fi(projFile); - if (fi.exists()) { - QDomDocument doc; - if (!StudioUtils::readFileToDomDocument(projFile, doc)) - return; - QDomElement assetsElem = doc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - for (QDomElement p = assetsElem.firstChild().toElement(); !p.isNull(); - p = p.nextSibling().toElement()) { - parseDataInputElem(p, dataInputs); - } - } - } -} - -void ProjectFile::loadSubpresentationsAndDatainputs( - QVector<SubPresentationRecord> &subpresentations, - QMap<QString, CDataInputDialogItem *> &datainputs) -{ - if (!m_fileInfo.exists()) - return; - - subpresentations.clear(); - datainputs.clear(); - - m_initialPresentation = g_StudioApp.GetCore()->GetDoc()->getPresentationId(); - - QDomDocument doc; - if (!StudioUtils::readFileToDomDocument(getProjectFilePath(), doc)) - return; - - QDomElement assetsElem = doc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - QString initial = assetsElem.attribute(QStringLiteral("initial")); - if (!initial.isEmpty()) - m_initialPresentation = initial; - for (QDomElement p = assetsElem.firstChild().toElement(); !p.isNull(); - p = p.nextSibling().toElement()) { - if ((p.nodeName() == QLatin1String("presentation") - || p.nodeName() == QLatin1String("presentation-qml")) - && p.attribute(QStringLiteral("id")) - != g_StudioApp.GetCore()->GetDoc()->getPresentationId()) { - QString argsOrSrc = p.attribute(QStringLiteral("src")); - if (argsOrSrc.isNull()) - argsOrSrc = p.attribute(QStringLiteral("args")); - subpresentations.push_back( - SubPresentationRecord(p.nodeName(), p.attribute("id"), argsOrSrc)); - } else { - parseDataInputElem(p, datainputs); - } - } - } - g_StudioApp.GetCore()->GetDoc()->UpdateDatainputMap(); -} - -/** - * Check that a given presentation's or Qml stream's id is unique - * - * @param id presentation's or Qml stream's Id - * @param src source node to exclude from the check. Defaults to empty. - */ -bool ProjectFile::isUniquePresentationId(const QString &id, const QString &src) const -{ - if (!m_fileInfo.exists()) - return true; - - bool isCurrDoc = src == g_StudioApp.GetCore()->GetDoc()->getRelativePath(); - - if (!isCurrDoc && id == g_StudioApp.GetCore()->GetDoc()->getPresentationId()) - return false; - - auto *sp = std::find_if(g_StudioApp.m_subpresentations.begin(), - g_StudioApp.m_subpresentations.end(), - [&id, &src](const SubPresentationRecord &spr) -> bool { - return spr.m_id == id && spr.m_argsOrSrc != src; - }); - return sp == g_StudioApp.m_subpresentations.end(); -} - -// Returns unique presentation name based on given relative presentation path -// Only the file name base is returned, no path or suffix. -QString ProjectFile::getUniquePresentationName(const QString &presSrc) const -{ - if (!m_fileInfo.exists()) - return {}; - - const QString fullPresSrc = getAbsoluteFilePathTo(presSrc); - QFileInfo fi(fullPresSrc); - const QStringList files = fi.dir().entryList(QDir::Files); - QString checkName = fi.fileName(); - if (files.contains(checkName)) { - const QString nameTemplate = QStringLiteral("%1%2.%3"); - const QString suffix = fi.suffix(); - QString base = fi.completeBaseName(); - int counter = 0; - int checkIndex = base.size(); - while (checkIndex > 1 && base.at(checkIndex - 1).isDigit()) - --checkIndex; - if (checkIndex < base.size()) - counter = base.mid(checkIndex).toInt(); - - if (counter > 0) - base = base.left(checkIndex); - - while (files.contains(checkName)) - checkName = nameTemplate.arg(base).arg(++counter).arg(suffix); - } - - return QFileInfo(checkName).completeBaseName(); -} - -QString ProjectFile::ensureUniquePresentationId(const QString &id) const -{ - if (!m_fileInfo.exists()) - return id; - - QDomDocument doc; - if (!StudioUtils::readFileToDomDocument(m_fileInfo.filePath(), doc)) - return id; - - QString newId = id; - QDomElement assetsElem = doc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - bool unique; - int n = 1; - do { - unique = true; - for (QDomElement p = assetsElem.firstChild().toElement(); !p.isNull(); - p = p.nextSibling().toElement()) { - if ((p.nodeName() == QLatin1String("presentation") - || p.nodeName() == QLatin1String("presentation-qml")) - && p.attribute(QStringLiteral("id")) == newId) { - newId = id + QString::number(n++); - unique = false; - break; - } - } - } while (!unique); - } - - return newId; -} - -// Get the path to the project root. If .uia doesn't exist, return path to current presentation. -QString ProjectFile::getProjectPath() const -{ - if (m_fileInfo.exists()) - return m_fileInfo.path(); - else - return QFileInfo(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath()).absolutePath(); -} - -// Get the path to the project's .uia file. If .uia doesn't exist, return empty string. -QString ProjectFile::getProjectFilePath() const -{ - if (m_fileInfo.exists()) - return m_fileInfo.filePath(); - else - return {}; -} - -// Returns current project name or empty string if there is no .uia file -QString ProjectFile::getProjectName() const -{ - if (m_fileInfo.exists()) - return m_fileInfo.completeBaseName(); - else - return {}; -} - -/** - * Get presentations out of a uia file - * - * @param inUiaPath uia file path - * @param outSubpresentations list of collected presentations - * @param excludePresentationSrc execluded presentation, (commonly the current presentation) - */ -// static -void ProjectFile::getPresentations(const QString &inUiaPath, - QVector<SubPresentationRecord> &outSubpresentations, - const QString &excludePresentationSrc) -{ - QFile file(inUiaPath); - if (!file.open(QFile::Text | QFile::ReadOnly)) { - qWarning() << file.errorString(); - return; - } - - QXmlStreamReader reader(&file); - reader.setNamespaceProcessing(false); - - while (!reader.atEnd()) { - if (reader.readNextStartElement() - && (reader.name() == QLatin1String("presentation") - || reader.name() == QLatin1String("presentation-qml"))) { - const auto attrs = reader.attributes(); - QString argsOrSrc = attrs.value(QLatin1String("src")).toString(); - if (excludePresentationSrc == argsOrSrc) - continue; - if (argsOrSrc.isNull()) - argsOrSrc = attrs.value(QLatin1String("args")).toString(); - - outSubpresentations.push_back( - SubPresentationRecord(reader.name().toString(), - attrs.value(QLatin1String("id")).toString(), - argsOrSrc)); - } else if (reader.name() == QLatin1String("assets") && !reader.isStartElement()) { - break; // reached end of <assets> - } - } -} - -void ProjectFile::setInitialPresentation(const QString &initialId) -{ - if (!initialId.isEmpty() && m_initialPresentation != initialId) { - m_initialPresentation = initialId; - - ensureProjectFile(); - - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - QDomElement assetsElem - = domDoc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull() && assetsElem.attribute(QStringLiteral("initial")) - != m_initialPresentation) { - assetsElem.setAttribute(QStringLiteral("initial"), m_initialPresentation); - - StudioUtils::commitDomDocumentSave(file, domDoc); - } - } -} - -// Returns true if file rename was successful. The parameters are relative to project root. -bool ProjectFile::renamePresentationFile(const QString &oldName, const QString &newName) -{ - const QString fullOldPath = getAbsoluteFilePathTo(oldName); - const QString fullNewPath = getAbsoluteFilePathTo(newName); - QFile presFile(fullOldPath); - const bool success = presFile.rename(fullNewPath); - - if (success) { - // Update assets in .uia - ensureProjectFile(); - - const bool isQml = oldName.endsWith(QLatin1String(".qml")); - - if (isQml && g_StudioApp.m_qmlStreamMap.contains(fullOldPath)) { - // Update Qml stream type cache - g_StudioApp.m_qmlStreamMap.remove(fullOldPath); - g_StudioApp.m_qmlStreamMap.insert(fullNewPath, true); - } - - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return false; - - QDomElement assetsElem - = domDoc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - QDomNodeList pqNodes - = isQml ? assetsElem.elementsByTagName(QStringLiteral("presentation-qml")) - : assetsElem.elementsByTagName(QStringLiteral("presentation")); - if (!pqNodes.isEmpty()) { - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - for (int i = 0; i < pqNodes.count(); ++i) { - QDomElement pqElem = pqNodes.at(i).toElement(); - const QString attTag = isQml ? QStringLiteral("args") : QStringLiteral("src"); - const QString srcOrArgs = pqElem.attribute(attTag); - if (srcOrArgs == oldName) { - pqElem.setAttribute(attTag, newName); - - if (pqElem.attribute(QStringLiteral("id")) != doc->getPresentationId()) { - // update m_subpresentations - auto *sp = std::find_if( - g_StudioApp.m_subpresentations.begin(), - g_StudioApp.m_subpresentations.end(), - [&oldName](const SubPresentationRecord &spr) -> bool { - return spr.m_argsOrSrc == oldName; - }); - if (sp != g_StudioApp.m_subpresentations.end()) - sp->m_argsOrSrc = newName; - } else { - // If renaming current presentation, need to update the doc path, too - doc->SetDocumentPath(fullNewPath); - } - - StudioUtils::commitDomDocumentSave(file, domDoc); - - Q_EMIT assetNameChanged(); - break; - } - } - } - } - } - - return success; -} - -/** - * Delete a presentation (or qml-stream) file and remove references to it from the project file. - * This function assumes the removed presentation is not referenced by any presentation - * in the project and is not the current presentation. - * - * @param filePath Absolute file path to presentation to delete - */ -void ProjectFile::deletePresentationFile(const QString &filePath) -{ - QFile(filePath).remove(); - - if (m_fileInfo.exists()) { - const QString relPath = getRelativeFilePathTo(filePath); - const bool isQml = relPath.endsWith(QLatin1String(".qml")); - - // Update records and caches - if (isQml && g_StudioApp.m_qmlStreamMap.contains(filePath)) - g_StudioApp.m_qmlStreamMap.remove(filePath); - for (int i = 0, count = g_StudioApp.m_subpresentations.size(); i < count; ++i) { - SubPresentationRecord &rec = g_StudioApp.m_subpresentations[i]; - if (rec.m_argsOrSrc == relPath) { - g_StudioApp.m_subpresentations.remove(i); - break; - } - } - - // Update project file - QDomDocument domDoc; - QSaveFile projectFile(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(projectFile, domDoc)) - return; - - QDomElement assetsElem - = domDoc.documentElement().firstChildElement(QStringLiteral("assets")); - if (!assetsElem.isNull()) { - QDomNodeList pqNodes - = isQml ? assetsElem.elementsByTagName(QStringLiteral("presentation-qml")) - : assetsElem.elementsByTagName(QStringLiteral("presentation")); - if (!pqNodes.isEmpty()) { - for (int i = 0; i < pqNodes.count(); ++i) { - QDomElement pqElem = pqNodes.at(i).toElement(); - const QString attTag = isQml ? QStringLiteral("args") : QStringLiteral("src"); - const QString srcOrArgs = pqElem.attribute(attTag); - if (srcOrArgs == relPath) { - const QString id = pqElem.attribute(QStringLiteral("id")); - // If initial presentation is deleted, change current to initial - if (assetsElem.attribute(QStringLiteral("initial")) == id) { - m_initialPresentation - = g_StudioApp.GetCore()->GetDoc()->getPresentationId(); - assetsElem.setAttribute(QStringLiteral("initial"), - m_initialPresentation); - } - assetsElem.removeChild(pqNodes.at(i)); - StudioUtils::commitDomDocumentSave(projectFile, domDoc); - break; - } - } - } - } - // Update registrations asynchronously, as it messes with event processing, which can - // cause issues with file models elsewhere in the editor unless file removal is fully - // handled. - QTimer::singleShot(0, []() { - g_StudioApp.getRenderer().RegisterSubpresentations(g_StudioApp.m_subpresentations); - }); - } -} - -void ProjectFile::renameMaterial(const QString &oldName, const QString &newName) -{ - for (auto &pres : qAsConst(g_StudioApp.m_subpresentations)) { - if (pres.m_type == QLatin1String("presentation")) { - PresentationFile::renameMaterial(getAbsoluteFilePathTo(pres.m_argsOrSrc), - oldName, newName); - } - } - Q_EMIT assetNameChanged(); -} - -// Copies oldPres presentation as newPres. The id for newPres will be autogenerated. -bool ProjectFile::duplicatePresentation(const QString &oldPres, const QString &newPres) -{ - const QString fullOldPath = getAbsoluteFilePathTo(oldPres); - const QString fullNewPath = getAbsoluteFilePathTo(newPres); - const bool success = QFile::copy(fullOldPath, fullNewPath); - - if (success) - addPresentationNode(fullNewPath, {}); - - return success; -} - -/** - * Returns an absolute file path for a given relative file path. - * - * @param relFilePath A file path relative to project root to convert. - */ -QString ProjectFile::getAbsoluteFilePathTo(const QString &relFilePath) const -{ - auto projectPath = QDir(getProjectPath()).absoluteFilePath(relFilePath); - return QDir::cleanPath(projectPath); -} - -/** - * Returns a file path relative to the project root for given absolute file path. - * - * @param absFilePath An absolute file path to convert. - */ -QString ProjectFile::getRelativeFilePathTo(const QString &absFilePath) const -{ - return QDir(getProjectPath()).relativeFilePath(absFilePath); -} - -// Return multimap of type subpresentationid - QPair<datainput, propertyname> -QMultiMap<QString, QPair<QString, QString>> -ProjectFile::getDiBindingtypesFromSubpresentations() const -{ - QMultiMap<QString, QPair<QString, QString>> map; - for (auto sp : qAsConst(g_StudioApp.m_subpresentations)) - PresentationFile::getDataInputBindings(sp, map); - - return map; -} - -/** - * Load variants data to m_variantsDef - * - * @param filePath the file path to load the variants from. If empty, variants are loaded from the - * project file and replace m_variantsDef. If a filePath is specified, the loaded - * variants are merged with m_variantsDef. - */ -void ProjectFile::loadVariants(const QString &filePath) -{ - if (!m_fileInfo.exists()) - return; - - bool isProj = filePath.isEmpty() || filePath == getProjectFilePath(); - QFile file(isProj ? getProjectFilePath() : filePath); - if (!file.open(QFile::Text | QFile::ReadOnly)) { - qWarning() << file.errorString(); - return; - } - - if (isProj) { - m_variantsDef.clear(); - m_variantsDefKeys.clear(); - } - - QXmlStreamReader reader(&file); - reader.setNamespaceProcessing(false); - - VariantGroup *currentGroup = nullptr; - while (!reader.atEnd()) { - if (reader.readNextStartElement()) { - if (reader.name() == QLatin1String("variantgroup")) { - QString groupId = reader.attributes().value(QLatin1String("id")).toString(); - currentGroup = &m_variantsDef[groupId]; - if (!m_variantsDefKeys.contains(groupId)) { - // Only update colors for new groups - currentGroup->m_color = reader.attributes().value( - QLatin1String("color")).toString(); - m_variantsDefKeys.append(groupId); - } - } else if (reader.name() == QLatin1String("variant")) { - if (currentGroup) { - QString tagId = reader.attributes().value(QLatin1String("id")).toString(); - if (!currentGroup->m_tags.contains(tagId)) - currentGroup->m_tags.append(tagId); - } else { - qWarning() << "Error parsing variant tags."; - } - } else if (currentGroup) { - break; - } - } - } - - if (!isProj) { - // if loading variants from a file, update the uia - QDomDocument domDoc; - QSaveFile fileProj(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(fileProj, domDoc)) - return; - - QDomElement vElem = domDoc.documentElement().firstChildElement(QStringLiteral("variants")); - if (!vElem.isNull()) - domDoc.documentElement().removeChild(vElem); - - vElem = domDoc.createElement(QStringLiteral("variants")); - domDoc.documentElement().appendChild(vElem); - - for (auto &g : qAsConst(m_variantsDefKeys)) { - QDomElement gElem = domDoc.createElement(QStringLiteral("variantgroup")); - gElem.setAttribute(QStringLiteral("id"), g); - gElem.setAttribute(QStringLiteral("color"), m_variantsDef[g].m_color); - vElem.appendChild(gElem); - - for (auto &t : qAsConst(m_variantsDef[g].m_tags)) { - QDomElement tElem = domDoc.createElement(QStringLiteral("variant")); - tElem.setAttribute(QStringLiteral("id"), t); - gElem.appendChild(tElem); - } - } - - StudioUtils::commitDomDocumentSave(fileProj, domDoc); - } - - g_StudioApp.m_pMainWnd->updateActionFilterEnableState(); -} - -// Add a new tag to a variants group -void ProjectFile::addVariantTag(const QString &group, const QString &newTag) -{ - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - QDomElement newTagElem = domDoc.createElement(QStringLiteral("variant")); - newTagElem.setAttribute(QStringLiteral("id"), newTag); - - QDomNodeList groupsElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")) - .elementsByTagName(QStringLiteral("variantgroup")); - - // update and save the uia - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == group) { - gElem.appendChild(newTagElem); - StudioUtils::commitDomDocumentSave(file, domDoc); - break; - } - } - - // update m_variantsDef - m_variantsDef[group].m_tags.append(newTag); -} - -// Add a new group, it is assumes that the new group name is unique -void ProjectFile::addVariantGroup(const QString &newGroup) -{ - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - QDomElement variantsElem = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")); - - if (variantsElem.isNull()) { - QDomElement newVariantsElem = domDoc.createElement(QStringLiteral("variants")); - domDoc.documentElement().appendChild(newVariantsElem); - variantsElem = newVariantsElem; - } - - // a set of predefined variant colors to assign to newly created groups - static const QStringList VARIANT_COLORS { - QStringLiteral("#06c4f4"), QStringLiteral("#f7752a"), - QStringLiteral("#d6c605"), QStringLiteral("#ff00ff"), - QStringLiteral("#725de8"), QStringLiteral("#8cc63f"), - QStringLiteral("#0071bc"), QStringLiteral("#ed1e79"), - QStringLiteral("#f9b406"), QStringLiteral("#74c905"), - QStringLiteral("#93278f"), QStringLiteral("#d9e021"), - QStringLiteral("#00a99d"), QStringLiteral("#c1272d"), - QStringLiteral("#f7931e"), QStringLiteral("#f45d85"), - QStringLiteral("#682e7a"), QStringLiteral("#05e2d6"), - QStringLiteral("#0000ff"), QStringLiteral("#ff0000") - }; - - if (m_variantColorsIter == -1) { // initialize m_variantColorsIter - m_variantColorsIter = 0; - if (!m_variantsDefKeys.isEmpty()) { - QString lastGroup = m_variantsDefKeys[m_variantsDefKeys.size() - 1]; - QString lastGroupColor = m_variantsDef[lastGroup].m_color; - for (int i = VARIANT_COLORS.length() - 1; i >= 0; --i) { - if (VARIANT_COLORS[i] == lastGroupColor) { - m_variantColorsIter = i + 1; - break; - } - } - } - } - - QString newColor = VARIANT_COLORS[m_variantColorsIter++ % VARIANT_COLORS.size()]; - QDomElement newGroupElem = domDoc.createElement(QStringLiteral("variantgroup")); - newGroupElem.setAttribute(QStringLiteral("id"), newGroup); - newGroupElem.setAttribute(QStringLiteral("color"), newColor); - variantsElem.appendChild(newGroupElem); - StudioUtils::commitDomDocumentSave(file, domDoc); - - // update m_variantsDef - VariantGroup g; - g.m_color = newColor; - m_variantsDef[newGroup] = g; - m_variantsDefKeys.append(newGroup); -} - -void ProjectFile::renameVariantTag(const QString &group, const QString &oldTag, - const QString &newTag) -{ - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - // rename the tag in all uip files - QDomNodeList presElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("assets")) - .elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - updateVariantsInUip(pPath, VariantsUpdateMode::Rename, group, oldTag, newTag); - } - - // update and save the uia - QDomNodeList groupsElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")) - .elementsByTagName(QStringLiteral("variantgroup")); - - bool renamed = false; - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == group) { - QDomNodeList tagsElems = gElem.childNodes(); - for (int j = 0; j < tagsElems.count(); ++j) { - QDomElement tElem = tagsElems.at(j).toElement(); - if (tElem.attribute(QStringLiteral("id")) == oldTag) { - tElem.setAttribute(QStringLiteral("id"), newTag); - StudioUtils::commitDomDocumentSave(file, domDoc); - renamed = true; - break; - } - } - if (renamed) - break; - } - } - - // update the property - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); - const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); - const QVector<int> instances = doc->getVariantInstances(); - for (auto instance : instances) { - auto property = bridge->getVariantsProperty(instance); - qt3dsdm::SValue sValue; - if (propertySystem->GetInstancePropertyValue(instance, property, sValue)) { - QString propVal = qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->toQString(); - QString oldGroupTagPair = QStringLiteral("%1:%2").arg(group).arg(oldTag); - if (propVal.contains(oldGroupTagPair)) { - propVal.replace(oldGroupTagPair, QStringLiteral("%1:%2").arg(group).arg(newTag)); - propertySystem->SetInstancePropertyValue(instance, property, QVariant(propVal)); - } - } - } - - // update m_variantsDef - for (auto &t : m_variantsDef[group].m_tags) { - if (t == oldTag) { - t = newTag; - renamed = true; - break; - } - } -} - -// rename a variant group, newGroup is assumed to be unique -void ProjectFile::renameVariantGroup(const QString &oldGroup, const QString &newGroup) -{ - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - // rename the group in all uip files - QDomNodeList presElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("assets")) - .elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - updateVariantsInUip(pPath, VariantsUpdateMode::Rename, oldGroup, {}, newGroup); - } - - // update and save the uia - QDomNodeList groupsElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")) - .elementsByTagName(QStringLiteral("variantgroup")); - - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == oldGroup) { - gElem.setAttribute(QStringLiteral("id"), newGroup); - StudioUtils::commitDomDocumentSave(file, domDoc); - break; - } - } - - // update the property - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); - const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); - const QVector<int> instances = doc->getVariantInstances(); - for (auto instance : instances) { - auto property = bridge->getVariantsProperty(instance); - qt3dsdm::SValue sValue; - if (propertySystem->GetInstancePropertyValue(instance, property, sValue)) { - QString propVal = qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->toQString(); - QString oldGroupWithColon = QStringLiteral("%1:").arg(oldGroup); - if (propVal.contains(oldGroupWithColon)) { - propVal.replace(oldGroupWithColon, QStringLiteral("%1:").arg(newGroup)); - propertySystem->SetInstancePropertyValue(instance, property, QVariant(propVal)); - } - } - } - - // update m_variantsDef - m_variantsDef[newGroup] = m_variantsDef[oldGroup]; - m_variantsDef.remove(oldGroup); - for (auto &g : m_variantsDefKeys) { - if (g == oldGroup) { - g = newGroup; - break; - } - } -} - -void ProjectFile::deleteVariantGroup(const QString &group) -{ - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - // check if group is in use in other presentations in the porject - int inUseIdx = -1; // index of first presentation that has the group in-use - QDomNodeList presElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("assets")) - .elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - if (pPath != doc->GetDocumentPath() && tagExistsInUip(pPath, group)) { - inUseIdx = i; - break; - } - } - - if (inUseIdx != -1) { - QMessageBox box; - box.setWindowTitle(tr("Group tags in use")); - box.setText(tr("Some tags in the Group '%1' are in use in the project, are you sure you" - " want to delete the group?").arg(group)); - box.setIcon(QMessageBox::Warning); - box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - box.setButtonText(QMessageBox::Yes, tr("Delete")); - switch (box.exec()) { - case QMessageBox::Yes: - // delete the group from all uips that use it - for (int i = inUseIdx; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - if (pPath != doc->GetDocumentPath()) - updateVariantsInUip(pPath, VariantsUpdateMode::Delete, group); - } - break; - - default: - // abort deletion - return; - } - } - - // delete the group from current uip, if exists - updateVariantsInUip(doc->GetDocumentPath(), VariantsUpdateMode::Delete, group); - - // delete the group from the property (if set) - const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); - const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); - const QVector<int> instances = doc->getVariantInstances(); - for (auto instance : instances) { - auto property = bridge->getVariantsProperty(instance); - qt3dsdm::SValue sValue; - if (propertySystem->GetInstancePropertyValue(instance, property, sValue)) { - QString propVal = qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->toQString(); - if (propVal.contains(QStringLiteral("%1:").arg(group))) { - // property has the deleted group, need to update it, else the deleted group - // will be saved the uip if the user saves the presentation. - QRegExp rgx(QStringLiteral("%1:\\w*,*|,%1:\\w*").arg(group)); - propVal.replace(rgx, {}); - propertySystem->SetInstancePropertyValue(instance, property, QVariant(propVal)); - } - } - } - - // update and save the uia - QDomElement variantsElem = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")); - QDomNodeList groupsElems = variantsElem.elementsByTagName(QStringLiteral("variantgroup")); - - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == group) { - variantsElem.removeChild(gElem); - StudioUtils::commitDomDocumentSave(file, domDoc); - break; - } - } - - // update m_variantsDef - m_variantsDef.remove(group); - m_variantsDefKeys.removeOne(group); -} - -void ProjectFile::changeVariantGroupColor(const QString &group, const QString &newColor) -{ - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - // update and save the uia - QDomNodeList groupsElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")) - .elementsByTagName(QStringLiteral("variantgroup")); - - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == group) { - gElem.setAttribute(QStringLiteral("color"), newColor); - StudioUtils::commitDomDocumentSave(file, domDoc); - break; - } - } - - // update m_variantsDef - m_variantsDef[group].m_color = newColor; -} - -void ProjectFile::deleteVariantTag(const QString &group, const QString &tag) -{ - CDoc *doc = g_StudioApp.GetCore()->GetDoc(); - QDomDocument domDoc; - QSaveFile file(getProjectFilePath()); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - // check if tag is in use in other presentations in the porject - int inUseIdx = -1; // list of presentations that has the tag in use - QDomNodeList presElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("assets")) - .elementsByTagName(QStringLiteral("presentation")); - for (int i = 0; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - if (pPath != doc->GetDocumentPath() - && tagExistsInUip(pPath, group, tag)) { - inUseIdx = i; - break; - } - } - - if (inUseIdx != -1) { - QMessageBox box; - box.setWindowTitle(tr("Tag in use")); - box.setText(tr("The tag '%1' is in use in another presentation, are you sure you want to" - " delete it?").arg(tag)); - box.setIcon(QMessageBox::Warning); - box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - box.setButtonText(QMessageBox::Yes, tr("Delete")); - switch (box.exec()) { - case QMessageBox::Yes: - // delete the tag from all uips that use it - for (int i = inUseIdx; i < presElems.count(); ++i) { - QString pPath = m_fileInfo.path() + QLatin1Char('/') - + presElems.at(i).toElement().attribute(QStringLiteral("src")); - if (pPath != doc->GetDocumentPath()) - updateVariantsInUip(pPath, VariantsUpdateMode::Delete, group, tag); - } - break; - - default: - // abort deletion - return; - } - } - - // delete the tag from current doc, if exists - updateVariantsInUip(doc->GetDocumentPath(), VariantsUpdateMode::Delete, group, tag); - - QDomNodeList groupsElems = domDoc.documentElement() - .firstChildElement(QStringLiteral("variants")) - .elementsByTagName(QStringLiteral("variantgroup")); - - // delete the tag from the property (if set) - const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); - const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); - const QVector<int> instances = doc->getVariantInstances(); - for (auto instance : instances) { - auto property = bridge->getVariantsProperty(instance); - qt3dsdm::SValue sValue; - if (propertySystem->GetInstancePropertyValue(instance, property, sValue)) { - QString propVal = qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->toQString(); - if (propVal.contains(QStringLiteral("%1:%2").arg(group).arg(tag))) { - // property has the deleted tag, need to update it, else the deleted tag will be - // saved in the uip if the user saves the presentation. - QRegExp rgx(QStringLiteral("%1:%2,*|,%1:%2").arg(group).arg(tag)); - propVal.replace(rgx, {}); - propertySystem->SetInstancePropertyValue(instance, property, QVariant(propVal)); - } - } - } - - // update and save the uia - bool deleted = false; - for (int i = 0; i < groupsElems.count(); ++i) { - QDomElement gElem = groupsElems.at(i).toElement(); - if (gElem.attribute(QStringLiteral("id")) == group) { - QDomNodeList tagsElems = gElem.childNodes(); - for (int j = 0; j < tagsElems.count(); ++j) { - QDomElement tElem = tagsElems.at(j).toElement(); - if (tElem.attribute(QStringLiteral("id")) == tag) { - gElem.removeChild(tElem); - StudioUtils::commitDomDocumentSave(file, domDoc); - deleted = true; - break; - } - } - if (deleted) - break; - } - } - - // update m_variantsDef - m_variantsDef[group].m_tags.removeOne(tag); -} - -// rename or delete a tag or group in a uip file -void ProjectFile::updateVariantsInUip(const QString &src, const VariantsUpdateMode &updateType, - const QString &group, const QString &tag, - const QString &newName) -{ - QDomDocument domDoc; - QSaveFile file(src); - if (!StudioUtils::openDomDocumentSave(file, domDoc)) - return; - - QDomElement graphElem = domDoc.documentElement().firstChild() - .firstChildElement(QStringLiteral("Graph")); - QDomElement sceneElem = graphElem.firstChildElement(QStringLiteral("Scene")); - - bool needsSave = false; - QString sceneStr; - QTextStream stream(&sceneStr); - sceneElem.save(stream, 4); - - // if tag isEmpty() update group, else update tag - - if (updateType == VariantsUpdateMode::Rename) { // rename a tag or a group - QString oldPair = group + QLatin1Char(':') + tag; - QString newPair = QStringLiteral("%1:%2").arg(tag.isEmpty() ? newName : group) - .arg(tag.isEmpty() ? tag : newName); - if (sceneStr.contains(oldPair)) { - sceneStr.replace(oldPair, newPair); - needsSave = true; - } - } else if (updateType == VariantsUpdateMode::Delete) { // delete a tag or a group - QRegExp rgx(tag.isEmpty() ? QStringLiteral("%1:\\w*,*|,%1:\\w*").arg(group) - : QStringLiteral("%1:%2,*|,%1:%2").arg(group).arg(tag)); - if (rgx.indexIn(sceneStr) != -1) { - sceneStr.replace(rgx, ""); - needsSave = true; - } - } - - if (needsSave) { - QDomDocument sceneDom; - sceneDom.setContent(sceneStr); - graphElem.replaceChild(sceneDom, sceneElem); - StudioUtils::commitDomDocumentSave(file, domDoc); - } -} - -// if tag param is empty, the method checks if a group exists -bool ProjectFile::tagExistsInUip(const QString &src, const QString &group, const QString &tag) const -{ - QFile file(src); - if (!file.open(QFile::Text | QFile::ReadOnly)) { - qWarning() << file.errorString(); - return false; - } - - QXmlStreamReader reader(&file); - reader.setNamespaceProcessing(false); - - while (!reader.atEnd()) { - if (reader.readNextStartElement()) { - if (reader.attributes().hasAttribute(QLatin1String("variants"))) { - QStringRef v = reader.attributes().value(QLatin1String("variants")); - if (v.contains(group + QLatin1Char(':') + tag)) - return true; - } else if (reader.name() == QLatin1String("Logic")) { - break; - } - } - } - - return false; -} - -bool ProjectFile::isVariantGroupUnique(const QString &group) const -{ - return !m_variantsDef.contains(group); -} - -bool ProjectFile::isVariantTagUnique(const QString &group, const QString &tag) const -{ - if (!m_variantsDef.contains(group)) - return true; - - return !m_variantsDef[group].m_tags.contains(tag); -} |