feat(gui): add origin abstraction layer for unified file operations
Implements Issue #9: Origin abstraction layer This commit introduces a foundational abstraction for document origins, enabling FreeCAD to work with different storage backends (local filesystem, Silo PLM, future cloud services) through a unified interface. ## Core Components ### FileOrigin Abstract Base Class (FileOrigin.h/cpp) - Defines interface for document origin handlers - Identity methods: id(), name(), nickname(), icon(), type() - Workflow characteristics: tracksExternally(), requiresAuthentication() - Capability queries: supportsRevisions(), supportsBOM(), supportsPartNumbers() - Connection state management with fastsignals notifications - Document identity: documentIdentity() returns UUID, documentDisplayId() for display - Property sync: syncProperties() for bidirectional database sync - Core operations: newDocument(), openDocument(), saveDocument(), saveDocumentAs() - Extended PLM operations: commitDocument(), pullDocument(), pushDocument(), etc. ### LocalFileOrigin Implementation - Default origin for local filesystem documents - ownsDocument(): Returns true if document has NO SiloItemId property - Wraps existing FreeCAD file operations (App::GetApplication()) ### OriginManager Singleton (OriginManager.h/cpp) - Follows WorkbenchManager pattern (instance()/destruct()) - Manages registered FileOrigin instances - Tracks current origin selection with persistence - Provides document-to-origin resolution via findOwningOrigin() - Emits signals: signalOriginRegistered, signalOriginUnregistered, signalCurrentOriginChanged - Preferences stored at: User parameter:BaseApp/Preferences/General/Origin ### Python Bindings (FileOriginPython.h/cpp) - Adapts Python objects to FileOrigin C++ interface - Enables Silo addon to implement origins in Python - Thread-safe with Base::PyGILStateLocker - Static addOrigin()/removeOrigin() for registration ### Python API (ApplicationPy.cpp) - FreeCADGui.addOrigin(obj) - Register Python origin - FreeCADGui.removeOrigin(obj) - Unregister Python origin - FreeCADGui.getOrigin(id) - Get origin info as dict - FreeCADGui.listOrigins() - List all registered origin IDs - FreeCADGui.activeOrigin() - Get current origin info - FreeCADGui.setActiveOrigin(id) - Set active origin ## Design Decisions 1. **UUID Tracking**: Documents tracked by SiloItemId (immutable UUID), SiloPartNumber used for human-readable display only 2. **Ownership by Properties**: Origin ownership determined by document properties (SiloItemId), not file path location 3. **Local Storage Always**: All documents saved locally; origins change workflow and identity model, not storage location 4. **Property Syncing**: syncProperties() enables bidirectional sync of document metadata with database (Description, SourcingType, etc.) ## Files Added - src/Gui/FileOrigin.h - src/Gui/FileOrigin.cpp - src/Gui/FileOriginPython.h - src/Gui/FileOriginPython.cpp - src/Gui/OriginManager.h - src/Gui/OriginManager.cpp ## Files Modified - src/Gui/CMakeLists.txt - Added new source files - src/Gui/Application.cpp - Initialize/destruct OriginManager - src/Gui/ApplicationPy.h - Added Python method declarations - src/Gui/ApplicationPy.cpp - Added Python method implementations Refs: #9
This commit is contained in:
146
src/Gui/FileOriginPython.h
Normal file
146
src/Gui/FileOriginPython.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/***************************************************************************
|
||||
* 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 *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GUI_FILEORIGINPYTHON_H
|
||||
#define GUI_FILEORIGINPYTHON_H
|
||||
|
||||
#include <vector>
|
||||
#include <FCGlobal.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
#include "FileOrigin.h"
|
||||
|
||||
namespace Gui {
|
||||
|
||||
/**
|
||||
* @brief Wrapper that adapts a Python object to the FileOrigin interface
|
||||
*
|
||||
* This allows Python addons (like Silo) to implement origins in Python
|
||||
* while integrating with the C++ OriginManager.
|
||||
*
|
||||
* The Python object should implement the following methods:
|
||||
* - id() -> str
|
||||
* - name() -> str
|
||||
* - nickname() -> str
|
||||
* - icon() -> str (icon name for BitmapFactory)
|
||||
* - type() -> int (OriginType enum value)
|
||||
* - tracksExternally() -> bool
|
||||
* - requiresAuthentication() -> bool
|
||||
* - ownsDocument(doc) -> bool
|
||||
* - documentIdentity(doc) -> str
|
||||
* - documentDisplayId(doc) -> str
|
||||
*
|
||||
* Optional methods:
|
||||
* - supportsRevisions() -> bool
|
||||
* - supportsBOM() -> bool
|
||||
* - supportsPartNumbers() -> bool
|
||||
* - supportsAssemblies() -> bool
|
||||
* - connectionState() -> int
|
||||
* - connect() -> bool
|
||||
* - disconnect() -> None
|
||||
* - syncProperties(doc) -> bool
|
||||
* - newDocument(name) -> Document
|
||||
* - openDocument(identity) -> Document
|
||||
* - saveDocument(doc) -> bool
|
||||
* - saveDocumentAs(doc, newIdentity) -> bool
|
||||
* - commitDocument(doc) -> bool
|
||||
* - pullDocument(doc) -> bool
|
||||
* - pushDocument(doc) -> bool
|
||||
* - showInfo(doc) -> None
|
||||
* - showBOM(doc) -> None
|
||||
*/
|
||||
class GuiExport FileOriginPython : public FileOrigin
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Register a Python object as an origin.
|
||||
* The Python object should implement the FileOrigin interface methods.
|
||||
* @param obj The Python object implementing the origin interface
|
||||
*/
|
||||
static void addOrigin(const Py::Object& obj);
|
||||
|
||||
/**
|
||||
* Unregister a Python origin by its Python object.
|
||||
* @param obj The Python object to unregister
|
||||
*/
|
||||
static void removeOrigin(const Py::Object& obj);
|
||||
|
||||
/**
|
||||
* Find a registered Python origin by its Python object.
|
||||
* @param obj The Python object to find
|
||||
* @return The FileOriginPython wrapper or nullptr
|
||||
*/
|
||||
static FileOriginPython* findOrigin(const Py::Object& obj);
|
||||
|
||||
// FileOrigin interface - delegates to Python
|
||||
std::string id() const override;
|
||||
std::string name() const override;
|
||||
std::string nickname() const override;
|
||||
QIcon icon() const override;
|
||||
OriginType type() const override;
|
||||
|
||||
bool tracksExternally() const override;
|
||||
bool requiresAuthentication() const override;
|
||||
bool supportsRevisions() const override;
|
||||
bool supportsBOM() const override;
|
||||
bool supportsPartNumbers() const override;
|
||||
bool supportsAssemblies() const override;
|
||||
|
||||
ConnectionState connectionState() const override;
|
||||
bool connect() override;
|
||||
void disconnect() override;
|
||||
|
||||
std::string documentIdentity(App::Document* doc) const override;
|
||||
std::string documentDisplayId(App::Document* doc) const override;
|
||||
bool ownsDocument(App::Document* doc) const override;
|
||||
bool syncProperties(App::Document* doc) override;
|
||||
|
||||
App::Document* newDocument(const std::string& name = "") override;
|
||||
App::Document* openDocument(const std::string& identity) override;
|
||||
bool saveDocument(App::Document* doc) override;
|
||||
bool saveDocumentAs(App::Document* doc, const std::string& newIdentity) override;
|
||||
|
||||
bool commitDocument(App::Document* doc) override;
|
||||
bool pullDocument(App::Document* doc) override;
|
||||
bool pushDocument(App::Document* doc) override;
|
||||
void showInfo(App::Document* doc) override;
|
||||
void showBOM(App::Document* doc) override;
|
||||
|
||||
private:
|
||||
explicit FileOriginPython(const Py::Object& obj);
|
||||
~FileOriginPython() override;
|
||||
|
||||
// Helper to call Python methods safely
|
||||
Py::Object callMethod(const char* method) const;
|
||||
Py::Object callMethod(const char* method, const Py::Tuple& args) const;
|
||||
bool callBoolMethod(const char* method, bool defaultValue = false) const;
|
||||
std::string callStringMethod(const char* method, const std::string& defaultValue = "") const;
|
||||
Py::Object getDocPyObject(App::Document* doc) const;
|
||||
|
||||
Py::Object _inst;
|
||||
std::string _cachedId; // Cache the ID since it's used for registration
|
||||
|
||||
static std::vector<FileOriginPython*> _instances;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_FILEORIGINPYTHON_H
|
||||
Reference in New Issue
Block a user