// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2009 Werner Mayer * * * * 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 # include # include # include # include # include # include # include #include "DocumentProtector.h" #include #include #include using namespace Sandbox; namespace Sandbox { static const int QT_CUSTOM_EVENT_PROTECTOR = 10000; class AbstractCustomProtectorEvent : public QEvent { public: AbstractCustomProtectorEvent() : QEvent(QEvent::Type(QT_CUSTOM_EVENT_PROTECTOR)), semaphore(0) { } ~AbstractCustomProtectorEvent() { if (semaphore) semaphore->release(); } virtual void execute() = 0; QSemaphore* semaphore; }; class CustomAddObjectEvent : public AbstractCustomProtectorEvent { public: CustomAddObjectEvent(App::DocumentObject** o, App::Document* d, const std::string& type, const std::string& name) : obj(o), doc(d), type(type), name(name) { } ~CustomAddObjectEvent() { } void execute() { *obj = doc->addObject(this->type.c_str(), this->name.c_str()); } protected: App::DocumentObject** obj; App::Document* doc; std::string type, name; }; class CustomRemoveObjectEvent : public AbstractCustomProtectorEvent { public: CustomRemoveObjectEvent(App::Document* d, const std::string& n) : doc(d), name(n) { } ~CustomRemoveObjectEvent() { } void execute() { doc->removeObject(this->name.c_str()); } protected: App::Document* doc; std::string name; }; class CustomRecomputeEvent : public AbstractCustomProtectorEvent { public: CustomRecomputeEvent(App::Document* d) : doc(d) { } ~CustomRecomputeEvent() { } void execute() { doc->recompute(); } protected: App::Document* doc; }; class CustomPropertyEvent : public AbstractCustomProtectorEvent { public: CustomPropertyEvent(App::Property& p, const App::Property& v) : property(p), value(v) { } ~CustomPropertyEvent() { } void execute() { property.Paste(value); } protected: App::Property& property; const App::Property& value; }; class CustomCallableEvent : public AbstractCustomProtectorEvent { public: CustomCallableEvent(const AbstractCallable& call) : callable(call) { } ~CustomCallableEvent() { } void execute() { callable(); } protected: const AbstractCallable& callable; }; class CustomPurgeEvent : public AbstractCustomProtectorEvent { public: CustomPurgeEvent(App::DocumentObject* o) : obj(o) { } ~CustomPurgeEvent() { } void execute() { obj->purgeTouched(); } protected: App::DocumentObject* obj; }; class DocumentReceiver : public QObject { public: DocumentReceiver(QObject *parent = 0) : QObject(parent) { } ~DocumentReceiver() { } static DocumentReceiver *globalInstance(); protected: void customEvent(QEvent*); void postEventAndWait(QEvent*); // friends friend class DocumentProtector; friend class DocumentObjectProtector; }; Q_GLOBAL_STATIC(DocumentReceiver, theInstance) DocumentReceiver *DocumentReceiver::globalInstance() { return theInstance(); } // Posts an event and waits for it to finish processing void DocumentReceiver::postEventAndWait(QEvent* e) { QThread *currentThread = QThread::currentThread(); QThread *thr = this->thread(); // this is the main thread if (currentThread == thr) { // we're in the main thread QCoreApplication::sendEvent(this, e); delete e; } else { // NOTE: We send an event to this instance that's why it is important // that this object is part of the main thread QSemaphore semaphore; static_cast(e)->semaphore = &semaphore; QCoreApplication::postEvent(this, e); // wait until the event has been processed semaphore.acquire(); } } void DocumentReceiver::customEvent(QEvent* e) { if ((int)e->type() == QT_CUSTOM_EVENT_PROTECTOR) { static_cast(e)->execute(); } } } DocumentProtector::DocumentProtector(App::Document* doc) : App::DocumentObserver(doc) { } DocumentProtector::~DocumentProtector() { } void DocumentProtector::init() { // this method must be called somewhere from the main thread DocumentReceiver::globalInstance()-> moveToThread(QCoreApplication::instance()->thread()); } void DocumentProtector::slotDeletedDocument(const App::Document& Doc) { if (&Doc == getDocument()) { this->detachDocument(); } } void DocumentProtector::validate() { if (!this->getDocument()) throw Base::ValueError("Handled document is null"); } App::DocumentObject *DocumentProtector::addObject(const std::string& type, const std::string& name) { validate(); App::DocumentObject* obj; DocumentReceiver::globalInstance()->postEventAndWait (new CustomAddObjectEvent(&obj, this->getDocument(), type, name)); return obj; } void DocumentProtector::removeObject(const std::string& name) { validate(); DocumentReceiver::globalInstance()->postEventAndWait (new CustomRemoveObjectEvent(this->getDocument(), name)); } void DocumentProtector::recompute() { validate(); DocumentReceiver::globalInstance()->postEventAndWait (new CustomRecomputeEvent(this->getDocument())); } // ------------------------------------------ DocumentObjectProtector::DocumentObjectProtector(App::DocumentObject* o) : obj(o) { } DocumentObjectProtector::~DocumentObjectProtector() { } void DocumentObjectProtector::validate() { if (!obj) throw Base::ValueError("Handled document object is null"); } App::DocumentObject* DocumentObjectProtector::getObject() const { return this->obj; } bool DocumentObjectProtector::setProperty(const std::string& name, const App::Property& value) { validate(); App::Property* prop = obj->getPropertyByName(name.c_str()); if (!prop) return false; DocumentReceiver::globalInstance()->postEventAndWait(new CustomPropertyEvent(*prop, value)); return true; } void DocumentObjectProtector::execute(const AbstractCallable& call) { validate(); DocumentReceiver::globalInstance()->postEventAndWait(new CustomCallableEvent(call)); } void DocumentObjectProtector::purgeTouched() { validate(); DocumentReceiver::globalInstance()->postEventAndWait(new CustomPurgeEvent(obj)); }