Sun, 24 Oct 2021 11:33:32 +0300
update
24 | 1 | /* |
2 | * LDForge: LDraw parts authoring CAD | |
3 | * Copyright (C) 2013 - 2020 Teemu Piippo | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
19 | #include <QFile> |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
20 | #include <QDir> |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
21 | #include <QFileInfo> |
3 | 22 | #include "documentmanager.h" |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
23 | #include "modeleditcontext.h" |
14 | 24 | #include "linetypes/comment.h" |
147 | 25 | #include "linetypes/subfilereference.h" |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
26 | #include "parser.h" |
3 | 27 | |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
28 | /** |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
29 | * @brief Constructs a new document manager |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
30 | * @param parent Parent object |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
31 | */ |
5 | 32 | DocumentManager::DocumentManager(QObject* parent) : |
33 | QObject{parent} | |
34 | { | |
35 | } | |
36 | ||
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
37 | /** |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
38 | * @brief Creates a new model. |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
39 | * @returns the name to the new model |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
40 | */ |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
41 | QString DocumentManager::newModel() |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
42 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
43 | const QString name = makeNewModelName(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
44 | this->openModels.emplace(name, new Model); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
45 | return name; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
46 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
47 | |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
48 | /** |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
49 | * @brief Looks for a model by name |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
50 | * @param name Name of the model |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
51 | * @returns model or null |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
52 | * ' |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
53 | */ |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
54 | Model* DocumentManager::findModelByName(const QString& name) |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
55 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
56 | const auto iterator = this->openModels.find(name); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
57 | if (iterator == std::end(this->openModels)) |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
58 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
59 | return nullptr; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
60 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
61 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
62 | { |
147 | 63 | return iterator->second.model.get(); |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
64 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
65 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
66 | |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
67 | QString pathToName(const QFileInfo& path) |
3 | 68 | { |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
69 | static const char* paths[] = { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
70 | "s", |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
71 | "48" |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
72 | "8" |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
73 | }; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
74 | const QString baseName = path.fileName(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
75 | const QString dirName = QFileInfo{path.dir().path()}.fileName(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
76 | QString result; |
17 | 77 | if (std::find(std::begin(paths), std::end(paths), dirName) != std::end(paths)) |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
78 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
79 | result = dirName + "\\" + baseName; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
80 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
81 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
82 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
83 | result = baseName; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
84 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
85 | return result; |
3 | 86 | } |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
87 | |
147 | 88 | /** |
89 | * @brief Tries to open the model at the specified path | |
90 | * @param path Path to the model to open | |
91 | * @param errorStream Where to write any errors | |
92 | * @param openType rationale behind opening this file | |
93 | * @returns file name or "" on error | |
94 | */ | |
95 | QString DocumentManager::openModel(const QString& path, QTextStream& errorStream, const OpenType openType) | |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
96 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
97 | QFile file{path}; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
98 | const QString name = pathToName(path); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
99 | file.open(QFile::ReadOnly | QFile::Text); |
140 | 100 | std::unique_ptr<Model> newModel = std::make_unique<Model>(path); |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
101 | QTextStream textStream{&file}; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
102 | Model::EditContext editor = newModel->edit(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
103 | Parser parser{file}; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
104 | parser.parseBody(editor); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
105 | QString result; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
106 | if (file.error() == QFile::NoError) |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
107 | { |
147 | 108 | openModels[name] = {std::move(newModel), openType}; |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
109 | result = name; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
110 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
111 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
112 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
113 | errorStream << file.errorString(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
114 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
115 | return result; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
116 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
117 | |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
118 | QString DocumentManager::makeNewModelName() |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
119 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
120 | untitledNameCounter += 1; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
121 | return "untitled-" + QString::number(untitledNameCounter); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
122 | } |
12 | 123 | |
124 | void DocumentManager::loadDependenciesForModel( | |
125 | const QString& modelName, | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
126 | const QString& path, |
12 | 127 | const LibraryManager& libraries, |
128 | QTextStream& errorStream) | |
129 | { | |
130 | QStringList missing; | |
131 | QStringList processed; | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
132 | loadDependenciesForModel(modelName, path, libraries, missing, processed, errorStream); |
12 | 133 | if (not missing.empty()) |
134 | { | |
135 | missing.sort(Qt::CaseInsensitive); | |
136 | errorStream << utility::format( | |
137 | "The following files could not be opened: %1", | |
138 | missing.join(", ")); | |
139 | } | |
140 | } | |
141 | ||
147 | 142 | void DocumentManager::closeDocument(const QString &name) |
143 | { | |
144 | const auto& it = this->openModels.find(name); | |
145 | if (it != this->openModels.end()) | |
146 | { | |
147 | this->openModels.erase(it); | |
148 | } | |
149 | QSet<QString> referenced; | |
150 | for (const auto& it : this->openModels) | |
151 | { | |
152 | if (it.second.opentype == OpenType::ManuallyOpened) | |
153 | { | |
154 | this->collectReferences(referenced, it.first, it.second.model.get()); | |
155 | } | |
156 | } | |
157 | ||
158 | } | |
159 | ||
160 | void DocumentManager::collectReferences(QSet<QString>& referenced, const QString &name, const Model *model) | |
161 | { | |
162 | if (not referenced.contains(name)) | |
163 | { | |
164 | referenced.insert(name); | |
165 | model->apply<ldraw::SubfileReference>([&](const ldraw::SubfileReference* referenceObject) | |
166 | { | |
167 | const ldraw::id_t id = referenceObject->id; | |
168 | const QString& referenceName = model->getObjectProperty<ldraw::Property::ReferenceName>(id); | |
169 | auto it = this->openModels.find(referenceName); | |
170 | if (it != this->openModels.end()) | |
171 | { | |
172 | const Model* const model = it->second.model.get(); | |
173 | this->collectReferences(referenced, referenceName, model); | |
174 | } | |
175 | }); | |
176 | } | |
177 | } | |
178 | ||
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
179 | static QString findFile(QString referenceName, const QString& path, const LibraryManager& libraries) |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
180 | { |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
181 | // Try to find the file in the same place as the model itself |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
182 | referenceName.replace("\\", "/"); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
183 | const QDir dir = QFileInfo{path}.dir(); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
184 | QString referencedFilePath = dir.filePath(referenceName); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
185 | if (not QFileInfo{referencedFilePath}.exists()) |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
186 | { |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
187 | // Look for it in the libraries |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
188 | referencedFilePath = libraries.findFile(referenceName); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
189 | } |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
190 | return referencedFilePath; |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
191 | } |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
192 | |
12 | 193 | void DocumentManager::loadDependenciesForModel( |
194 | const QString& modelName, | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
195 | const QString &path, |
12 | 196 | const LibraryManager& libraries, |
197 | QStringList& missing, | |
198 | QStringList& processed, | |
199 | QTextStream& errorStream) | |
200 | { | |
201 | struct LoadingError | |
202 | { | |
203 | QString message; | |
204 | }; | |
205 | processed.append(modelName); | |
206 | Model* model = this->findModelByName(modelName); | |
207 | for (int i = 0; i < model->size(); i += 1) | |
208 | { | |
35
98906a94732f
renamed the linetypes namespace to ldraw namespace and added more structures to it
Teemu Piippo <teemu@hecknology.net>
parents:
24
diff
changeset
|
209 | const QString referenceName = model->getObjectProperty(i, ldraw::Property::ReferenceName).toString(); |
12 | 210 | if (not referenceName.isEmpty() |
211 | and openModels.find(referenceName) == std::end(openModels) | |
212 | and not missing.contains(referenceName)) | |
213 | { | |
214 | try | |
215 | { | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
216 | const QString referencedFilePath = findFile(referenceName, path, libraries); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
217 | if (referencedFilePath.isEmpty()) |
12 | 218 | { |
219 | throw LoadingError{utility::format("'%1' was not found.", referenceName)}; | |
220 | } | |
221 | QString errorString; | |
222 | QTextStream localErrorStream{&errorString}; | |
147 | 223 | QString resultName = this->openModel( |
224 | referencedFilePath, | |
225 | localErrorStream, | |
226 | OpenType::AutomaticallyOpened); | |
12 | 227 | if (resultName.isEmpty()) |
228 | { | |
229 | throw LoadingError{utility::format( | |
230 | "could not load '%1': %2", | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
231 | referencedFilePath, |
12 | 232 | errorString)}; |
233 | } | |
234 | if (not processed.contains(referenceName)) | |
235 | { | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
236 | loadDependenciesForModel(referenceName, path, libraries, missing, processed, errorStream); |
12 | 237 | } |
238 | } | |
239 | catch(const LoadingError& error) | |
240 | { | |
241 | errorStream << error.message << "\n"; | |
242 | missing.append(referenceName); | |
243 | processed.append(referenceName); | |
244 | } | |
245 | } | |
246 | } | |
247 | } |