Files
create/src/Gui/FileOriginPython.cpp
forbes-0023 79c85ed2e5
Some checks failed
Build and Test / build (push) Failing after 2m13s
fix(gui): add interactive methods to FileOriginPython and fix Console API calls
- Add openDocumentInteractive() and saveDocumentAsInteractive() to FileOriginPython
- Fix Console API: Warning -> warning, Error -> error in OriginManager.cpp
- These methods bridge Python origins to the new interactive document operations
2026-02-05 14:25:21 -06:00

625 lines
18 KiB
C++

/***************************************************************************
* 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 <App/Application.h>
#include <App/Document.h>
#include <App/DocumentPy.h>
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/PyObjectBase.h>
#include "FileOriginPython.h"
#include "OriginManager.h"
#include "BitmapFactory.h"
namespace Gui {
std::vector<FileOriginPython*> FileOriginPython::_instances;
void FileOriginPython::addOrigin(const Py::Object& obj)
{
// Check if already registered
if (findOrigin(obj)) {
Base::Console().warning("FileOriginPython: Origin already registered\n");
return;
}
auto* origin = new FileOriginPython(obj);
// Cache the ID immediately for registration
origin->_cachedId = origin->callStringMethod("id");
if (origin->_cachedId.empty()) {
Base::Console().error("FileOriginPython: Origin must have non-empty id()\n");
delete origin;
return;
}
_instances.push_back(origin);
// Register with OriginManager
if (!OriginManager::instance()->registerOrigin(origin)) {
// Registration failed - remove from our instances list
// (registerOrigin already deleted the origin)
_instances.pop_back();
}
}
void FileOriginPython::removeOrigin(const Py::Object& obj)
{
FileOriginPython* origin = findOrigin(obj);
if (!origin) {
return;
}
std::string originId = origin->_cachedId;
// Remove from instances list
auto it = std::find(_instances.begin(), _instances.end(), origin);
if (it != _instances.end()) {
_instances.erase(it);
}
// Unregister from OriginManager (this will delete the origin)
OriginManager::instance()->unregisterOrigin(originId);
}
FileOriginPython* FileOriginPython::findOrigin(const Py::Object& obj)
{
for (auto* instance : _instances) {
if (instance->_inst == obj) {
return instance;
}
}
return nullptr;
}
FileOriginPython::FileOriginPython(const Py::Object& obj)
: _inst(obj)
{
}
FileOriginPython::~FileOriginPython() = default;
Py::Object FileOriginPython::callMethod(const char* method) const
{
return callMethod(method, Py::Tuple());
}
Py::Object FileOriginPython::callMethod(const char* method, const Py::Tuple& args) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr(method)) {
Py::Callable func(_inst.getAttr(method));
return func.apply(args);
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return Py::None();
}
bool FileOriginPython::callBoolMethod(const char* method, bool defaultValue) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr(method)) {
Py::Callable func(_inst.getAttr(method));
Py::Object result = func.apply(Py::Tuple());
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return defaultValue;
}
std::string FileOriginPython::callStringMethod(const char* method, const std::string& defaultValue) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr(method)) {
Py::Callable func(_inst.getAttr(method));
Py::Object result = func.apply(Py::Tuple());
if (result.isString()) {
return Py::String(result).as_std_string();
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return defaultValue;
}
Py::Object FileOriginPython::getDocPyObject(App::Document* doc) const
{
if (!doc) {
return Py::None();
}
return Py::asObject(doc->getPyObject());
}
// Identity methods
std::string FileOriginPython::id() const
{
return _cachedId.empty() ? callStringMethod("id") : _cachedId;
}
std::string FileOriginPython::name() const
{
return callStringMethod("name", id());
}
std::string FileOriginPython::nickname() const
{
return callStringMethod("nickname", name());
}
QIcon FileOriginPython::icon() const
{
std::string iconName = callStringMethod("icon", "document-new");
return BitmapFactory().iconFromTheme(iconName.c_str());
}
OriginType FileOriginPython::type() const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("type")) {
Py::Callable func(_inst.getAttr("type"));
Py::Object result = func.apply(Py::Tuple());
if (result.isNumeric()) {
int t = static_cast<int>(Py::Long(result));
if (t >= 0 && t <= static_cast<int>(OriginType::Custom)) {
return static_cast<OriginType>(t);
}
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return OriginType::Custom;
}
// Workflow characteristics
bool FileOriginPython::tracksExternally() const
{
return callBoolMethod("tracksExternally", false);
}
bool FileOriginPython::requiresAuthentication() const
{
return callBoolMethod("requiresAuthentication", false);
}
// Capability queries
bool FileOriginPython::supportsRevisions() const
{
return callBoolMethod("supportsRevisions", false);
}
bool FileOriginPython::supportsBOM() const
{
return callBoolMethod("supportsBOM", false);
}
bool FileOriginPython::supportsPartNumbers() const
{
return callBoolMethod("supportsPartNumbers", false);
}
bool FileOriginPython::supportsAssemblies() const
{
return callBoolMethod("supportsAssemblies", false);
}
// Connection state
ConnectionState FileOriginPython::connectionState() const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("connectionState")) {
Py::Callable func(_inst.getAttr("connectionState"));
Py::Object result = func.apply(Py::Tuple());
if (result.isNumeric()) {
int s = static_cast<int>(Py::Long(result));
if (s >= 0 && s <= static_cast<int>(ConnectionState::Error)) {
return static_cast<ConnectionState>(s);
}
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return ConnectionState::Connected;
}
bool FileOriginPython::connect()
{
return callBoolMethod("connect", true);
}
void FileOriginPython::disconnect()
{
callMethod("disconnect");
}
// Document identity
std::string FileOriginPython::documentIdentity(App::Document* doc) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("documentIdentity")) {
Py::Callable func(_inst.getAttr("documentIdentity"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isString()) {
return Py::String(result).as_std_string();
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return {};
}
std::string FileOriginPython::documentDisplayId(App::Document* doc) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("documentDisplayId")) {
Py::Callable func(_inst.getAttr("documentDisplayId"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isString()) {
return Py::String(result).as_std_string();
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return documentIdentity(doc);
}
bool FileOriginPython::ownsDocument(App::Document* doc) const
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("ownsDocument")) {
Py::Callable func(_inst.getAttr("ownsDocument"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
bool FileOriginPython::syncProperties(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("syncProperties")) {
Py::Callable func(_inst.getAttr("syncProperties"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return true;
}
// Core document operations
App::Document* FileOriginPython::newDocument(const std::string& name)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("newDocument")) {
Py::Callable func(_inst.getAttr("newDocument"));
Py::Tuple args(1);
args.setItem(0, Py::String(name));
Py::Object result = func.apply(args);
if (!result.isNone()) {
// Extract App::Document* from Python object
if (PyObject_TypeCheck(result.ptr(), &(App::DocumentPy::Type))) {
return static_cast<App::DocumentPy*>(result.ptr())->getDocumentPtr();
}
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return nullptr;
}
App::Document* FileOriginPython::openDocument(const std::string& identity)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("openDocument")) {
Py::Callable func(_inst.getAttr("openDocument"));
Py::Tuple args(1);
args.setItem(0, Py::String(identity));
Py::Object result = func.apply(args);
if (!result.isNone()) {
if (PyObject_TypeCheck(result.ptr(), &(App::DocumentPy::Type))) {
return static_cast<App::DocumentPy*>(result.ptr())->getDocumentPtr();
}
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return nullptr;
}
bool FileOriginPython::saveDocument(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("saveDocument")) {
Py::Callable func(_inst.getAttr("saveDocument"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
bool FileOriginPython::saveDocumentAs(App::Document* doc, const std::string& newIdentity)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("saveDocumentAs")) {
Py::Callable func(_inst.getAttr("saveDocumentAs"));
Py::Tuple args(2);
args.setItem(0, getDocPyObject(doc));
args.setItem(1, Py::String(newIdentity));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
// Extended operations
bool FileOriginPython::commitDocument(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("commitDocument")) {
Py::Callable func(_inst.getAttr("commitDocument"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
bool FileOriginPython::pullDocument(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("pullDocument")) {
Py::Callable func(_inst.getAttr("pullDocument"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
bool FileOriginPython::pushDocument(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("pushDocument")) {
Py::Callable func(_inst.getAttr("pushDocument"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
void FileOriginPython::showInfo(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("showInfo")) {
Py::Callable func(_inst.getAttr("showInfo"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
func.apply(args);
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
}
void FileOriginPython::showBOM(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("showBOM")) {
Py::Callable func(_inst.getAttr("showBOM"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
func.apply(args);
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
}
App::Document* FileOriginPython::openDocumentInteractive()
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("openDocumentInteractive")) {
Py::Callable func(_inst.getAttr("openDocumentInteractive"));
Py::Object result = func.apply(Py::Tuple());
if (!result.isNone()) {
if (PyObject_TypeCheck(result.ptr(), &(App::DocumentPy::Type))) {
return static_cast<App::DocumentPy*>(result.ptr())->getDocumentPtr();
}
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return nullptr;
}
bool FileOriginPython::saveDocumentAsInteractive(App::Document* doc)
{
Base::PyGILStateLocker lock;
try {
if (_inst.hasAttr("saveDocumentAsInteractive")) {
Py::Callable func(_inst.getAttr("saveDocumentAsInteractive"));
Py::Tuple args(1);
args.setItem(0, getDocPyObject(doc));
Py::Object result = func.apply(args);
if (result.isBoolean()) {
return Py::Boolean(result);
}
if (result.isNumeric()) {
return Py::Long(result) != 0;
}
}
}
catch (Py::Exception&) {
Base::PyException e;
e.reportException();
}
return false;
}
} // namespace Gui