diff --git a/src/App/AutoTransaction.cpp b/src/App/AutoTransaction.cpp
index 82ccc8cec9..6094ef2070 100644
--- a/src/App/AutoTransaction.cpp
+++ b/src/App/AutoTransaction.cpp
@@ -21,7 +21,9 @@
****************************************************************************/
#include "PreCompiled.h"
+
#include
+#include
#include "Application.h"
#include "Transactions.h"
#include "Document.h"
@@ -31,6 +33,9 @@ FC_LOG_LEVEL_INIT("App",true,true)
using namespace App;
+static int _TransactionLock;
+static int _TransactionClosed;
+
AutoTransaction::AutoTransaction(const char *name, bool tmpName) {
auto &app = GetApplication();
if(name && app._activeTransactionGuard>=0) {
@@ -125,7 +130,11 @@ int Application::setActiveTransaction(const char *name, bool persist) {
AutoTransaction::setEnable(false);
return 0;
}
- }else{
+ } else if (_TransactionLock) {
+ if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
+ FC_WARN("Transaction locked, ignore new transaction '" << name << "'");
+ return 0;
+ } else {
FC_LOG("set active transaction '" << name << "'");
_activeTransactionID = 0;
for(auto &v : DocMap)
@@ -156,10 +165,17 @@ void Application::closeActiveTransaction(bool abort, int id) {
return;
}
+ if(_TransactionLock) {
+ if(_TransactionClosed >= 0)
+ _TransactionLock = abort?-1:1;
+ FC_LOG("pending " << (abort?"abort":"close") << " transaction");
+ return;
+ }
+
FC_LOG("close transaction '" << _activeTransactionName << "' " << abort);
_activeTransactionID = 0;
- TransactionSignaller siganller(abort,false);
+ TransactionSignaller signaller(abort,false);
for(auto &v : DocMap) {
if(v.second->getTransactionID(true) != id)
continue;
@@ -170,3 +186,57 @@ void Application::closeActiveTransaction(bool abort, int id) {
}
}
+////////////////////////////////////////////////////////////////////////
+
+TransactionLocker::TransactionLocker(bool lock)
+ :active(lock)
+{
+ if(lock)
+ ++_TransactionLock;
+}
+
+TransactionLocker::~TransactionLocker()
+{
+ if(active) {
+ try {
+ activate(false);
+ return;
+ } catch (Base::Exception &e) {
+ e.ReportException();
+ } catch (Py::Exception &) {
+ Base::PyException e;
+ e.ReportException();
+ } catch (std::exception &e) {
+ FC_ERR(e.what());
+ } catch (...) {
+ }
+ FC_ERR("Exception when unlocking transaction");
+ }
+}
+
+void TransactionLocker::activate(bool enable)
+{
+ if(active == enable)
+ return;
+
+ active = enable;
+ if(active) {
+ ++_TransactionLock;
+ return;
+ }
+
+ if(--_TransactionLock != 0)
+ return;
+
+ if(_TransactionClosed) {
+ bool abort = (_TransactionClosed<0);
+ _TransactionClosed = 0;
+ GetApplication().closeActiveTransaction(abort);
+ }
+}
+
+bool TransactionLocker::isLocked() {
+ return _TransactionLock > 0;
+}
+
+
diff --git a/src/App/AutoTransaction.h b/src/App/AutoTransaction.h
index f22fc123a4..e792b41b89 100644
--- a/src/App/AutoTransaction.h
+++ b/src/App/AutoTransaction.h
@@ -25,6 +25,8 @@
namespace App {
+class Application;
+
/// Helper class to manager transaction (i.e. undo/redo)
class AppExport AutoTransaction {
private:
@@ -79,6 +81,51 @@ private:
int tid = 0;
};
+
+/** Helper class to lock a transaction from being closed or aborted.
+ *
+ * The helper class is used to protect some critical transaction from being
+ * closed prematurely, e.g. when deleting some object.
+ */
+class AppExport TransactionLocker {
+public:
+
+ /** Constructor
+ * @param lock: whether to activate the lock
+ */
+ TransactionLocker(bool lock=true);
+
+ /** Destructor
+ * Unlock the transaction is this locker is active
+ */
+ ~TransactionLocker();
+
+ /** Activate or deactivate this locker
+ * @param enable: whether to activate the locker
+ *
+ * An internal counter is used to support recursive locker. When activated,
+ * the current active transaction cannot be closed or aborted. But the
+ * closing call (Application::closeActiveTransaction()) will be remembered,
+ * and performed when the internal lock counter reaches zero.
+ */
+ void activate(bool enable);
+
+ /// Check if the locker is active
+ bool isActive() const {return active;}
+
+ /// Check if transaction is being locked
+ static bool isLocked();
+
+ friend class Application;
+
+private:
+ /// Private new operator to prevent heap allocation
+ void* operator new(size_t size);
+
+private:
+ bool active;
+};
+
} // namespace App
#endif // APP_AUTOTRANSACTION_H
diff --git a/src/App/Document.cpp b/src/App/Document.cpp
index cf54a7924e..f916e05fc1 100644
--- a/src/App/Document.cpp
+++ b/src/App/Document.cpp
@@ -91,6 +91,7 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
#include
#include
+#include "AutoTransaction.h"
#include "Document.h"
#include "Application.h"
#include "DocumentObject.h"
@@ -3764,6 +3765,8 @@ void Document::removeObject(const char* sName)
return;
}
+ TransactionLocker tlock;
+
_checkTransaction(pos->second,0,__LINE__);
#if 0
@@ -3865,6 +3868,8 @@ void Document::_removeObject(DocumentObject* pcObject)
return;
}
+ TransactionLocker tlock;
+
// TODO Refactoring: share code with Document::removeObject() (2015-09-01, Fat-Zer)
_checkTransaction(pcObject,0,__LINE__);
diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp
index e091e32835..566e88da89 100644
--- a/src/Gui/CommandDoc.cpp
+++ b/src/Gui/CommandDoc.cpp
@@ -1130,6 +1130,9 @@ void StdCmdDelete::activated(int iMsg)
commitCommand();
return;
}
+
+ App::TransactionLocker tlock;
+
Gui::getMainWindow()->setUpdatesEnabled(false);
auto editDoc = Application::Instance->editDocument();
ViewProviderDocumentObject *vpedit = 0;