253 }); |
254 }); |
254 return QObject::tr("The following files could not be opened: %1") |
255 return QObject::tr("The following files could not be opened: %1") |
255 .arg(missingString); |
256 .arg(missingString); |
256 } |
257 } |
257 |
258 |
258 /** |
259 template<typename T, typename K> |
259 * @brief Cleans up and erases models that are no longer required. |
260 void removeFromSet(std::set<T>& set, K&& valueToRemove) |
260 */ |
261 { |
|
262 const auto it = std::lower_bound(set.begin(), set.end(), valueToRemove); |
|
263 if (it != set.end() and *it == valueToRemove) { |
|
264 set.erase(it); |
|
265 } |
|
266 } |
|
267 |
|
268 //! @brief Cleans up and erases models that are no longer required. |
261 void DocumentManager::prune() |
269 void DocumentManager::prune() |
262 { |
270 { |
263 for (auto it = this->openModels.begin(); it != this->openModels.end(); ++it) |
271 Graph<ModelId> dependencyGraph; |
264 { |
272 forValueInMap(this->openModels, [&dependencyGraph](const ModelInfo& info) { |
265 // Find models that are not edited by the user and are not needed by any other model |
273 forValueInMap(info.dependencies, [&dependencyGraph, &info](ModelId dep){ |
266 if (true |
274 dependencyGraph.push_back({.from = info.id, .to = dep}); |
267 and it->second.opentype == OpenType::AutomaticallyOpened |
275 }); |
268 and not this->isReferencedByAnything(it->first) |
276 }); |
269 ) { |
277 std::set<ModelId> autoOpened; |
270 // Remove the model |
278 forValueInMap(this->openModels, [&autoOpened](const ModelInfo& info) { |
271 this->openModels.erase(it); |
279 if (info.opentype == OpenType::AutomaticallyOpened) { |
272 // We need to start over now. It is possible that other models that |
280 autoOpened.insert(info.id); |
273 // previously were referenced by the model we just erased have |
281 } |
274 // become prunable. Moreover, our iterator is invalid now and we |
282 }); |
275 // cannot continue in this loop. |
283 bool repeat = true; |
276 this->prune(); |
284 while (repeat) { |
277 break; |
285 repeat = false; |
278 } |
286 std::set<ModelId> prunable = autoOpened; |
279 } |
287 for (const auto& pair : dependencyGraph) { |
280 } |
288 removeFromSet(prunable, pair.to); |
281 |
289 } |
282 /** |
290 for (ModelId idToPrune : prunable) { |
283 * @brief Finds out whether the specified model id is referenced by any other model |
291 auto it = this->openModels.find(idToPrune); |
284 * @param modelId |
292 if (it != this->openModels.end()) { |
285 * @returns bool |
293 this->openModels.erase(it); |
286 */ |
294 } |
287 bool DocumentManager::isReferencedByAnything(const ModelId modelId) const |
295 removeFromSet(autoOpened, idToPrune); |
288 { |
296 std::erase_if(dependencyGraph, [&idToPrune](const GraphEdge<ModelId>& edge) { |
289 for (auto& haystackModelPair : this->openModels) |
297 return edge.from == idToPrune; |
290 { |
298 }); |
291 if (haystackModelPair.first != modelId) |
299 repeat = true; |
292 { |
300 } |
293 for (auto& dependencyPair : haystackModelPair.second.dependencies) |
301 } |
294 { |
|
295 if (dependencyPair.second == modelId) |
|
296 { |
|
297 return true; |
|
298 } |
|
299 } |
|
300 } |
|
301 } |
|
302 return false; |
|
303 } |
302 } |
304 |
303 |
305 void DocumentManager::makePolygonCacheForModel(const ModelId modelId) |
304 void DocumentManager::makePolygonCacheForModel(const ModelId modelId) |
306 { |
305 { |
307 Model* model = this->getModelById(modelId); |
306 Model* model = this->getModelById(modelId); |