1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "addnodemodel.h"
6#include "nodesmodel.h"
7#include "effectmanager.h"
8#include <QDir>
9
10bool operator==(const AddNodeModel::NodeData &a, const AddNodeModel::NodeData &b) noexcept
11{
12 return a.name == b.name;
13}
14
15AddNodeModel::AddNodeModel(QObject *effectManager)
16 : QAbstractListModel(effectManager)
17{
18 m_effectManager = static_cast<EffectManager *>(effectManager);
19 connect(sender: this, signal: &QAbstractListModel::modelReset, context: this, slot: &AddNodeModel::rowCountChanged);
20 updateNodesList();
21}
22
23int AddNodeModel::rowCount(const QModelIndex &) const
24{
25 return m_modelList.size();
26}
27
28QHash<int, QByteArray> AddNodeModel::roleNames() const
29{
30 QHash<int, QByteArray> roles;
31 roles[Name] = "name";
32 roles[Description] = "description";
33 roles[File] = "file";
34 roles[Group] = "group";
35 roles[Properties] = "properties";
36 roles[CanBeAdded] = "canBeAdded";
37 roles[Show] = "show";
38 roles[RequiredNodes] = "requires";
39 return roles;
40}
41
42QVariant AddNodeModel::data(const QModelIndex &index, int role) const
43{
44 if (!index.isValid())
45 return QVariant();
46
47 if (index.row() >= m_modelList.size())
48 return false;
49
50 const auto &node = (m_modelList)[index.row()];
51
52 if (role == Name)
53 return QVariant::fromValue(value: node.name);
54 else if (role == Description)
55 return QVariant::fromValue(value: node.description);
56 else if (role == File)
57 return QVariant::fromValue(value: node.file);
58 else if (role == Group)
59 return QVariant::fromValue(value: node.group);
60 else if (role == Properties)
61 return QVariant::fromValue(value: node.properties);
62 else if (role == CanBeAdded)
63 return QVariant::fromValue(value: node.canBeAdded);
64 else if (role == Show)
65 return QVariant::fromValue(value: node.show);
66 else if (role == RequiredNodes)
67 return QVariant::fromValue(value: node.requiredNodes.join(sep: ", "));
68
69 return QVariant();
70}
71
72void AddNodeModel::loadNodesFromPath(const QString &path) {
73 qInfo() << "Loading nodes from:" << path;
74 QList<NodeData> nodes;
75
76 QDir rootDirectory(path);
77 QStringList dirList;
78 dirList << path;
79 // List subdirectories, in our preferred order
80 QStringList subDirList = rootDirectory.entryList(filters: QDir::AllDirs | QDir::NoDotAndDotDot, sort: QDir::Name);
81 for (auto &subDir : subDirList) {
82 QString dirPath = (path + "/" + subDir);
83 // Trick to get "common" nodes first in list
84 if (subDir == "common")
85 dirList.prepend(t: dirPath);
86 else
87 dirList.append(t: dirPath);
88 }
89 // Load nodes from all subdirectories
90 for (const auto &dirPath : dirList) {
91 QDir directory(dirPath);
92 QStringList nodeList = directory.entryList(nameFilters: QStringList() << "*.qen" << "*.QEN", filters: QDir::Files, sort: QDir::Name);
93 for (auto &filename : nodeList) {
94 QString filePath = directory.path() + "/" + filename;
95 auto node = m_effectManager->loadEffectNode(filename: filePath);
96 if (!node.name.isEmpty()) {
97 NodeData data;
98 data.file = filePath;
99 data.name = node.name;
100 if (!node.jsonUniforms.isEmpty()) {
101 for (const auto &u : node.jsonUniforms) {
102 NodeDataProperty property;
103 property.m_name = u.name;
104 if (u.type == UniformModel::Uniform::Type::Define)
105 property.m_type = QStringLiteral("define");
106 else
107 property.m_type = UniformModel::typeToProperty(type: u.type);
108 QVariant varProperty;
109 varProperty.setValue(property);
110 data.properties << varProperty;
111 }
112 }
113 data.description = node.description;
114 data.group = directory.dirName();
115 // Seek through code to get tags
116 QStringList shaderCodeLines;
117 shaderCodeLines += node.vertexCode.split(sep: '\n');
118 shaderCodeLines += node.fragmentCode.split(sep: '\n');
119 for (const auto &codeLine : shaderCodeLines) {
120 QString trimmedLine = codeLine.trimmed();
121 if (trimmedLine.startsWith(s: "@requires")) {
122 // Get the required node, remove "@requires"
123 QString l = trimmedLine.sliced(pos: 9).trimmed();
124 QString nodeName = l.split(sep: ' ').first();
125 if (!nodeName.isEmpty() && !data.requiredNodes.contains(str: nodeName))
126 data.requiredNodes << nodeName;
127 }
128 }
129 if (!m_modelList.contains(t: data))
130 nodes << data;
131 }
132 }
133 }
134
135 // Add all nodes into model
136 for (const auto &node : nodes)
137 m_modelList << node;
138}
139
140void AddNodeModel::updateCanBeAdded(const QStringList &propertyNames)
141{
142 beginResetModel();
143 // Check which nodes contain overlapping properties
144 for (auto &nodeData : m_modelList) {
145 for (const auto &variant : nodeData.properties) {
146 NodeDataProperty property = variant.value<NodeDataProperty>();
147 if (propertyNames.contains(str: property.m_name)) {
148 nodeData.canBeAdded = false;
149 } else {
150 nodeData.canBeAdded = true;
151 }
152 }
153 }
154 endResetModel();
155}
156
157void AddNodeModel::updateShowHide(const QString &groupName, bool show)
158{
159 int i = 0;
160 for (auto &nodeData : m_modelList) {
161 if (nodeData.group == groupName) {
162 nodeData.show = show;
163 Q_EMIT dataChanged(topLeft: QAbstractItemModel::createIndex(arow: 0, acolumn: 0),
164 bottomRight: QAbstractItemModel::createIndex(arow: i, acolumn: 0));
165 }
166 i++;
167 }
168}
169
170void AddNodeModel::updateNodesList()
171{
172 beginResetModel();
173
174 m_modelList.clear();
175
176 QString defaultNodePath = m_effectManager->settings()->defaultResourcePath() + "/defaultnodes";
177 loadNodesFromPath(path: defaultNodePath);
178
179 auto customNodesPaths = m_effectManager->settings()->customNodesPaths();
180 for (const auto &nodesPath : std::as_const(t&: customNodesPaths))
181 loadNodesFromPath(path: nodesPath);
182
183 static QString envCustomNodePath = qEnvironmentVariable(varName: "QQEM_CUSTOM_NODES_PATH");
184 if (!envCustomNodePath.isEmpty())
185 loadNodesFromPath(path: envCustomNodePath);
186
187 endResetModel();
188}
189

source code of qtquickeffectmaker/tools/qqem/addnodemodel.cpp