diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Application/PresentationFile.cpp')
-rw-r--r-- | src/Authoring/Qt3DStudio/Application/PresentationFile.cpp | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Application/PresentationFile.cpp b/src/Authoring/Qt3DStudio/Application/PresentationFile.cpp new file mode 100644 index 00000000..cbc7f417 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Application/PresentationFile.cpp @@ -0,0 +1,646 @@ +/**************************************************************************** +** +** 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 "PresentationFile.h" +#include "ProjectFile.h" +#include "Dialogs.h" +#include "StudioApp.h" +#include "Core.h" +#include "Doc.h" +#include "StudioUtils.h" +#include "IDocumentReader.h" +#include "IDocumentEditor.h" +#include "Qt3DSDMStudioSystem.h" +#include "ClientDataModelBridge.h" +#include <QtCore/qfile.h> +#include <QtCore/qsavefile.h> +#include <QtXml/qdom.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdiriterator.h> +#include <QtCore/qxmlstream.h> +#include <QtCore/qlist.h> + +// This class provides utility static methods for working with presentation files (.uip). Old uip +// functionality should be gradually moved here whenever feasible. + +// static +QSize PresentationFile::readSize(const QString &uipPath) +{ + QFile file(uipPath); + file.open(QFile::Text | QFile::ReadOnly); + if (!file.isOpen()) { + qWarning() << file.errorString(); + return QSize(); + } + + QXmlStreamReader reader(&file); + reader.setNamespaceProcessing(false); + + while (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("ProjectSettings")) { + const auto attrs = reader.attributes(); + return QSize(attrs.value(QLatin1String("presentationWidth")).toInt(), + attrs.value(QLatin1String("presentationHeight")).toInt()); + } + } + + return QSize(); +} + +/** + * Find all occurrences of a presentation Id in a .uip file and replace them with a new value + * + * @param uipPath presentation file path + * @param oldId the presentation Id to find + * @param newId the presentation Id to replace + */ +// static +void PresentationFile::updatePresentationId(const QString &uipPath, const QString &oldId, + const QString &newId) +{ + QDomDocument domDoc; + QSaveFile file(uipPath); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomElement rootElem = domDoc.documentElement(); + QDomNodeList addNodes = rootElem.elementsByTagName(QStringLiteral("Add")); + QDomNodeList setNodes = rootElem.elementsByTagName(QStringLiteral("Set")); + bool updated = false; + + const auto updateNodes = [&](const QDomNodeList &nodes) { + if (!nodes.isEmpty()) { + for (int i = 0; i < nodes.length(); ++i) { + QDomElement elem = nodes.at(i).toElement(); + if (elem.attribute(QStringLiteral("sourcepath")) == oldId) { + elem.setAttribute(QStringLiteral("sourcepath"), newId); + updated = true; + } + if (elem.attribute(QStringLiteral("subpresentation")) == oldId) { + elem.setAttribute(QStringLiteral("subpresentation"), newId); + updated = true; + } + } + } + }; + + updateNodes(addNodes); + updateNodes(setNodes); + + if (updated) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +/** + * Find all occurrences of a material name in a .uip file and replace them with a new value + * + * @param uipPath presentation file path + * @param oldName the material name to find + * @param newName the material name to replace + */ +// static +void PresentationFile::renameMaterial(const QString &uipPath, const QString &oldName, + const QString &newName) +{ + const auto doc = g_StudioApp.GetCore()->GetDoc(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + const auto sceneEditor = doc->getSceneEditor(); + + const auto absOldPath = sceneEditor->getFilePathFromMaterialName(oldName); + const auto absNewPath = sceneEditor->getFilePathFromMaterialName(newName); + + const auto dir = QFileInfo(uipPath).dir(); + const auto relOldPath = dir.relativeFilePath(absOldPath); + const auto relNewPath = dir.relativeFilePath(absNewPath); + + auto refNewName = newName; + int slashIndex = newName.lastIndexOf(QLatin1Char('/')); + if (slashIndex != -1) + refNewName = newName.mid(slashIndex + 1); + + QDomDocument domDoc; + QSaveFile file(uipPath); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomElement rootElem = domDoc.documentElement(); + QDomNodeList addNodes = rootElem.elementsByTagName(QStringLiteral("Add")); + QDomNodeList setNodes = rootElem.elementsByTagName(QStringLiteral("Set")); + + bool updated = false; + + QDomNodeList materialNodes = rootElem.elementsByTagName(QStringLiteral("Material")); + + // Find the material container + QDomElement materialContainer; + for (int i = 0; i < materialNodes.length(); ++i) { + QDomElement elem = materialNodes.at(i).toElement(); + if (elem.attribute(QStringLiteral("id")) == bridge->getMaterialContainerName()) { + materialContainer = elem; + break; + } + } + + if (materialContainer.isNull()) + return; + + QDomNodeList containerNodes = materialContainer.childNodes(); + + // Store the material ids for further use and change the names and ids to new ones + QStringList materialIds; + for (int i = 0; i < containerNodes.length(); ++i) { + QDomElement elem = containerNodes.at(i).toElement(); + materialIds.append(elem.attribute(QStringLiteral("id"))); + if (elem.attribute(QStringLiteral("id")) == oldName) { + elem.setAttribute(QStringLiteral("id"), newName); + updated = true; + } + } + + // Since rename can change the visible name only in the original presentation + // and retain the old id, we have to cross-reference the ids here to the ids + // logged previously. If a material has the same visible name as the old name, + // the id and the new name are stored + QVector<QPair<QString, QString>> materialRenames; + + const auto renameReferencedMaterial = [&](QDomElement &elem) { + QString ref = elem.attribute(QStringLiteral("ref")); + if (elem.attribute(QStringLiteral("name")) == oldName + && !ref.isEmpty() && materialIds.contains(ref.mid(1))) { + materialRenames.append(QPair<QString, QString>(ref.mid(1), newName)); + elem.setAttribute(QStringLiteral("ref"), QLatin1Char('#') + newName); + elem.setAttribute(QStringLiteral("name"), newName); + updated = true; + } + if (elem.attribute(QStringLiteral("sourcepath")) == relOldPath) { + elem.setAttribute(QStringLiteral("sourcepath"), relNewPath); + if (elem.hasAttribute(QStringLiteral("referencedmaterial"))) { + elem.setAttribute(QStringLiteral("referencedmaterial"), + QLatin1Char('#') + newName); + elem.setAttribute(QStringLiteral("name"), refNewName); + } + updated = true; + } + }; + + for (int i = 0; i < addNodes.length(); ++i) { + QDomElement elem = addNodes.at(i).toElement(); + renameReferencedMaterial(elem); + } + + for (int i = 0; i < setNodes.length(); ++i) { + QDomElement elem = setNodes.at(i).toElement(); + renameReferencedMaterial(elem); + } + + // New pass is needed to change the ids stored when changing the Add and Set Nodes + for (int i = 0; i < containerNodes.length(); ++i) { + for (auto &materialRename : qAsConst(materialRenames)) { + QDomElement elem = containerNodes.at(i).toElement(); + if (elem.attribute(QStringLiteral("id")) == materialRename.first) { + elem.setAttribute(QStringLiteral("id"), materialRename.second); + updated = true; + } + } + } + + if (updated) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +/** + * Find the project file path matching the given presentation file path + * + * @param uipPath presentation file path + * @return project file absolute path + */ +// static +QString PresentationFile::findProjectFile(const QString &uipPath) +{ + QFileInfo fi(uipPath); + + // first check if there is a uia in the same folder as the uip with the same name + QString uiaPath = fi.dir().absoluteFilePath(fi.completeBaseName() + QStringLiteral(".uia")); + if (QFile::exists(uiaPath)) + return uiaPath; + + // next search for a uia starting from uip folder and going up, the first found 1 is assumed + // to be the project file + QDir currDir = fi.dir(); + const int MAX_SEARCH_DEPTH = 3; // scan up to 3 levels up (for performance reasons) + int searchDepth = 0; + do { + QDirIterator di(currDir.path(), QDir::NoDotAndDotDot | QDir::Files); + while (di.hasNext()) { + QFileInfo fi2 = di.next(); + if (fi2.suffix() == QLatin1String("uia")) + return fi2.filePath(); + } + ++searchDepth; + } while (searchDepth < MAX_SEARCH_DEPTH && currDir.cdUp()); + + return {}; +} + +// Get all available child assets source paths (materials, images, effects, etc). +// The source paths returned are relative to the presentation file being parsed. +// static +void PresentationFile::getSourcePaths(const QFileInfo &uipSrc, const QFileInfo &uipTarget, + QHash<QString, QString> &outPathMap, + QString &outProjPathSrc, + QHash<QString, QString> &outPresentationNodes, + QSet<QString> &outDataInputs, + QSet<QString> &outDataOutputs) +{ + QDomDocument domDoc; + if (!StudioUtils::readFileToDomDocument(uipTarget.filePath(), domDoc)) + return; + + QVector<SubPresentationRecord> subpresentations; + QString uiaPath = findProjectFile(uipSrc.filePath()); + if (!uiaPath.isEmpty()) { + outProjPathSrc = QFileInfo(uiaPath).path(); + QString uipPathRelative = QFileInfo(uiaPath).dir().relativeFilePath(uipSrc.filePath()); + ProjectFile::getPresentations(uiaPath, subpresentations, uipPathRelative); + } else { + outProjPathSrc = uipSrc.path(); + } + QDir srcProjDir(outProjPathSrc); + QDir srcUipDir(uipSrc.path()); + + const auto convertPath = [&](const QString &path, bool forceProj = false) -> QString { + if (forceProj || path.startsWith(QLatin1String("./"))) + return srcUipDir.relativeFilePath(srcProjDir.absoluteFilePath(path)); + else + return path; // Assuming path is already presentation relative + }; + + // Map to cache effect and material properties during the presentation file parsing, + // as we don't yet know which ones are actually assets. + // Key: Material/effect class id + // Value: Set of material/effect properties + QHash<QString, QSet<QString>> matEffPropertyMap; + + // search <Classes> + QDomElement classesElem = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Classes")); + for (QDomElement p = classesElem.firstChild().toElement(); !p.isNull(); + p = p.nextSibling().toElement()) { + const QString sourcepath = convertPath(p.attribute(QStringLiteral("sourcepath"))); + if (!sourcepath.isEmpty() && !outPathMap.contains(sourcepath)) { + outPathMap.insert(sourcepath, {}); + + QFileInfo fi(sourcepath); + QByteArray ext = fi.suffix().toLatin1(); + + // if material or effect, find and add their dependents + if (CDialogs::IsMaterialFileExtension(ext.data()) || + CDialogs::IsEffectFileExtension(ext.data())) { + QSet<QString> propertySet; + QHash<QString, QString> matEffPathMap; + g_StudioApp.GetCore()->GetDoc()->GetDocumentReader() + .ParseSourcePathsOutOfEffectFile( + uipSrc.path() + QStringLiteral("/") + sourcepath, + outProjPathSrc, true, matEffPathMap, propertySet); + // ParseSourcePathsOutOfEffectFile returns paths relative to project + QHashIterator<QString, QString> pathIter(matEffPathMap); + while (pathIter.hasNext()) { + pathIter.next(); + outPathMap.insert(convertPath(pathIter.key(), true), pathIter.value()); + } + matEffPropertyMap.insert(QStringLiteral("#") + p.attribute(QStringLiteral("id")), + propertySet); + } + } + } + + std::function<void(const QDomElement &, bool)> parseDataInput; + parseDataInput = [&](const QDomElement &elem, bool parseChildren) { + const QString ctrlAtt = elem.attribute(QStringLiteral("controlledproperty")); + if (!ctrlAtt.isEmpty()) { + const QStringList dataInputs = ctrlAtt.split(QLatin1Char('$')); + for (auto &di : dataInputs) { + if (!di.isEmpty()) + outDataInputs.insert(di.left(di.indexOf(QLatin1Char(' ')))); + } + } + if (parseChildren) { + const QDomNodeList children = elem.childNodes(); + for (int i = 0; i < children.count(); ++i) + parseDataInput(children.at(i).toElement(), true); + } + }; + + std::function<void(const QDomElement &, bool)> parseDataOutput; + parseDataOutput = [&](const QDomElement &elem, bool parseChildren) { + const QString obsAtt = elem.attribute(QStringLiteral("observedproperty")); + if (!obsAtt.isEmpty()) { + const QStringList dataOutputs = obsAtt.split(QLatin1Char('$')); + for (auto &dout : dataOutputs) { + if (!dout.isEmpty()) + outDataOutputs.insert(dout.left(dout.indexOf(QLatin1Char(' ')))); + } + } + if (parseChildren) { + const QDomNodeList children = elem.childNodes(); + for (int i = 0; i < children.count(); ++i) + parseDataOutput(children.at(i).toElement(), true); + } + }; + // mesh files for group imports, materials, and effects are found under <Graph> + QDomElement graphElement = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Graph")); + + parseDataInput(graphElement, true); + parseDataOutput(graphElement, true); + + QDomNodeList modelElems = graphElement.elementsByTagName(QStringLiteral("Model")); + for (int i = 0; i < modelElems.count(); ++i) { + QDomElement elem = modelElems.at(i).toElement(); + const QString sourcePath = convertPath(elem.attribute(QStringLiteral("sourcepath"))); + if (!sourcePath.isEmpty()) { + QFileInfo fi(sourcePath); + QByteArray ext = fi.suffix().toLatin1(); + if (CDialogs::isMeshFileExtension(ext.data())) { + if (!outPathMap.contains(sourcePath)) + outPathMap.insert(sourcePath, {}); + continue; + } + } + } + + // find material and effect files instance ids + QHash<QString, QString> matEffClassIdMap; + auto parseMatEffIds = [&matEffClassIdMap](const QDomNodeList& nodes) { + for (int i = 0; i < nodes.count(); ++i) { + QDomElement elem = nodes.at(i).toElement(); + const QString classId = elem.attribute(QStringLiteral("class")); + if (!classId.isEmpty()) { + const QString id = elem.attribute(QStringLiteral("id")); + matEffClassIdMap.insert(QStringLiteral("#") + id, classId); + } + } + }; + + parseMatEffIds(graphElement.elementsByTagName(QStringLiteral("Effect"))); + parseMatEffIds(graphElement.elementsByTagName(QStringLiteral("CustomMaterial"))); + + // search <Logic> -> <State> -> <Add>/<Set> + const auto parseNodes = [&](const QDomNodeList &nodes) { + for (int i = 0; i < nodes.count(); ++i) { + QDomElement elem = nodes.at(i).toElement(); + const QString sourcePath = convertPath(elem.attribute(QStringLiteral("sourcepath"))); + if (!sourcePath.isEmpty()) { + QFileInfo fi(sourcePath); + QByteArray ext = fi.suffix().toLatin1(); + // supported types: + // images, custom mesh files for basic objects, import files, materialdef files + if (CDialogs::IsImageFileExtension(ext.data()) + || CDialogs::isMeshFileExtension(ext.data()) + || CDialogs::isImportFileExtension(ext.data()) + || CDialogs::IsMaterialFileExtension(ext.data())) { + if (!outPathMap.contains(sourcePath)) + outPathMap.insert(sourcePath, {}); + } else { + // add layer subpresentations paths + auto *sp = std::find_if( + subpresentations.begin(), subpresentations.end(), + [&sourcePath](const SubPresentationRecord &spr) -> bool { + return spr.m_id == sourcePath; + }); + if (sp != subpresentations.end()) { // has a subpresentation + QString spPath = convertPath(sp->m_argsOrSrc, true); + if (!outPathMap.contains(spPath)) { + outPathMap.insert(spPath, {}); + outPresentationNodes.insert(spPath, sp->m_id); + } + } + } + } + + // add texture subpresentations paths + QString subpresentation = elem.attribute(QStringLiteral("subpresentation")); + if (!subpresentation.isEmpty()) { + auto *sp = std::find_if( + subpresentations.begin(), subpresentations.end(), + [&subpresentation](const SubPresentationRecord &spr) -> bool { + return spr.m_id == subpresentation; + }); + if (sp != subpresentations.end()) { // has a subpresentation + QString spPath = convertPath(sp->m_argsOrSrc, true); + if (!outPathMap.contains(spPath)) { + outPathMap.insert(spPath, {}); + outPresentationNodes.insert(spPath, sp->m_id); + } + } + } + + // add fonts paths + QString font = elem.attribute(QStringLiteral("font")); + if (!font.isEmpty()) { + // the .uip file only shows the font name, we search for the font file in the + // current directory plus the 'fonts' directory at the same level or 1 level up. + + const QString TTF_EXT = QStringLiteral(".ttf"); // TODO: should we also handle .otf? + const QString slashUipPath = uipSrc.path() + QLatin1Char('/'); + + // this is the most probable place so lets search it first + QString fontPath = QStringLiteral("../fonts/") + font + TTF_EXT; + QFileInfo absFontPath(slashUipPath + fontPath); + if (absFontPath.exists()) { + if (!outPathMap.contains(fontPath)) + outPathMap.insert(fontPath, absFontPath.absoluteFilePath()); + } else { + fontPath = font + TTF_EXT; + absFontPath = QFileInfo(slashUipPath + fontPath); + if (absFontPath.exists()) { + if (!outPathMap.contains(fontPath)) + outPathMap.insert(fontPath, absFontPath.absoluteFilePath()); + } else { + fontPath = QStringLiteral("fonts/") + font + TTF_EXT; + absFontPath = QFileInfo(slashUipPath + fontPath); + if (!outPathMap.contains(fontPath) && absFontPath.exists()) + outPathMap.insert(fontPath, absFontPath.absoluteFilePath()); + } + } + } + + // add custom material/effect assets + const QString ref = elem.attribute(QStringLiteral("ref")); + const QString classId = matEffClassIdMap.value(ref, {}); + if (!classId.isEmpty()) { + const QSet<QString> textureProps = matEffPropertyMap.value(classId, {}); + for (auto &prop : textureProps) { + QString texturePath = elem.attribute(prop); + if (!texturePath.isEmpty()) { + // Typically these paths have ./ prepended even though they are relative + // to uip. + // Remove it as ./ at start is interpreted as relative to project file + if (texturePath.startsWith(QLatin1String("./"))) + texturePath = texturePath.mid(2); + if (!texturePath.isEmpty()) { + QFileInfo absTexPath(uipSrc.path() + QLatin1Char('/') + texturePath); + if (!outPathMap.contains(texturePath) && absTexPath.exists() + && absTexPath.isFile()) { + outPathMap.insert(texturePath, absTexPath.absoluteFilePath()); + } + } + } + } + } + + parseDataInput(elem, false); + } + }; + + QDomElement logicElem + = domDoc.documentElement().firstChild().firstChildElement(QStringLiteral("Logic")); + QDomNodeList addElems = logicElem.elementsByTagName(QStringLiteral("Add")); + QDomNodeList setElems = logicElem.elementsByTagName(QStringLiteral("Set")); + + parseNodes(addElems); + parseNodes(setElems); +} + +/** + * Find datainput use in subpresentation + * + * @param subpresentation subpresentation + * @param outmap returned list of datainput - property name pairs + */ +// static +bool PresentationFile::getDataInputBindings(const SubPresentationRecord &subpresentation, + QMultiMap<QString, QPair<QString, QString>> &outmap) +{ + QList<QString> ctrldPropList; + + QString spPath(g_StudioApp.getRenderableAbsolutePath(subpresentation.m_id)); + + QDomDocument domDoc; + if (!StudioUtils::readFileToDomDocument(spPath, domDoc)) + return false; + + // search <Graph> + QDomElement graphElem = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Graph")); + for (QDomElement p = graphElem.firstChild().toElement(); !p.isNull(); + p = p.nextSibling().toElement()) { + QString ctrldPropStr = p.attribute(QStringLiteral("controlledproperty")); + if (!ctrldPropStr.isEmpty()) + ctrldPropList.append(ctrldPropStr); + } + // Search Logic - State - Add + QDomNodeList addElems = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Logic")) + .elementsByTagName(QStringLiteral("Add")); + + for (int i = 0; i < addElems.count(); ++i) { + QDomElement elem = addElems.at(i).toElement(); + QString ctrldPropStr = elem.attribute(QStringLiteral("controlledproperty")); + if (!ctrldPropStr.isEmpty()) + ctrldPropList.append(ctrldPropStr); + } + + for (auto di : qAsConst(ctrldPropList)) { + QStringList split = di.split(QLatin1String(" ")); + for (int i = 0; i < split.size(); i += 2) { + QString diName = split[i]; + // Datainput names indicated with prefix "$", remove + // if found. + if (diName.startsWith(QLatin1String("$"))) + diName = diName.mid(1, diName.size() - 1); + QString propName = split[i + 1]; + // We should find the datainput from the global datainput list + // parsed out from UIA file, but check just in case and do not insert + // if not found. + if (g_StudioApp.m_dataInputDialogItems.contains(diName)) { + outmap.insert(subpresentation.m_id, QPair<QString, QString>(diName, propName)); + } else { + qWarning() << "Subpresentation" << subpresentation.m_id + << "is using datainput" << diName << "that is " + "not found from the current UIA file"; + } + } + } + return true; +} + +bool PresentationFile::getDataOutputBindings(const SubPresentationRecord &subpresentation, + QMultiMap<QString, QPair<QString, QString>> &outmap) +{ + QList<QString> ctrldPropList; + + QString spPath(g_StudioApp.getRenderableAbsolutePath(subpresentation.m_id)); + + QDomDocument domDoc; + if (!StudioUtils::readFileToDomDocument(spPath, domDoc)) + return false; + + // search <Graph> + QDomElement graphElem = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Graph")); + for (QDomElement p = graphElem.firstChild().toElement(); !p.isNull(); + p = p.nextSibling().toElement()) { + QString ctrldPropStr = p.attribute(QStringLiteral("observedproperty")); + if (!ctrldPropStr.isEmpty()) + ctrldPropList.append(ctrldPropStr); + } + // Search Logic - State - Add + QDomNodeList addElems = domDoc.documentElement().firstChild() + .firstChildElement(QStringLiteral("Logic")) + .elementsByTagName(QStringLiteral("Add")); + + for (int i = 0; i < addElems.count(); ++i) { + QDomElement elem = addElems.at(i).toElement(); + QString ctrldPropStr = elem.attribute(QStringLiteral("observedproperty")); + if (!ctrldPropStr.isEmpty()) + ctrldPropList.append(ctrldPropStr); + } + + for (auto dout : qAsConst(ctrldPropList)) { + QStringList split = dout.split(QLatin1Char(' ')); + for (int i = 0; i < split.size(); i += 2) { + QString doName = split[i]; + // Dataoutput names indicated with prefix "$", remove if found. + if (doName.startsWith(QLatin1Char('$'))) + doName = doName.mid(1, doName.size() - 1); + QString propName = split[i + 1]; + // We should find the dataoutputs from the global dataoutput list + // parsed out from UIA file, but check just in case and do not insert + // if not found. + if (g_StudioApp.m_dataInputDialogItems.contains(doName)) { + outmap.insert(subpresentation.m_id, QPair<QString, QString>(doName, propName)); + } else { + qWarning() << "Subpresentation" << subpresentation.m_id + << "is using dataoutput" << doName << "that is " + "not found from the current UIA file"; + } + } + } + return true; +} |