summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp')
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp1372
1 files changed, 0 insertions, 1372 deletions
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
deleted file mode 100644
index 4f62cd59..00000000
--- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
+++ /dev/null
@@ -1,1372 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 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 "qtAuthoring-config.h"
-#include <QtCore/qset.h>
-#include <QtCore/qtimer.h>
-
-#include "PresentationFile.h"
-#include "Qt3DSCommonPrecompile.h"
-#include "ProjectFileSystemModel.h"
-#include "StudioUtils.h"
-#include "StudioApp.h"
-#include "ClientDataModelBridge.h"
-#include "Core.h"
-#include "Doc.h"
-#include "Qt3DSFileTools.h"
-#include "ImportUtils.h"
-#include "Dialogs.h"
-#include "Qt3DSDMStudioSystem.h"
-#include "Qt3DSImportTranslation.h"
-#include "Qt3DSMessageBox.h"
-#include "IDocumentEditor.h"
-#include "IDragable.h"
-#include "IObjectReferenceHelper.h"
-#include "IDirectoryWatchingSystem.h"
-
-ProjectFileSystemModel::ProjectFileSystemModel(QObject *parent) : QAbstractListModel(parent)
- , m_model(new QFileSystemModel(this))
-{
- connect(m_model, &QAbstractItemModel::rowsInserted, this, &ProjectFileSystemModel::modelRowsInserted);
- connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ProjectFileSystemModel::modelRowsRemoved);
- connect(m_model, &QAbstractItemModel::layoutChanged, this, &ProjectFileSystemModel::modelLayoutChanged);
- connect(&g_StudioApp.GetCore()->getProjectFile(), &ProjectFile::presentationIdChanged,
- this, &ProjectFileSystemModel::handlePresentationIdChange);
- connect(&g_StudioApp.GetCore()->getProjectFile(), &ProjectFile::assetNameChanged,
- this, &ProjectFileSystemModel::asyncUpdateReferences);
-
- m_projectReferencesUpdateTimer.setSingleShot(true);
- m_projectReferencesUpdateTimer.setInterval(0);
-
- connect(&m_projectReferencesUpdateTimer, &QTimer::timeout,
- this, &ProjectFileSystemModel::updateProjectReferences);
-}
-
-QHash<int, QByteArray> ProjectFileSystemModel::roleNames() const
-{
- auto modelRoleNames = m_model->roleNames();
- modelRoleNames.insert(IsExpandableRole, "_isExpandable");
- modelRoleNames.insert(IsDraggableRole, "_isDraggable");
- modelRoleNames.insert(IsReferencedRole, "_isReferenced");
- modelRoleNames.insert(IsProjectReferencedRole, "_isProjectReferenced");
- modelRoleNames.insert(DepthRole, "_depth");
- modelRoleNames.insert(ExpandedRole, "_expanded");
- modelRoleNames.insert(FileIdRole, "_fileId");
- modelRoleNames.insert(ExtraIconRole, "_extraIcon");
- return modelRoleNames;
-}
-
-int ProjectFileSystemModel::rowCount(const QModelIndex &) const
-{
- return m_items.count();
-}
-
-QVariant ProjectFileSystemModel::data(const QModelIndex &index, int role) const
-{
- const auto &item = m_items.at(index.row());
-
- switch (role) {
- case Qt::DecorationRole: {
- QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
- return StudioUtils::resourceImageUrl() + getIconName(path);
- }
-
- case IsExpandableRole: {
- if (item.index == m_rootIndex) {
- return false;
- } else {
- return hasVisibleChildren(item.index);
- }
- }
-
- case IsDraggableRole:
- return QFileInfo(item.index.data(QFileSystemModel::FilePathRole).toString()).isFile();
-
- case IsReferencedRole: {
- const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
- return m_references.contains(path);
- }
-
- case IsProjectReferencedRole: {
- const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
- return m_projectReferences.contains(path);
- }
-
- case DepthRole:
- return item.depth;
-
- case ExpandedRole:
- return item.expanded;
-
- case FileIdRole: {
- const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
- EStudioObjectType iconType = getIconType(path);
- if (iconType == OBJTYPE_PRESENTATION || iconType == OBJTYPE_QML_STREAM)
- return presentationId(path);
- else
- return {};
- }
-
- case ExtraIconRole: {
- const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
- EStudioObjectType iconType = getIconType(path);
- if (iconType == OBJTYPE_PRESENTATION || iconType == OBJTYPE_QML_STREAM) {
- if (presentationId(path).isEmpty())
- return QStringLiteral("warning.png");
- else
- return {};
- } else {
- return {};
- }
- }
-
- default:
- return m_model->data(item.index, role);
- }
-}
-
-QMimeData *ProjectFileSystemModel::mimeData(const QModelIndexList &indexes) const
-{
- const QString path = filePath(indexes.first().row()); // can only drag one item
- return CDropSourceFactory::Create(QT3DS_FLAVOR_ASSET_UICFILE, path);
-}
-
-QString ProjectFileSystemModel::filePath(int row) const
-{
- if (row < 0 || row >= m_items.size())
- return QString();
- const auto &item = m_items.at(row);
- return item.index.data(QFileSystemModel::FilePathRole).toString();
-}
-
-bool ProjectFileSystemModel::isRefreshable(int row) const
-{
- const QString path = filePath(row);
- // Import needs to be refreshable even if it is not referenced, as user may drag just individual
- // meshes into the scene, and not the whole import.
- return path.endsWith(QLatin1String(".import"));
-}
-
-void ProjectFileSystemModel::updateReferences()
-{
- m_references.clear();
- const auto doc = g_StudioApp.GetCore()->GetDoc();
- const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
- const auto sourcePathList = bridge->GetSourcePathList();
- const auto fontFileList = bridge->GetFontFileList();
- const auto effectTextureList = bridge->GetDynamicObjectTextureList();
- auto renderableList = bridge->getRenderableList();
- auto subpresentationRecord = g_StudioApp.m_subpresentations;
-
- const QDir projectDir(doc->GetCore()->getProjectFile().getProjectPath());
- const QString projectPath = QDir::cleanPath(projectDir.absolutePath());
- const QString projectPathSlash = projectPath + QLatin1Char('/');
-
- // Add current presentation to renderables list
- renderableList.insert(doc->getPresentationId());
- subpresentationRecord.push_back(
- SubPresentationRecord({}, doc->getPresentationId(),
- projectDir.relativeFilePath(doc->GetDocumentPath())));
-
- auto addReferencesPresentation = [this, doc, &projectPath](const QString &str) {
- addPathsToReferences(m_references, projectPath, doc->GetResolvedPathToDoc(str));
- };
- auto addReferencesRenderable = [this, &projectPath, &projectPathSlash, &subpresentationRecord]
- (const QString &id) {
- for (SubPresentationRecord r : qAsConst(subpresentationRecord)) {
- if (r.m_id == id)
- addPathsToReferences(m_references, projectPath, projectPathSlash + r.m_argsOrSrc);
- }
- };
-
- std::for_each(sourcePathList.begin(), sourcePathList.end(), addReferencesPresentation);
- std::for_each(fontFileList.begin(), fontFileList.end(), addReferencesPresentation);
- std::for_each(effectTextureList.begin(), effectTextureList.end(), addReferencesPresentation);
- std::for_each(renderableList.begin(), renderableList.end(), addReferencesRenderable);
-
- m_references.insert(projectPath);
-
- updateRoles({IsReferencedRole, Qt::DecorationRole});
-}
-
-/**
- * Checks if file is already imported and if not, adds it to outImportedFiles
- *
- * @param importFile The new imported file to check
- * @param outImportedFiles List of already imported files
- * @return true if importFile was added
- */
-bool ProjectFileSystemModel::addUniqueImportFile(const QString &importFile,
- QStringList &outImportedFiles) const
-{
- const QString cleanPath = QFileInfo(importFile).canonicalFilePath();
- if (outImportedFiles.contains(cleanPath)) {
- return false;
- } else {
- outImportedFiles.append(cleanPath);
- return true;
- }
-}
-
-/**
- * Copy a file with option to override an existing file or skip the override.
- *
- * @param srcFile The source file to copy.
- * @param targetFile The destination file path.
- * @param outImportedFiles list of absolute source paths of the dependent assets that are imported
- * in the same import context.
- * @param outOverrideChoice The copy skip/override choice used in this import context.
- */
-void ProjectFileSystemModel::overridableCopyFile(const QString &srcFile, const QString &targetFile,
- QStringList &outImportedFiles,
- int &outOverrideChoice) const
-{
- QFileInfo srcFileInfo(srcFile);
- if (srcFileInfo.exists() && addUniqueImportFile(srcFile, outImportedFiles)) {
- QFileInfo targetFileInfo(targetFile);
- if (srcFileInfo == targetFileInfo)
- return; // Autoskip when source and target is the same
- if (!targetFileInfo.dir().exists())
- targetFileInfo.dir().mkpath(QStringLiteral("."));
-
- if (targetFileInfo.exists()) { // asset exists, show override / skip box
- if (outOverrideChoice == QMessageBox::YesToAll) {
- QFile::remove(targetFile);
- } else if (outOverrideChoice == QMessageBox::NoToAll) {
- // QFile::copy() does not override files
- } else {
- QString pathFromRoot = QDir(g_StudioApp.GetCore()->getProjectFile()
- .getProjectPath())
- .relativeFilePath(targetFile);
- outOverrideChoice = g_StudioApp.GetDialogs()
- ->displayOverrideAssetBox(pathFromRoot);
- if (outOverrideChoice & (QMessageBox::Yes | QMessageBox::YesToAll))
- QFile::remove(targetFile);
- }
- }
- QFile::copy(srcFile, targetFile);
- }
-}
-
-void ProjectFileSystemModel::updateProjectReferences()
-{
- m_projectReferences.clear();
-
- const QDir projectDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
- const QString projectPath = QDir::cleanPath(projectDir.absolutePath());
-
- QHashIterator<QString, bool> updateIt(m_projectReferencesUpdateMap);
- while (updateIt.hasNext()) {
- updateIt.next();
- m_presentationReferences.remove(updateIt.key());
- if (updateIt.value()) {
- QFileInfo fi(updateIt.key());
- QDir fileDir = fi.dir();
- const QString suffix = fi.suffix();
- QHash<QString, QString> importPathMap;
- QSet<QString> newReferences;
-
- const auto addReferencesFromImportMap = [&]() {
- QHashIterator<QString, QString> pathIter(importPathMap);
- while (pathIter.hasNext()) {
- pathIter.next();
- const QString path = pathIter.key();
- QString targetAssetPath;
- if (path.startsWith(QLatin1String("./"))) // path from project root
- targetAssetPath = projectDir.absoluteFilePath(path);
- else // relative path
- targetAssetPath = fileDir.absoluteFilePath(path);
- newReferences.insert(QDir::cleanPath(targetAssetPath));
- }
- };
-
- if (CDialogs::presentationExtensions().contains(suffix)
- || CDialogs::qmlStreamExtensions().contains(suffix)) {
- // Presentation file added/modified, check that it is one of the subpresentations,
- // or we don't care about it
- const QString relPath = g_StudioApp.GetCore()->getProjectFile()
- .getRelativeFilePathTo(updateIt.key());
- 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) {
- if (rec.m_type == QLatin1String("presentation")) {
- // Since this is not actual import, source and target uip is the same,
- // and we are only interested in the absolute paths of the "imported"
- // asset files
- QString dummyStr;
- QHash<QString, QString> dummyMap;
- QSet<QString> dummyDataInputSet;
- QSet<QString> dummyDataOutputSet;
- PresentationFile::getSourcePaths(fi, fi, importPathMap,
- dummyStr, dummyMap, dummyDataInputSet,
- dummyDataOutputSet);
- addReferencesFromImportMap();
- } else { // qml-stream
- QQmlApplicationEngine qmlEngine;
- bool isQmlStream = false;
- QObject *qmlRoot = getQmlStreamRootNode(qmlEngine, updateIt.key(),
- isQmlStream);
- if (qmlRoot && isQmlStream) {
- QSet<QString> assetPaths;
- getQmlAssets(qmlRoot, assetPaths);
- QDir qmlDir = fi.dir();
- for (auto &assetSrc : qAsConst(assetPaths)) {
- QString targetAssetPath;
- targetAssetPath = qmlDir.absoluteFilePath(assetSrc);
- newReferences.insert(QDir::cleanPath(targetAssetPath));
- }
- }
- }
- break;
- }
- }
- } else if (CDialogs::materialExtensions().contains(suffix)
- || CDialogs::effectExtensions().contains(suffix)) {
- // Use dummy set, as we are only interested in values set in material files
- QSet<QString> dummySet;
- g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
- .ParseSourcePathsOutOfEffectFile(
- updateIt.key(),
- g_StudioApp.GetCore()->getProjectFile().getProjectPath(),
- false, // No need to recurse src mats; those get handled individually
- importPathMap, dummySet);
- addReferencesFromImportMap();
- }
- if (!newReferences.isEmpty())
- m_presentationReferences.insert(updateIt.key(), newReferences);
- }
- }
-
- // Update reference cache
- QHashIterator<QString, QSet<QString>> presIt(m_presentationReferences);
- while (presIt.hasNext()) {
- presIt.next();
- const auto &refs = presIt.value();
- for (auto &ref : refs)
- addPathsToReferences(m_projectReferences, projectPath, ref);
- }
-
- m_projectReferencesUpdateMap.clear();
- updateRoles({IsProjectReferencedRole});
-}
-
-void ProjectFileSystemModel::getQmlAssets(const QObject *qmlNode,
- QSet<QString> &outAssetPaths) const
-{
- QString assetSrc = qmlNode->property("source").toString(); // absolute file path
-
- if (!assetSrc.isEmpty()) {
- // remove file:///
- if (assetSrc.startsWith(QLatin1String("file:///")))
- assetSrc = assetSrc.mid(8);
- else if (assetSrc.startsWith(QLatin1String("file://")))
- assetSrc = assetSrc.mid(7);
-
-#if !defined(Q_OS_WIN)
- // Only windows has drive letter in the path, other platforms need to start with /
- assetSrc.prepend(QLatin1Char('/'));
-#endif
- outAssetPaths.insert(assetSrc);
- }
-
- // recursively load child nodes
- const QObjectList qmlNodeChildren = qmlNode->children();
- for (auto &node : qmlNodeChildren)
- getQmlAssets(node, outAssetPaths);
-}
-
-QObject *ProjectFileSystemModel::getQmlStreamRootNode(QQmlApplicationEngine &qmlEngine,
- const QString &filePath,
- bool &outIsQmlStream) const
-{
- QObject *qmlRoot = nullptr;
- outIsQmlStream = false;
-
- qmlEngine.load(filePath);
- if (qmlEngine.rootObjects().size() > 0) {
- qmlRoot = qmlEngine.rootObjects().at(0);
- const char *rootClassName = qmlEngine.rootObjects().at(0)
- ->metaObject()->superClass()->className();
- // The assumption here is that any qml that is not a behavior is a qml stream
- if (strcmp(rootClassName, "Q3DStudio::Q3DSQmlBehavior") != 0)
- outIsQmlStream = true;
- }
-
- return qmlRoot;
-}
-
-Q3DStudio::DocumentEditorFileType::Enum ProjectFileSystemModel::assetTypeForRow(int row)
-{
- if (row <= 0 || row >= m_items.size())
- return Q3DStudio::DocumentEditorFileType::Unknown;
-
- const QString rootPath = m_items[0].index.data(QFileSystemModel::FilePathRole).toString();
- QString path = m_items[row].index.data(QFileSystemModel::FilePathRole).toString();
- QFileInfo fi(path);
- if (!fi.isDir())
- path = fi.absolutePath();
- path = path.mid(rootPath.length() + 1);
- const int slash = path.indexOf(QLatin1String("/"));
- if (slash >= 0)
- path = path.left(slash);
- if (path == QLatin1String("effects"))
- return Q3DStudio::DocumentEditorFileType::Effect;
- else if (path == QLatin1String("fonts"))
- return Q3DStudio::DocumentEditorFileType::Font;
- else if (path == QLatin1String("maps"))
- return Q3DStudio::DocumentEditorFileType::Image;
- else if (path == QLatin1String("materials"))
- return Q3DStudio::DocumentEditorFileType::Material;
- else if (path == QLatin1String("models"))
- return Q3DStudio::DocumentEditorFileType::DAE;
- else if (path == QLatin1String("scripts"))
- return Q3DStudio::DocumentEditorFileType::Behavior;
- else if (path == QLatin1String("presentations"))
- return Q3DStudio::DocumentEditorFileType::Presentation;
- else if (path == QLatin1String("qml"))
- return Q3DStudio::DocumentEditorFileType::QmlStream;
-
- return Q3DStudio::DocumentEditorFileType::Unknown;
-}
-
-void ProjectFileSystemModel::setRootPath(const QString &path)
-{
- m_projectReferences.clear();
- m_presentationReferences.clear();
- m_projectReferencesUpdateMap.clear();
- m_projectReferencesUpdateTimer.stop();
-
- // Delete the old model. If the new project is in a totally different directory tree, not
- // doing this will result in unexplicable crashes when trying to parse something that should
- // not be parsed.
- disconnect(m_model, &QAbstractItemModel::rowsInserted,
- this, &ProjectFileSystemModel::modelRowsInserted);
- disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
- this, &ProjectFileSystemModel::modelRowsRemoved);
- disconnect(m_model, &QAbstractItemModel::layoutChanged,
- this, &ProjectFileSystemModel::modelLayoutChanged);
- delete m_model;
- m_model = new QFileSystemModel(this);
- connect(m_model, &QAbstractItemModel::rowsInserted,
- this, &ProjectFileSystemModel::modelRowsInserted);
- connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
- this, &ProjectFileSystemModel::modelRowsRemoved);
- connect(m_model, &QAbstractItemModel::layoutChanged,
- this, &ProjectFileSystemModel::modelLayoutChanged);
-
- setRootIndex(m_model->setRootPath(path));
-
- // Open the presentations folder by default
- connect(this, &ProjectFileSystemModel::dataChanged,
- this, &ProjectFileSystemModel::asyncExpandPresentations);
-
- QTimer::singleShot(0, [this]() {
- // Watch the project directory for changes to .uip files.
- // Note that this initial connection will notify creation for all files, so we call it
- // asynchronously to ensure the subpresentations are registered.
- m_directoryConnection = g_StudioApp.getDirectoryWatchingSystem().AddDirectory(
- g_StudioApp.GetCore()->getProjectFile().getProjectPath(),
- std::bind(&ProjectFileSystemModel::onFilesChanged, this,
- std::placeholders::_1));
- });
-}
-
-void ProjectFileSystemModel::setRootIndex(const QModelIndex &rootIndex)
-{
- if (rootIndex == m_rootIndex)
- return;
-
- clearModelData();
-
- m_rootIndex = rootIndex;
-
- beginInsertRows({}, 0, 0);
- m_items.append({ m_rootIndex, 0, true, nullptr, 0 });
- endInsertRows();
-
- showModelTopLevelItems();
-}
-
-void ProjectFileSystemModel::clearModelData()
-{
- beginResetModel();
- m_defaultDirToAbsPathMap.clear();
- m_items.clear();
- endResetModel();
-}
-
-void ProjectFileSystemModel::showModelTopLevelItems()
-{
- int rowCount = m_model->rowCount(m_rootIndex);
-
- if (rowCount == 0) {
- if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex))
- m_model->fetchMore(m_rootIndex);
- } else {
- showModelChildItems(m_rootIndex, 0, rowCount - 1);
- }
-}
-
-void ProjectFileSystemModel::showModelChildItems(const QModelIndex &parentIndex, int start, int end)
-{
- const int parentRow = modelIndexRow(parentIndex);
- if (parentRow == -1)
- return;
-
- Q_ASSERT(isVisible(parentIndex));
-
- QVector<QModelIndex> rowsToInsert;
- for (int i = start; i <= end; ++i) {
- const auto &childIndex = m_model->index(i, 0, parentIndex);
- if (isVisible(childIndex))
- rowsToInsert.append(childIndex);
- }
-
- const int insertCount = rowsToInsert.count();
- if (insertCount == 0)
- return;
-
- auto parent = &m_items[parentRow];
-
- const int depth = parent->depth + 1;
- const int startRow = parentRow + parent->childCount + 1;
-
- beginInsertRows({}, startRow, startRow + insertCount - 1);
-
- for (auto it = rowsToInsert.rbegin(); it != rowsToInsert.rend(); ++it)
- m_items.insert(startRow, { *it, depth, false, parent, 0 });
-
- for (; parent != nullptr; parent = parent->parent)
- parent->childCount += insertCount;
-
- endInsertRows();
-
- // also fetch children so we're notified when files are added or removed in immediate subdirs
- for (const auto &childIndex : rowsToInsert) {
- if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
- m_model->fetchMore(childIndex);
- }
-}
-
-void ProjectFileSystemModel::expand(int row)
-{
- if (row < 0 || row > m_items.size() - 1 || m_items[row].expanded)
- return;
-
- auto &item = m_items[row];
- const auto &modelIndex = item.index;
-
- const int rowCount = m_model->rowCount(modelIndex);
- if (rowCount == 0) {
- if (m_model->hasChildren(modelIndex) && m_model->canFetchMore(modelIndex))
- m_model->fetchMore(modelIndex);
- } else {
- showModelChildItems(modelIndex, 0, rowCount - 1);
- }
-
- item.expanded = true;
- Q_EMIT dataChanged(index(row), index(row));
-}
-
-bool ProjectFileSystemModel::hasValidUrlsForDropping(const QList<QUrl> &urls) const
-{
- for (const auto &url : urls) {
- if (url.isLocalFile()) {
- const QString path = url.toLocalFile();
- const QFileInfo fileInfo(path);
- if (fileInfo.isFile()) {
- const QString extension = fileInfo.suffix();
- return extension.compare(QLatin1String(CDialogs::GetDAEFileExtension()),
- Qt::CaseInsensitive) == 0
-#ifdef QT_3DSTUDIO_FBX
- || extension.compare(QLatin1String(CDialogs::GetFbxFileExtension()),
- Qt::CaseInsensitive) == 0
-#endif
- || getIconType(path) != OBJTYPE_UNKNOWN;
- }
- }
- }
-
- return false;
-}
-
-void ProjectFileSystemModel::showInfo(int row)
-{
- if (row < 0 || row >= m_items.size())
- row = 0;
-
- const TreeItem &item = m_items.at(row);
- QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
-
- QFileInfo fi(path);
-
- if (fi.suffix() == QLatin1String("materialdef")) {
- const auto doc = g_StudioApp.GetCore()->GetDoc();
- bool isDocModified = doc->isModified();
- { // Scope for the ScopedDocumentEditor
- Q3DStudio::ScopedDocumentEditor sceneEditor(
- Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QString()));
- const auto material = sceneEditor->getOrCreateMaterial(path);
- QString name;
- QMap<QString, QString> values;
- QMap<QString, QMap<QString, QString>> textureValues;
- sceneEditor->getMaterialInfo(fi.absoluteFilePath(), name, values, textureValues);
- sceneEditor->setMaterialValues(fi.absoluteFilePath(), values, textureValues);
- if (material.Valid())
- doc->SelectDataModelObject(material);
- }
- // Several aspects of the editor are not updated correctly
- // if the data core is changed without a transaction
- // The above scope completes the transaction for creating a new material
- // Next the added undo has to be popped from the stack
- // and the modified flag has to be restored
- // TODO: Find a way to update the editor fully without a transaction
- doc->SetModifiedFlag(isDocModified);
- g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
- }
-}
-
-void ProjectFileSystemModel::duplicate(int row)
-{
- if (row < 0 || row >= m_items.size())
- row = 0;
-
- const TreeItem &item = m_items.at(row);
- QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
-
- QFileInfo srcFile(path);
- const QString destPathStart = srcFile.dir().absolutePath() + QLatin1Char('/')
- + srcFile.completeBaseName() + QStringLiteral(" Copy");
- const QString destPathEnd = QStringLiteral(".") + srcFile.suffix();
- QString destPath = destPathStart + destPathEnd;
-
- int i = 1;
- while (QFile::exists(destPath)) {
- i++;
- destPath = destPathStart + QString::number(i) + destPathEnd;
- }
-
- QFile::copy(path, destPath);
-}
-
-void ProjectFileSystemModel::importUrls(const QList<QUrl> &urls, int row, bool autoSort)
-{
- if (row < 0 || row >= m_items.size())
- row = 0; // Import to root folder row not specified
-
- // If importing via buttons or doing in-context import to project root,
- // sort imported items to default folders according to their type
- const bool sortToDefaults = autoSort || row == 0;
- if (sortToDefaults)
- updateDefaultDirMap();
-
- const TreeItem &item = m_items.at(row);
- QString targetPath = item.index.data(QFileSystemModel::FilePathRole).toString();
-
- QFileInfo fi(targetPath);
- if (!fi.isDir())
- targetPath = fi.absolutePath();
- const QDir targetDir(targetPath);
-
- QStringList expandPaths;
- QHash<QString, QString> presentationNodes; // <relative path to presentation, presentation id>
- // List of all files that have been copied by this import. Used to avoid duplicate imports
- // due to some of the imported files also being assets used by other imported files.
- QStringList importedFiles;
- QMap<QString, CDataInputDialogItem *> importedDataInputs;
- int overrideChoice = QMessageBox::NoButton;
-
- for (const auto &url : urls) {
- QString sortedPath = targetPath;
- QDir sortedDir = targetDir;
-
- if (sortToDefaults) {
- const QString defaultDir = m_defaultDirToAbsPathMap.value(
- g_StudioApp.GetDialogs()->defaultDirForUrl(url));
- if (!defaultDir.isEmpty()) {
- sortedPath = defaultDir;
- sortedDir.setPath(sortedPath);
- }
- }
-
- if (sortedDir.exists()) {
- importUrl(sortedDir, url, presentationNodes, importedFiles, importedDataInputs,
- overrideChoice);
- expandPaths << sortedDir.path();
- }
- }
-
- // Batch update all imported presentation nodes
- g_StudioApp.GetCore()->getProjectFile().addPresentationNodes(presentationNodes);
-
- // Add new data inputs that are missing from project's data inputs. Duplicates are ignored,
- // even if they are different type.
- QMapIterator<QString, CDataInputDialogItem *> diIt(importedDataInputs);
- bool addedDi = false;
- while (diIt.hasNext()) {
- diIt.next();
- if (!g_StudioApp.m_dataInputDialogItems.contains(diIt.key())) {
- g_StudioApp.m_dataInputDialogItems.insert(diIt.key(), diIt.value());
- addedDi = true;
- } else {
- delete diIt.value();
- }
- }
- if (addedDi) {
- g_StudioApp.saveDataInputsToProjectFile();
- g_StudioApp.checkDeletedDatainputs(); // Updates externalPresBoundTypes
- }
-
- for (const QString &expandPath : qAsConst(expandPaths)) {
- int expandRow = rowForPath(expandPath);
- if (expandRow >= 0 && !m_items[expandRow].expanded)
- expand(expandRow);
- }
-}
-
-/**
- * Imports a single asset and the assets it depends on.
- *
- * @param targetDir Target path where the asset is imported to
- * @param url Source url where the asset is imported from
- * @param outPresentationNodes Map where presentation node information is stored for later
- * registration. The key is relative path to presentation. The value
- * is presentation id.
- * @param outImportedFiles List of absolute source paths of the dependent assets that are imported
- * in the same import context.
- * @param outDataInputs Map of data inputs that are in use in this import context.
- * @param outOverrideChoice The copy skip/override choice used in this import context.
- */
-void ProjectFileSystemModel::importUrl(QDir &targetDir, const QUrl &url,
- QHash<QString, QString> &outPresentationNodes,
- QStringList &outImportedFiles,
- QMap<QString, CDataInputDialogItem *> &outDataInputs,
- int &outOverrideChoice) const
-{
- using namespace Q3DStudio;
- using namespace qt3dsimp;
- // Drag and Drop - From Explorer window to Project Palette
- // For all valid Project File Types:
- // - This performs a file copy from the source Explorer location to the selected Project Palette
- // Folder
- // - The destination copy must NOT be read-only even if the source is read-only
- // For DAE, it will import the file.
-
- if (!url.isLocalFile())
- return;
-
- const QString sourceFile = url.toLocalFile();
-
- const QFileInfo fileInfo(sourceFile);
- if (!fileInfo.isFile())
- return;
-
- // Skip importing if the file has already been imported
- if (!addUniqueImportFile(sourceFile, outImportedFiles))
- return;
-
- const auto doc = g_StudioApp.GetCore()->GetDoc();
-
- const QString extension = fileInfo.suffix();
- const QString fileStem = fileInfo.baseName();
- const QString outputFileName = QStringLiteral("%1.%2").arg(fileStem).arg(CDialogs::GetImportFileExtension());
-
- if (extension.compare(QLatin1String(CDialogs::GetDAEFileExtension()), Qt::CaseInsensitive) == 0) {
- SColladaTranslator translator(sourceFile);
- const QDir outputDir = SFileTools::FindUniqueDestDirectory(targetDir, fileStem);
- const QString fullOutputFile = outputDir.filePath(outputFileName);
- const SImportResult importResult =
- CPerformImport::TranslateToImportFile(translator, CFilePath(fullOutputFile));
- bool forceError = QFileInfo(fullOutputFile).isFile() == false;
- IDocumentEditor::DisplayImportErrors(
- sourceFile, importResult.m_Error, doc->GetImportFailedHandler(),
- translator.m_TranslationLog, forceError);
-#ifdef QT_3DSTUDIO_FBX
- } else if (extension.compare(QLatin1String(CDialogs::GetFbxFileExtension()), Qt::CaseInsensitive) == 0) {
- SFbxTranslator translator(sourceFile);
- const QDir outputDir = SFileTools::FindUniqueDestDirectory(targetDir, fileStem);
- const QString fullOutputFile = outputDir.filePath(outputFileName);
- const SImportResult importResult =
- CPerformImport::TranslateToImportFile(translator, CFilePath(fullOutputFile));
- bool forceError = QFileInfo(fullOutputFile).isFile() == false;
- IDocumentEditor::DisplayImportErrors(
- sourceFile, importResult.m_Error, doc->GetImportFailedHandler(),
- translator.m_TranslationLog, forceError);
-#endif
- } else {
- QQmlApplicationEngine qmlEngine;
- QObject *qmlRoot = nullptr;
- bool isQmlStream = false;
- if (extension == QLatin1String("qml")) {
- qmlRoot = getQmlStreamRootNode(qmlEngine, sourceFile, isQmlStream);
- if (qmlRoot) {
- if (isQmlStream && targetDir.path().endsWith(QLatin1String("/scripts"))) {
- const QString path(QStringLiteral("../qml"));
- targetDir.mkpath(path); // create the folder if doesn't exist
- targetDir.cd(path);
- }
- } else {
- // Invalid qml file, block import
- g_StudioApp.GetDialogs()->DisplayKnownErrorDialog(
- tr("Failed to parse '%1'\nAborting import.").arg(sourceFile));
- return;
- }
- }
- // Copy the file to target directory
- // FindAndCopyDestFile will make sure the file name is unique and make sure it is
- // not read only.
- QString destPath; // final file path (after copying and renaming)
- bool copyResult = SFileTools::FindAndCopyDestFile(targetDir, sourceFile, destPath);
- Q_ASSERT(copyResult);
-
- QString presentationPath;
- if (CDialogs::isPresentationFileExtension(extension.toLatin1().data())) {
- presentationPath = doc->GetCore()->getProjectFile().getRelativeFilePathTo(destPath);
- QSet<QString> dataInputs;
- QSet<QString> dataOutputs;
- importPresentationAssets(fileInfo, QFileInfo(destPath), outPresentationNodes,
- outImportedFiles, dataInputs, dataOutputs, outOverrideChoice);
- const QString projFile = PresentationFile::findProjectFile(fileInfo.absoluteFilePath());
-
- // #TODO: Handle DataOutputs QT3DS-3510
- QMap<QString, CDataInputDialogItem *> allDataInputs;
- ProjectFile::loadDataInputs(projFile, allDataInputs);
- for (auto &di : dataInputs) {
- if (allDataInputs.contains(di))
- outDataInputs.insert(di, allDataInputs[di]);
- }
- } else if (qmlRoot && isQmlStream) { // importing a qml stream
- presentationPath = doc->GetCore()->getProjectFile().getRelativeFilePathTo(destPath);
- importQmlAssets(qmlRoot, fileInfo.dir(), targetDir, outImportedFiles,
- outOverrideChoice);
- }
-
- // outPresentationNodes can already contain this presentation in case of multi-importing
- // both a presentation and its subpresentation
- if (!presentationPath.isEmpty() && !outPresentationNodes.contains(presentationPath)) {
- const QString srcProjFile = PresentationFile::findProjectFile(sourceFile);
- QString presId;
- if (!srcProjFile.isEmpty()) {
- QVector<SubPresentationRecord> subpresentations;
- ProjectFile::getPresentations(srcProjFile, subpresentations);
- QDir srcProjDir(QFileInfo(srcProjFile).path());
- const QString relSrcPresFilePath = srcProjDir.relativeFilePath(sourceFile);
- auto *sp = std::find_if(
- subpresentations.begin(), subpresentations.end(),
- [&relSrcPresFilePath](const SubPresentationRecord &spr) -> bool {
- return spr.m_argsOrSrc == relSrcPresFilePath;
- });
- // Make sure we are not adding a duplicate id. In that case presId will be empty
- // which causes autogeneration of an unique id.
- if (sp != subpresentations.end()
- && g_StudioApp.GetCore()->getProjectFile().isUniquePresentationId(sp->m_id)) {
- presId = sp->m_id;
- }
- }
- outPresentationNodes.insert(presentationPath, presId);
- }
-
- // For effect and custom material files, automatically copy related resources
- if (CDialogs::IsEffectFileExtension(extension.toLatin1().data())
- || CDialogs::IsMaterialFileExtension(extension.toLatin1().data())) {
- QHash<QString, QString> effectFileSourcePaths;
- QString absSrcPath = fileInfo.absoluteFilePath();
- QString projectPath
- = QFileInfo(PresentationFile::findProjectFile(absSrcPath)).absolutePath();
- // Since we are importing a bare material/effect, we don't care about possible dynamic
- // values of texture properties
- QSet<QString> dummyPropertySet;
- g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
- .ParseSourcePathsOutOfEffectFile(absSrcPath, projectPath, true,
- effectFileSourcePaths, dummyPropertySet);
-
- QHashIterator<QString, QString> pathIter(effectFileSourcePaths);
- while (pathIter.hasNext()) {
- pathIter.next();
- overridableCopyFile(pathIter.value(),
- QDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath())
- .absoluteFilePath(pathIter.key()),
- outImportedFiles, outOverrideChoice);
- }
- }
- }
-}
-
-/**
- * Import all assets used in a uip file, this includes materials and effects (and their assets),
- * images, fonts, subpresentations (and their recursive assets), models and scripts. Assets are
- * imported in the same relative structure in the imported-from folder in order not to break the
- * assets paths.
- *
- * @param uipSrc source file path where the uip is imported from
- * @param uipTarget target path where the uip is imported to
- * @param outPresentationNodes map where presentation node information is stored for later
- * registration. The key is relative path to presentation. The value
- * is presentation id.
- * @param overrideChoice tracks user choice (yes to all / no to all) to maintain the value through
- * recursive calls
- * @param outImportedFiles list of absolute source paths of the dependent assets that are imported
- * in the same import context.
- * @param outDataInputs set of data input identifiers that are in use by this presentation and its
- * subpresentations.
- * @param outOverrideChoice The copy skip/override choice used in this import context.
- */
-void ProjectFileSystemModel::importPresentationAssets(
- const QFileInfo &uipSrc, const QFileInfo &uipTarget,
- QHash<QString, QString> &outPresentationNodes, QStringList &outImportedFiles,
- QSet<QString> &outDataInputs, QSet<QString> &outDataOutputs, int &outOverrideChoice) const
-{
- QHash<QString, QString> importPathMap;
- QString projPathSrc; // project absolute path for the source uip
- PresentationFile::getSourcePaths(uipSrc, uipTarget, importPathMap, projPathSrc,
- outPresentationNodes, outDataInputs, outDataOutputs);
- const QDir projDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
- const QDir uipSrcDir = uipSrc.dir();
- const QDir uipTargetDir = uipTarget.dir();
-
- QHashIterator<QString, QString> pathIter(importPathMap);
- while (pathIter.hasNext()) {
- pathIter.next();
- QString srcAssetPath = pathIter.value();
- const QString path = pathIter.key();
- QString targetAssetPath;
- if (srcAssetPath.isEmpty())
- srcAssetPath = uipSrcDir.absoluteFilePath(path);
- targetAssetPath = uipTargetDir.absoluteFilePath(path);
-
- overridableCopyFile(srcAssetPath, targetAssetPath, outImportedFiles, outOverrideChoice);
-
- if (path.endsWith(QLatin1String(".uip"))) {
- // recursively load any uip asset's assets
- importPresentationAssets(QFileInfo(srcAssetPath), QFileInfo(targetAssetPath),
- outPresentationNodes, outImportedFiles, outDataInputs,
- outDataOutputs, outOverrideChoice);
-
- // update the path in outPresentationNodes to be correctly relative in target project
- const QString subId = outPresentationNodes.take(path);
- if (!subId.isEmpty())
- outPresentationNodes.insert(projDir.relativeFilePath(targetAssetPath), subId);
- } else if (path.endsWith(QLatin1String(".qml"))) {
- // recursively load any qml stream assets
- QQmlApplicationEngine qmlEngine;
- bool isQmlStream = false;
- QObject *qmlRoot = getQmlStreamRootNode(qmlEngine, srcAssetPath, isQmlStream);
- if (qmlRoot && isQmlStream) {
- importQmlAssets(qmlRoot, QFileInfo(srcAssetPath).dir(),
- QFileInfo(targetAssetPath).dir(), outImportedFiles,
- outOverrideChoice);
- // update path in outPresentationNodes to be correctly relative in target project
- const QString subId = outPresentationNodes.take(path);
- if (!subId.isEmpty())
- outPresentationNodes.insert(projDir.relativeFilePath(targetAssetPath), subId);
- }
- }
- }
-}
-
-/**
- * Import all assets specified in "source" properties in a qml file.
- *
- * @param qmlNode The qml node to checkfor assets. Recursively checks all child nodes, too.
- * @param srcDir target dir where the assets are imported to
- * @param outImportedFiles list of absolute source paths of the dependent assets that are imported
- * in the same import context.
- * @param outOverrideChoice The copy skip/override choice used in this import context.
- */
-void ProjectFileSystemModel::importQmlAssets(const QObject *qmlNode, const QDir &srcDir,
- const QDir &targetDir,
- QStringList &outImportedFiles,
- int &outOverrideChoice) const
-{
- QSet<QString> assetPaths;
- getQmlAssets(qmlNode, assetPaths);
-
- for (auto &assetSrc : qAsConst(assetPaths)) {
- overridableCopyFile(srcDir.absoluteFilePath(assetSrc),
- targetDir.absoluteFilePath(srcDir.relativeFilePath(assetSrc)),
- outImportedFiles, outOverrideChoice);
- }
-}
-
-int ProjectFileSystemModel::rowForPath(const QString &path) const
-{
- for (int i = m_items.size() - 1; i >= 0 ; --i) {
- const QString itemPath = m_items[i].index.data(QFileSystemModel::FilePathRole).toString();
- if (path == itemPath)
- return i;
- }
- return -1;
-}
-
-void ProjectFileSystemModel::updateRoles(const QVector<int> &roles, int startRow, int endRow)
-{
- Q_EMIT dataChanged(index(startRow, 0),
- index(endRow < 0 ? rowCount() - 1 : endRow, 0), roles);
-}
-
-void ProjectFileSystemModel::collapse(int row)
-{
- Q_ASSERT(row >= 0 && row < m_items.size());
-
- auto &item = m_items[row];
- Q_ASSERT(item.expanded == true);
-
- const int childCount = item.childCount;
-
- if (childCount > 0) {
- beginRemoveRows({}, row + 1, row + childCount);
-
- m_items.erase(std::begin(m_items) + row + 1, std::begin(m_items) + row + 1 + childCount);
-
- for (auto parent = &item; parent != nullptr; parent = parent->parent)
- parent->childCount -= childCount;
-
- endRemoveRows();
- }
-
- item.expanded = false;
- Q_EMIT dataChanged(index(row), index(row));
-}
-
-int ProjectFileSystemModel::modelIndexRow(const QModelIndex &modelIndex) const
-{
- auto it = std::find_if(
- std::begin(m_items),
- std::end(m_items),
- [&modelIndex](const TreeItem &item)
- {
- return item.index == modelIndex;
- });
-
- return it != std::end(m_items) ? std::distance(std::begin(m_items), it) : -1;
-}
-
-bool ProjectFileSystemModel::isExpanded(const QModelIndex &modelIndex) const
-{
- if (modelIndex == m_rootIndex)
- return true;
- const int row = modelIndexRow(modelIndex);
- return row != -1 && m_items.at(row).expanded;
-}
-
-EStudioObjectType ProjectFileSystemModel::getIconType(const QString &path) const
-{
- return Q3DStudio::ImportUtils::GetObjectFileTypeForFile(path).m_IconType;
-}
-
-QString ProjectFileSystemModel::getIconName(const QString &path) const
-{
- QString iconName;
-
- bool referenced = m_references.contains(path);
-
- QFileInfo fileInfo(path);
- if (fileInfo.isFile()) {
- EStudioObjectType type = getIconType(path);
-
- if (type == OBJTYPE_PRESENTATION) {
- const bool isCurrent = isCurrentPresentation(path);
- const bool isInitial = isInitialPresentation(path);
- if (isInitial) {
- iconName = isCurrent ? QStringLiteral("initial_used.png")
- : QStringLiteral("initial_notUsed.png");
- } else if (isCurrent) {
- iconName = QStringLiteral("presentation_edit.png");
- }
- }
-
- if (iconName.isEmpty()) {
- if (type != OBJTYPE_UNKNOWN) {
- iconName = referenced ? CStudioObjectTypes::GetNormalIconName(type)
- : CStudioObjectTypes::GetDisabledIconName(type);
- } else {
- iconName = referenced ? QStringLiteral("Objects-Layer-Normal.png")
- : QStringLiteral("Objects-Layer-Disabled.png");
- }
- }
- } else {
- iconName = referenced ? QStringLiteral("Objects-Folder-Normal.png")
- : QStringLiteral("Objects-Folder-Disabled.png");
- }
-
- return iconName;
-}
-
-bool ProjectFileSystemModel::hasVisibleChildren(const QModelIndex &modelIndex) const
-{
- const QDir dir(modelIndex.data(QFileSystemModel::FilePathRole).toString());
- if (!dir.exists() || dir.isEmpty())
- return false;
-
- const auto fileInfoList = dir.entryInfoList(QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot);
- for (const auto &fileInfo : fileInfoList) {
- if (fileInfo.isDir() || getIconType(fileInfo.filePath()) != OBJTYPE_UNKNOWN)
- return true;
- }
-
- return false;
-}
-
-bool ProjectFileSystemModel::isVisible(const QModelIndex &modelIndex) const
-{
- QString path = modelIndex.data(QFileSystemModel::FilePathRole).toString();
-
- if (modelIndex == m_rootIndex || QFileInfo(path).isDir())
- return true;
-
- if (path.endsWith(QLatin1String("_autosave.uip"))
- || path.endsWith(QLatin1String("_@preview@.uip"))
- || path.endsWith(QLatin1String(".uia"))) {
- return false;
- }
-
- return getIconType(path) != OBJTYPE_UNKNOWN;
-}
-
-void ProjectFileSystemModel::modelRowsInserted(const QModelIndex &parent, int start, int end)
-{
- if (!m_rootIndex.isValid())
- return;
-
- if (isExpanded(parent)) {
- showModelChildItems(parent, start, end);
- } else {
- if (hasVisibleChildren(parent)) {
- // show expand arrow
- const int row = modelIndexRow(parent);
- Q_EMIT dataChanged(index(row), index(row));
- }
- }
-}
-
-void ProjectFileSystemModel::modelRowsRemoved(const QModelIndex &parent, int start, int end)
-{
- if (!m_rootIndex.isValid())
- return;
-
- if (isExpanded(parent)) {
- for (int i = start; i <= end; ++i) {
- const int row = modelIndexRow(m_model->index(i, 0, parent));
-
- if (row != -1) {
- const auto &item = m_items.at(row);
-
- beginRemoveRows({}, row, row + item.childCount);
-
- for (auto parent = item.parent; parent != nullptr; parent = parent->parent)
- parent->childCount -= 1 + item.childCount;
-
- m_items.erase(std::begin(m_items) + row,
- std::begin(m_items) + row + item.childCount + 1);
-
- endRemoveRows();
- }
- }
- }
-
- if (!hasVisibleChildren(parent)) {
- // collapse the now empty folder
- const int row = modelIndexRow(parent);
- if (m_items[row].expanded)
- collapse(row);
- else
- Q_EMIT dataChanged(index(row), index(row));
- }
-}
-
-void ProjectFileSystemModel::modelLayoutChanged()
-{
- if (!m_rootIndex.isValid())
- return;
-
- QSet<QPersistentModelIndex> expandedItems;
- for (const auto &item : m_items) {
- if (item.expanded)
- expandedItems.insert(item.index);
- }
-
- const std::function<int(const QModelIndex &, TreeItem *)> insertChildren = [this, &expandedItems, &insertChildren](const QModelIndex &parentIndex, TreeItem *parent)
- {
- Q_ASSERT(isVisible(parentIndex));
-
- const int rowCount = m_model->rowCount(parentIndex);
- const int depth = parent->depth + 1;
-
- int childCount = 0;
-
- for (int i = 0; i < rowCount; ++i) {
- const auto &childIndex = m_model->index(i, 0, parentIndex);
- if (isVisible(childIndex)) {
- const bool expanded = expandedItems.contains(childIndex);
- m_items.append({ childIndex, depth, expanded, parent, 0 });
- auto &item = m_items.last();
- if (expanded) {
- item.childCount = insertChildren(childIndex, &item);
- childCount += item.childCount;
- }
- ++childCount;
- }
- }
-
- return childCount;
- };
-
- const int itemCount = m_items.count();
-
- m_items.erase(std::begin(m_items) + 1, std::end(m_items));
- m_items.reserve(itemCount);
- insertChildren(m_rootIndex, &m_items.first());
-
- Q_ASSERT(m_items.count() == itemCount);
-
- Q_EMIT dataChanged(index(0), index(itemCount - 1));
-}
-
-void ProjectFileSystemModel::updateDefaultDirMap()
-{
- if (m_defaultDirToAbsPathMap.isEmpty()) {
- m_defaultDirToAbsPathMap.insert(QStringLiteral("effects"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("fonts"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("maps"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("materials"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("models"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("scripts"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("presentations"), QString());
- m_defaultDirToAbsPathMap.insert(QStringLiteral("qml"), QString());
- }
-
- const QString rootPath = m_items[0].index.data(QFileSystemModel::FilePathRole).toString();
- const QStringList keys = m_defaultDirToAbsPathMap.keys();
- for (const QString &key : keys) {
- QString currentValue = m_defaultDirToAbsPathMap[key];
- if (currentValue.isEmpty()) {
- const QString defaultPath = rootPath + QLatin1Char('/') + key;
- const QFileInfo fi(defaultPath);
- if (fi.exists() && fi.isDir())
- m_defaultDirToAbsPathMap.insert(key, defaultPath);
- } else {
- const QFileInfo fi(currentValue);
- if (!fi.exists())
- m_defaultDirToAbsPathMap.insert(key, QString());
- }
- }
-}
-
-void ProjectFileSystemModel::addPathsToReferences(QSet<QString> &references,
- const QString &projectPath,
- const QString &origPath)
-{
- references.insert(origPath);
- QString path = origPath;
- QString parentPath = QFileInfo(path).path();
- do {
- references.insert(path);
- path = parentPath;
- parentPath = QFileInfo(path).path();
- } while (path != projectPath && parentPath != path);
-}
-
-void ProjectFileSystemModel::handlePresentationIdChange(const QString &path, const QString &id)
-{
- const QString cleanPath = QDir::cleanPath(
- QDir(g_StudioApp.GetCore()->GetDoc()->GetCore()->getProjectFile()
- .getProjectPath()).absoluteFilePath(path));
- int row = rowForPath(cleanPath);
- m_projectReferencesUpdateMap.insert(cleanPath, true);
- m_projectReferencesUpdateTimer.start();
- updateRoles({FileIdRole, ExtraIconRole}, row, row);
-}
-
-void ProjectFileSystemModel::asyncExpandPresentations()
-{
- disconnect(this, &ProjectFileSystemModel::dataChanged,
- this, &ProjectFileSystemModel::asyncExpandPresentations);
-
- // expand presentation folder by default (if it exists).
- QTimer::singleShot(0, [this]() {
- QString path = g_StudioApp.GetCore()->getProjectFile().getProjectPath()
- + QStringLiteral("/presentations");
- expand(rowForPath(path));
- });
-}
-
-void ProjectFileSystemModel::asyncUpdateReferences()
-{
- QTimer::singleShot(0, this, &ProjectFileSystemModel::updateReferences);
-}
-
-void ProjectFileSystemModel::onFilesChanged(
- const Q3DStudio::TFileModificationList &inFileModificationList)
-{
- // If any presentation file changes, update asset reference caches
- for (size_t idx = 0, end = inFileModificationList.size(); idx < end; ++idx) {
- const Q3DStudio::SFileModificationRecord &record(inFileModificationList[idx]);
- if (record.m_File.isFile()) {
- const QString suffix = record.m_File.suffix();
- const bool isQml = CDialogs::qmlStreamExtensions().contains(suffix);
- if (isQml || CDialogs::presentationExtensions().contains(suffix)
- || CDialogs::materialExtensions().contains(suffix)
- || CDialogs::effectExtensions().contains(suffix)) {
- const QString filePath = record.m_File.absoluteFilePath();
- if (record.m_ModificationType == Q3DStudio::FileModificationType::Created
- || record.m_ModificationType == Q3DStudio::FileModificationType::Modified) {
- if (isQml && !g_StudioApp.isQmlStream(filePath))
- continue; // Skip non-stream qml's to match import logic
- m_projectReferencesUpdateMap.insert(filePath, true);
- } else if (record.m_ModificationType
- == Q3DStudio::FileModificationType::Destroyed) {
- m_projectReferencesUpdateMap.insert(filePath, false);
- }
- m_projectReferencesUpdateTimer.start();
- }
- }
- }
-}
-
-bool ProjectFileSystemModel::isCurrentPresentation(const QString &path) const
-{
- return path == g_StudioApp.GetCore()->GetDoc()->GetDocumentPath();
-}
-
-bool ProjectFileSystemModel::isInitialPresentation(const QString &path) const
-{
- QString checkId = presentationId(path);
-
- return !checkId.isEmpty()
- && checkId == g_StudioApp.GetCore()->getProjectFile().initialPresentation();
-}
-
-QString ProjectFileSystemModel::presentationId(const QString &path) const
-{
- QString presId;
- if (isCurrentPresentation(path))
- presId = g_StudioApp.GetCore()->GetDoc()->getPresentationId();
- else
- presId = g_StudioApp.getRenderableId(QFileInfo(path).absoluteFilePath());
-
- return presId;
-}