/*************************************************************************** * Copyright (c) 2025 Kindred Systems * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #include #include #include "OriginManager.h" #include "FileOrigin.h" namespace Gui { // Preferences path for origin settings static const char* PREF_PATH = "User parameter:BaseApp/Preferences/General/Origin"; static const char* PREF_CURRENT_ORIGIN = "CurrentOriginId"; // Built-in origin ID that cannot be unregistered static const char* LOCAL_ORIGIN_ID = "local"; OriginManager* OriginManager::_instance = nullptr; OriginManager* OriginManager::instance() { if (!_instance) { _instance = new OriginManager(); } return _instance; } void OriginManager::destruct() { delete _instance; _instance = nullptr; } OriginManager::OriginManager() { ensureLocalOrigin(); loadPreferences(); } OriginManager::~OriginManager() { savePreferences(); _origins.clear(); } void OriginManager::ensureLocalOrigin() { // Create the built-in local filesystem origin auto localOrigin = std::make_unique(); _origins[LOCAL_ORIGIN_ID] = std::move(localOrigin); _currentOriginId = LOCAL_ORIGIN_ID; } void OriginManager::loadPreferences() { try { auto hGrp = App::GetApplication().GetParameterGroupByPath(PREF_PATH); std::string savedOriginId = hGrp->GetASCII(PREF_CURRENT_ORIGIN, LOCAL_ORIGIN_ID); // Only use saved origin if it's registered if (_origins.find(savedOriginId) != _origins.end()) { _currentOriginId = savedOriginId; } } catch (...) { // Ignore preference loading errors _currentOriginId = LOCAL_ORIGIN_ID; } } void OriginManager::savePreferences() { try { auto hGrp = App::GetApplication().GetParameterGroupByPath(PREF_PATH); hGrp->SetASCII(PREF_CURRENT_ORIGIN, _currentOriginId.c_str()); } catch (...) { // Ignore preference saving errors } } bool OriginManager::registerOrigin(FileOrigin* origin) { if (!origin) { return false; } std::string originId = origin->id(); if (originId.empty()) { Base::Console().warning("OriginManager: Cannot register origin with empty ID\n"); delete origin; return false; } // Check if ID already in use if (_origins.find(originId) != _origins.end()) { Base::Console().warning("OriginManager: Origin '%s' already registered\n", originId.c_str()); delete origin; return false; } _origins[originId] = std::unique_ptr(origin); Base::Console().log("OriginManager: Registered origin '%s'\n", originId.c_str()); signalOriginRegistered(originId); return true; } bool OriginManager::unregisterOrigin(const std::string& id) { // Cannot unregister the built-in local origin if (id == LOCAL_ORIGIN_ID) { Base::Console().warning("OriginManager: Cannot unregister built-in local origin\n"); return false; } auto it = _origins.find(id); if (it == _origins.end()) { return false; } // If unregistering the current origin, switch to local if (_currentOriginId == id) { _currentOriginId = LOCAL_ORIGIN_ID; signalCurrentOriginChanged(_currentOriginId); } _origins.erase(it); Base::Console().log("OriginManager: Unregistered origin '%s'\n", id.c_str()); signalOriginUnregistered(id); return true; } std::vector OriginManager::originIds() const { std::vector ids; ids.reserve(_origins.size()); for (const auto& pair : _origins) { ids.push_back(pair.first); } return ids; } FileOrigin* OriginManager::getOrigin(const std::string& id) const { auto it = _origins.find(id); if (it != _origins.end()) { return it->second.get(); } return nullptr; } FileOrigin* OriginManager::currentOrigin() const { auto it = _origins.find(_currentOriginId); if (it != _origins.end()) { return it->second.get(); } // Fallback to local (should never happen) return _origins.at(LOCAL_ORIGIN_ID).get(); } std::string OriginManager::currentOriginId() const { return _currentOriginId; } bool OriginManager::setCurrentOrigin(const std::string& id) { if (_origins.find(id) == _origins.end()) { return false; } if (_currentOriginId != id) { _currentOriginId = id; savePreferences(); signalCurrentOriginChanged(_currentOriginId); } return true; } FileOrigin* OriginManager::findOwningOrigin(App::Document* doc) const { if (!doc) { return nullptr; } // Check each origin to see if it owns this document // Start with non-local origins since they have specific ownership criteria for (const auto& pair : _origins) { if (pair.first == LOCAL_ORIGIN_ID) { continue; // Check local last as fallback } if (pair.second->ownsDocument(doc)) { return pair.second.get(); } } // If no PLM origin claims it, check local auto localIt = _origins.find(LOCAL_ORIGIN_ID); if (localIt != _origins.end() && localIt->second->ownsDocument(doc)) { return localIt->second.get(); } return nullptr; } FileOrigin* OriginManager::originForDocument(App::Document* doc) const { if (!doc) { return nullptr; } // Check explicit association first auto it = _documentOrigins.find(doc); if (it != _documentOrigins.end()) { FileOrigin* origin = getOrigin(it->second); if (origin) { return origin; } // Origin was unregistered, clear stale association _documentOrigins.erase(it); } // Fall back to ownership detection FileOrigin* owner = findOwningOrigin(doc); if (owner) { // Cache the result _documentOrigins[doc] = owner->id(); return owner; } return nullptr; } void OriginManager::setDocumentOrigin(App::Document* doc, FileOrigin* origin) { if (!doc) { return; } std::string originId = origin ? origin->id() : ""; if (origin) { _documentOrigins[doc] = originId; } else { _documentOrigins.erase(doc); } signalDocumentOriginChanged(doc, originId); } void OriginManager::clearDocumentOrigin(App::Document* doc) { if (!doc) { return; } auto it = _documentOrigins.find(doc); if (it != _documentOrigins.end()) { _documentOrigins.erase(it); } } FileOrigin* OriginManager::originForNewDocument() const { return currentOrigin(); } } // namespace Gui