Wed, 25 May 2022 20:36:34 +0300
Fix pick() picking from weird places on the screen with high DPI scaling
glReadPixels reads data from the frame buffer, which contains data after
high DPI scaling, so any reads to that need to take this scaling into account
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> |
148 | 22 | #include <QSaveFile> |
3 | 23 | #include "documentmanager.h" |
153
2f79053c2e9a
Renamed modeleditcontext.cpp -> modeleditor.cpp
Teemu Piippo <teemu@hecknology.net>
parents:
152
diff
changeset
|
24 | #include "modeleditor.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. |
148 | 39 | * @returns the ID of the new model |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
40 | */ |
148 | 41 | ModelId DocumentManager::newModel() |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
42 | { |
148 | 43 | const ModelId modelId{++this->modelIdCounter}; |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
44 | const QString name = makeNewModelName(); |
148 | 45 | this->openModels[modelId] = ModelInfo{ |
46 | .model = std::make_unique<Model>(), | |
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
47 | .id = modelId, |
148 | 48 | .opentype = OpenType::ManuallyOpened, |
49 | }; | |
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
50 | this->makePolygonCacheForModel(modelId); |
148 | 51 | return modelId; |
8
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 | /** |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
55 | * @brief Looks for a model by name |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
56 | * @param name Name of the model |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
57 | * @returns model or null |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
58 | */ |
148 | 59 | Model* DocumentManager::findDependencyByName(const ModelId modelId, const QString& name) |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
60 | { |
148 | 61 | const auto modelsIterator = this->openModels.find(modelId); |
62 | if (modelsIterator != std::end(this->openModels)) | |
63 | { | |
64 | const auto& dependencies = modelsIterator->second.dependencies; | |
65 | const auto dependenciesIterator = dependencies.find(name); | |
66 | if (dependenciesIterator != dependencies.end()) | |
67 | { | |
68 | ModelInfo& modelInfo = this->openModels[dependenciesIterator->second]; | |
69 | return modelInfo.model.get(); | |
70 | } | |
71 | else | |
72 | { | |
73 | return nullptr; | |
74 | } | |
75 | } | |
76 | else | |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
77 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
78 | return nullptr; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
79 | } |
148 | 80 | } |
81 | ||
82 | /** | |
83 | * @brief Gets a model pointer by id or nullptr if not found | |
84 | * @param modelId id of model to find | |
85 | * @returns model pointer or null | |
86 | */ | |
87 | Model *DocumentManager::getModelById(ModelId modelId) | |
88 | { | |
89 | const auto iterator = this->openModels.find(modelId); | |
90 | if (iterator != this->openModels.end()) | |
91 | { | |
92 | return iterator->second.model.get(); | |
93 | } | |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
94 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
95 | { |
148 | 96 | return nullptr; |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
97 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
98 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
99 | |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
100 | QString pathToName(const QFileInfo& path) |
3 | 101 | { |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
102 | static const char* paths[] = { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
103 | "s", |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
104 | "48" |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
105 | "8" |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
106 | }; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
107 | const QString baseName = path.fileName(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
108 | const QString dirName = QFileInfo{path.dir().path()}.fileName(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
109 | QString result; |
17 | 110 | 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
|
111 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
112 | result = dirName + "\\" + baseName; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
113 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
114 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
115 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
116 | result = baseName; |
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 | return result; |
3 | 119 | } |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
120 | |
147 | 121 | /** |
122 | * @brief Tries to open the model at the specified path | |
123 | * @param path Path to the model to open | |
124 | * @param errorStream Where to write any errors | |
125 | * @param openType rationale behind opening this file | |
148 | 126 | * @returns model id, or no value on error |
147 | 127 | */ |
148 | 128 | std::optional<ModelId> DocumentManager::openModel( |
129 | const QString& path, | |
130 | QTextStream& errorStream, | |
131 | const OpenType openType | |
132 | ) { | |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
133 | QFile file{path}; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
134 | const QString name = pathToName(path); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
135 | file.open(QFile::ReadOnly | QFile::Text); |
148 | 136 | std::unique_ptr<Model> newModel = std::make_unique<Model>(this); |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
137 | QTextStream textStream{&file}; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
138 | Parser parser{file}; |
152 | 139 | parser.parseBody(*newModel); |
148 | 140 | std::optional<ModelId> result; |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
141 | if (file.error() == QFile::NoError) |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
142 | { |
148 | 143 | const ModelId modelId{++this->modelIdCounter}; |
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
144 | this->openModels[modelId] = {std::move(newModel), modelId, path, openType}; |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
145 | this->makePolygonCacheForModel(modelId); |
148 | 146 | result = modelId; |
8
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
147 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
148 | else |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
149 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
150 | errorStream << file.errorString(); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
151 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
152 | return result; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
153 | } |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
154 | |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
155 | QString DocumentManager::makeNewModelName() |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
156 | { |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
157 | untitledNameCounter += 1; |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
158 | return "untitled-" + QString::number(untitledNameCounter); |
44679e468ba9
major update with many things
Teemu Piippo <teemu@hecknology.net>
parents:
5
diff
changeset
|
159 | } |
12 | 160 | |
148 | 161 | void DocumentManager::loadDependenciesForAllModels(const LibraryManager& libraries, QTextStream& errorStream) |
162 | { | |
163 | for (const auto& modelInfoPair : this->openModels) | |
164 | { | |
165 | this->loadDependenciesForModel(modelInfoPair.first, modelInfoPair.second.path, libraries, errorStream); | |
166 | } | |
167 | } | |
168 | ||
169 | struct DocumentManager::LoadDepedenciesBag | |
170 | { | |
171 | const LibraryManager& libraries; | |
172 | QStringList missing; | |
173 | QSet<ModelId> processed; | |
174 | QTextStream& errorStream; | |
175 | }; | |
176 | ||
12 | 177 | void DocumentManager::loadDependenciesForModel( |
148 | 178 | const ModelId modelId, |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
179 | const QString& path, |
12 | 180 | const LibraryManager& libraries, |
181 | QTextStream& errorStream) | |
182 | { | |
148 | 183 | LoadDepedenciesBag bag { |
184 | .libraries = libraries, | |
185 | .missing = {}, | |
186 | .processed = {}, | |
187 | .errorStream = errorStream, | |
188 | }; | |
189 | this->loadDependenciesForModel(modelId, path, bag); | |
190 | if (not bag.missing.empty()) | |
12 | 191 | { |
148 | 192 | bag.missing.sort(Qt::CaseInsensitive); |
12 | 193 | errorStream << utility::format( |
194 | "The following files could not be opened: %1", | |
148 | 195 | bag.missing.join(", ")); |
196 | } | |
197 | } | |
198 | ||
199 | void DocumentManager::closeDocument(const ModelId modelId) | |
200 | { | |
201 | ModelInfo* modelInfo = findInMap(this->openModels, modelId); | |
202 | if (modelInfo != nullptr) | |
203 | { | |
204 | modelInfo->opentype = OpenType::AutomaticallyOpened; | |
205 | this->prune(); | |
206 | } | |
207 | } | |
208 | ||
209 | const QString *DocumentManager::modelPath(ModelId modelId) const | |
210 | { | |
211 | const auto iterator = this->openModels.find(modelId); | |
212 | if (iterator != this->openModels.end()) | |
213 | { | |
214 | return &iterator->second.path; | |
215 | } | |
216 | else | |
217 | { | |
218 | return nullptr; | |
219 | } | |
220 | } | |
221 | ||
222 | /** | |
223 | * @brief Changes the path of the specified model. Since the name of the file may change, | |
224 | * changing the path can cause dependencies to be resolved differently. As such, dependencies | |
225 | * need to be resolved for all files after this operation. | |
226 | * @param modelId Model to change the path of | |
227 | * @param newPath New path | |
228 | * @param libraries Library manager for the purpose of dependency resolving | |
229 | * @param errorStream Where to write any errors regarding dependency resolving | |
230 | */ | |
231 | void DocumentManager::setModelPath( | |
232 | const ModelId modelId, | |
233 | const QString &newPath, | |
234 | const LibraryManager &libraries, | |
235 | QTextStream &errorStream) | |
236 | { | |
237 | auto modelInfoPair = this->openModels.find(modelId); | |
238 | if (true | |
239 | and modelInfoPair != this->openModels.end() | |
240 | and modelInfoPair->second.opentype == OpenType::ManuallyOpened | |
241 | ) { | |
242 | modelInfoPair->second.path = newPath; | |
243 | this->loadDependenciesForAllModels(libraries, errorStream); | |
12 | 244 | } |
245 | } | |
246 | ||
148 | 247 | bool DocumentManager::saveModel(const ModelId modelId, QTextStream &errors) |
147 | 248 | { |
148 | 249 | const QString* const path = this->modelPath(modelId); |
250 | if (path != nullptr) | |
147 | 251 | { |
148 | 252 | QSaveFile file{*path}; |
253 | file.setDirectWriteFallback(true); | |
254 | if (file.open(QSaveFile::WriteOnly)) | |
147 | 255 | { |
148 | 256 | // if path is not nullptr, getModelById will always return a value as well |
151 | 257 | ::save(*this->getModelById(modelId), &file); |
148 | 258 | const bool commitSucceeded = file.commit(); |
259 | if (not commitSucceeded) | |
260 | { | |
261 | errors << tr("Could not save: %1").arg(file.errorString()); | |
262 | return false; | |
263 | } | |
264 | else | |
265 | { | |
266 | return true; | |
267 | } | |
268 | } | |
269 | else | |
270 | { | |
271 | errors << tr("Could not open %1 for writing: %2") | |
272 | .arg(file.fileName()) | |
273 | .arg(file.errorString()); | |
274 | return false; | |
147 | 275 | } |
276 | } | |
148 | 277 | else |
278 | { | |
279 | errors << tr("Bad model ID %1").arg(modelId.value); | |
280 | return false; | |
281 | } | |
147 | 282 | } |
283 | ||
148 | 284 | /** |
285 | * @brief Searches the open models for the specified model and returns its id if found | |
286 | * @param model model to look for | |
287 | * @return id or no value if not found | |
288 | */ | |
289 | std::optional<ModelId> DocumentManager::findIdForModel(const Model *model) const | |
147 | 290 | { |
148 | 291 | std::optional<ModelId> result; |
292 | for (auto it = this->openModels.begin(); it != this->openModels.end(); ++it) | |
147 | 293 | { |
148 | 294 | if (it->second.model.get() == model) |
147 | 295 | { |
148 | 296 | result = it->first; |
297 | break; | |
298 | } | |
299 | } | |
300 | return result; | |
301 | } | |
302 | ||
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
303 | PolygonCache *DocumentManager::getPolygonCacheForModel(ModelId modelId) |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
304 | { |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
305 | auto it = this->polygonCaches.find(modelId); |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
306 | if (it != this->polygonCaches.end()) |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
307 | { |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
308 | return &it->second; |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
309 | } |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
310 | else |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
311 | { |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
312 | return nullptr; |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
313 | } |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
314 | } |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
315 | |
148 | 316 | /** |
317 | * @brief Cleans up and erases models that are no longer required. | |
318 | */ | |
319 | void DocumentManager::prune() | |
320 | { | |
321 | for (auto it = this->openModels.begin(); it != this->openModels.end(); ++it) | |
322 | { | |
323 | // Find models that are not edited by the user and are not needed by any other model | |
324 | if (true | |
325 | and it->second.opentype == OpenType::AutomaticallyOpened | |
326 | and not this->isReferencedByAnything(it->first) | |
327 | ) { | |
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
328 | // Remove its polygon cache |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
329 | const auto polygonCache = this->polygonCaches.find(it->first); |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
330 | if (polygonCache != this->polygonCaches.end()) |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
331 | { |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
332 | this->polygonCaches.erase(polygonCache); |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
333 | } |
148 | 334 | // Remove the model |
335 | this->openModels.erase(it); | |
336 | // We need to start over now. It is possible that other models that previously | |
337 | // were referenced by the model we just erased have become prunable. | |
338 | // Moreover, our iterator is invalid now and we cannot continue in this for loop. | |
339 | this->prune(); | |
340 | break; | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | /** | |
346 | * @brief Finds out whether the specified model id is referenced by any other model | |
347 | * @param modelId | |
348 | * @returns bool | |
349 | */ | |
350 | bool DocumentManager::isReferencedByAnything(const ModelId modelId) const | |
351 | { | |
352 | for (auto& haystackModelPair : this->openModels) | |
353 | { | |
354 | if (haystackModelPair.first != modelId) | |
355 | { | |
356 | for (auto& dependencyPair : haystackModelPair.second.dependencies) | |
147 | 357 | { |
148 | 358 | if (dependencyPair.second == modelId) |
359 | { | |
360 | return true; | |
361 | } | |
147 | 362 | } |
148 | 363 | } |
147 | 364 | } |
148 | 365 | return false; |
147 | 366 | } |
367 | ||
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
368 | void DocumentManager::makePolygonCacheForModel(const ModelId modelId) |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
369 | { |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
370 | Model* model = this->getModelById(modelId); |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
371 | if (model != nullptr) |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
372 | { |
193 | 373 | this->polygonCaches[modelId] = {}; |
374 | connect(model, &Model::dataChanged, this, &DocumentManager::modelModified); | |
375 | connect(model, &Model::rowsInserted, this, &DocumentManager::modelModified); | |
376 | connect(model, &Model::rowsRemoved, this, &DocumentManager::modelModified); | |
377 | } | |
378 | } | |
379 | ||
380 | void DocumentManager::modelModified() | |
381 | { | |
195 | 382 | Model* const model = qobject_cast<Model*>(this->sender()); |
194 | 383 | const std::optional<ModelId> modelId = this->findIdForModel(model); |
384 | if (modelId.has_value()) { | |
385 | this->polygonCaches[*modelId].needRecache = true; | |
150
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
386 | } |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
387 | } |
b6cbba6e29a1
extract polygon cache out of Model
Teemu Piippo <teemu@hecknology.net>
parents:
148
diff
changeset
|
388 | |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
389 | 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
|
390 | { |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
391 | // 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
|
392 | referenceName.replace("\\", "/"); |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
393 | 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
|
394 | 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
|
395 | 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
|
396 | { |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
397 | // 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
|
398 | 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
|
399 | } |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
400 | return referencedFilePath; |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
401 | } |
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
402 | |
12 | 403 | void DocumentManager::loadDependenciesForModel( |
148 | 404 | const ModelId modelId, |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
405 | const QString &path, |
148 | 406 | LoadDepedenciesBag& bag) |
12 | 407 | { |
148 | 408 | QSet<QString> failedToOpen; |
12 | 409 | struct LoadingError |
410 | { | |
411 | QString message; | |
412 | }; | |
148 | 413 | bag.processed.insert(modelId); |
414 | if (not this->openModels.contains(modelId)) | |
12 | 415 | { |
148 | 416 | bag.errorStream << tr("bad model ID %1").arg(modelId.value); |
417 | return; | |
418 | } | |
419 | ModelInfo& modelInfo = this->openModels[modelId]; | |
420 | modelInfo.dependencies.clear(); | |
421 | for (int i = 0; i < modelInfo.model->size(); i += 1) | |
422 | { | |
151 | 423 | const QString referenceName = (*modelInfo.model)[i]->getProperty(ldraw::Property::ReferenceName).toString(); |
12 | 424 | if (not referenceName.isEmpty() |
148 | 425 | and modelInfo.dependencies.count(referenceName) == 0 |
426 | and not failedToOpen.contains(referenceName)) | |
12 | 427 | { |
428 | try | |
429 | { | |
148 | 430 | const QString referencedFilePath = ::findFile(referenceName, path, bag.libraries); |
23
3387a84ddaba
fixed a pile of nonsense that caused subfiles to go haywire
Teemu Piippo <teemu@hecknology.net>
parents:
17
diff
changeset
|
431 | if (referencedFilePath.isEmpty()) |
12 | 432 | { |
148 | 433 | throw LoadingError{tr("could not find '%1'").arg(referenceName)}; |
12 | 434 | } |
148 | 435 | QString loadErrorString; |
436 | QTextStream localErrorStream{&loadErrorString}; | |
437 | const std::optional<ModelId> modelIdOpt = this->openModel( | |
147 | 438 | referencedFilePath, |
439 | localErrorStream, | |
440 | OpenType::AutomaticallyOpened); | |
148 | 441 | if (not modelIdOpt.has_value()) |
12 | 442 | { |
148 | 443 | const QString& errorMessage = tr("could not load '%1': %2") |
444 | .arg(referencedFilePath) | |
445 | .arg(loadErrorString); | |
446 | throw LoadingError{errorMessage}; | |
12 | 447 | } |
148 | 448 | modelInfo.dependencies[referenceName] = modelIdOpt.value(); |
449 | if (not bag.processed.contains(modelIdOpt.value())) | |
12 | 450 | { |
148 | 451 | this->loadDependenciesForModel(modelIdOpt.value(), referencedFilePath, bag); |
12 | 452 | } |
453 | } | |
454 | catch(const LoadingError& error) | |
455 | { | |
148 | 456 | bag.errorStream << error.message << "\n"; |
457 | failedToOpen.insert(referenceName); | |
458 | bag.missing.append(referenceName); | |
12 | 459 | } |
460 | } | |
461 | } | |
462 | } |