From f4665aa7b55f7f415d39ad71ee232a4dd8e01b30 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sun, 1 Feb 2026 12:01:58 -0500 Subject: [PATCH 01/12] Core: support multiple active transactions --- src/App/Application.h | 77 +-- src/App/ApplicationPy.cpp | 17 +- src/App/AutoTransaction.cpp | 334 ++++++------- src/App/AutoTransaction.h | 70 +-- src/App/Document.cpp | 300 ++++++++---- src/App/Document.h | 25 +- src/App/Document.pyi | 9 + src/App/DocumentPyImp.cpp | 9 + src/App/PropertyStandard.cpp | 6 +- src/App/TransactionDefs.h | 35 ++ src/App/Transactions.cpp | 2 +- src/App/Transactions.h | 4 +- src/App/private/DocumentP.h | 6 + src/Gui/Application.cpp | 82 +++- src/Gui/Application.h | 19 +- src/Gui/Command.cpp | 82 +++- src/Gui/Command.h | 21 +- src/Gui/CommandDoc.cpp | 75 ++- src/Gui/CommandLink.cpp | 12 +- src/Gui/CommandStructure.cpp | 2 + src/Gui/CommandView.cpp | 2 +- src/Gui/Control.cpp | 161 +++++-- src/Gui/Control.h | 27 +- src/Gui/Dialogs/DlgAddProperty.cpp | 11 +- src/Gui/Document.cpp | 86 +++- src/Gui/Document.h | 13 +- src/Gui/Document.pyi | 25 + src/Gui/DocumentPyImp.cpp | 28 ++ src/Gui/ExpressionBinding.cpp | 18 +- src/Gui/OverlayWidgets.cpp | 6 +- src/Gui/QSint/actionpanel/actionpanel.cpp | 5 - src/Gui/QSint/actionpanel/actionpanel.h | 6 - src/Gui/Selection/Selection.h | 2 +- src/Gui/TaskElementColors.cpp | 42 +- src/Gui/TaskView/TaskDialog.cpp | 6 + src/Gui/TaskView/TaskDialog.h | 7 + src/Gui/TaskView/TaskDialogPython.cpp | 75 ++- src/Gui/TaskView/TaskView.cpp | 447 ++++++++++-------- src/Gui/TaskView/TaskView.h | 63 ++- src/Gui/Tree.cpp | 60 +-- src/Gui/ViewProvider.cpp | 5 +- src/Gui/ViewProvider.h | 2 + src/Gui/ViewProviderDocumentObject.cpp | 6 +- src/Gui/ViewProviderDragger.cpp | 10 +- src/Gui/ViewProviderFeaturePython.h | 15 +- src/Gui/ViewProviderImagePlane.cpp | 5 +- src/Gui/ViewProviderLink.cpp | 58 ++- src/Gui/propertyeditor/PropertyEditor.cpp | 56 ++- src/Gui/propertyeditor/PropertyEditor.h | 2 + src/Gui/propertyeditor/PropertyItem.cpp | 9 +- src/Mod/Assembly/CommandCreateAssembly.py | 4 +- src/Mod/Assembly/CommandCreateBom.py | 8 +- src/Mod/Assembly/CommandCreateJoint.py | 4 +- src/Mod/Assembly/CommandCreateSimulation.py | 8 +- src/Mod/Assembly/CommandCreateView.py | 10 +- src/Mod/Assembly/CommandInsertLink.py | 6 +- src/Mod/Assembly/CommandInsertNewPart.py | 2 +- src/Mod/Assembly/CommandSolveAssembly.py | 2 +- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 37 +- src/Mod/Assembly/Gui/ViewProviderAssembly.h | 4 + .../Assembly/Gui/ViewProviderAssemblyLink.cpp | 4 +- src/Mod/Assembly/JointObject.py | 14 +- src/Mod/Assembly/UtilsAssembly.py | 3 + src/Mod/Fem/Gui/Command.cpp | 8 +- src/Mod/Fem/Gui/TaskCreateNodeSet.cpp | 14 +- src/Mod/Fem/Gui/TaskCreateNodeSet.h | 2 + src/Mod/Fem/Gui/TaskDlgCreateElementSet.cpp | 17 +- src/Mod/Fem/Gui/TaskDlgCreateElementSet.h | 3 + src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp | 15 +- src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h | 3 + src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp | 12 +- src/Mod/Fem/Gui/TaskFemConstraint.cpp | 18 +- src/Mod/Fem/Gui/TaskFemConstraint.h | 4 + .../TaskFemConstraintInitialTemperature.cpp | 6 +- src/Mod/Fem/Gui/TaskPostBoxes.cpp | 8 +- src/Mod/Fem/femguiutils/selection_widgets.py | 8 + .../Fem/femsolver/elmer/equations/equation.py | 6 + .../Fem/femtaskpanels/base_femtaskpanel.py | 8 + src/Mod/Measure/Gui/QuickMeasure.cpp | 2 +- src/Mod/Measure/Gui/TaskMeasure.cpp | 55 ++- src/Mod/Measure/Gui/TaskMeasure.h | 5 + src/Mod/Mesh/Gui/DlgDecimating.cpp | 12 +- src/Mod/Mesh/Gui/DlgSmoothing.cpp | 11 +- src/Mod/Part/Gui/Command.cpp | 34 +- src/Mod/Part/Gui/CommandSimple.cpp | 5 +- src/Mod/Part/Gui/DlgExtrusion.cpp | 25 +- src/Mod/Part/Gui/DlgExtrusion.h | 5 + src/Mod/Part/Gui/DlgFilletEdges.cpp | 17 +- src/Mod/Part/Gui/DlgFilletEdges.h | 3 + src/Mod/Part/Gui/DlgProjectionOnSurface.cpp | 107 +++-- src/Mod/Part/Gui/DlgProjectionOnSurface.h | 17 +- src/Mod/Part/Gui/DlgRevolution.cpp | 24 +- src/Mod/Part/Gui/DlgRevolution.h | 4 + src/Mod/Part/Gui/Mirroring.cpp | 27 +- src/Mod/Part/Gui/Mirroring.h | 4 + src/Mod/Part/Gui/ShapeFromMesh.cpp | 7 +- src/Mod/Part/Gui/TaskAttacher.cpp | 23 +- src/Mod/Part/Gui/TaskAttacher.h | 6 +- src/Mod/Part/Gui/TaskFaceAppearances.cpp | 23 +- src/Mod/Part/Gui/TaskFaceAppearances.h | 3 + src/Mod/Part/Gui/TaskOffset.cpp | 6 +- src/Mod/Part/Gui/TaskShapeBuilder.cpp | 17 +- src/Mod/Part/Gui/TaskShapeBuilder.h | 3 + src/Mod/Part/Gui/TaskThickness.cpp | 6 +- src/Mod/Part/Gui/ViewProvider.cpp | 2 +- .../Part/Gui/ViewProviderAttachExtension.cpp | 10 +- .../Part/Gui/ViewProviderGridExtension.cpp | 20 +- src/Mod/Part/Gui/ViewProviderGridExtension.h | 2 +- src/Mod/Part/Gui/ViewProviderMirror.cpp | 37 +- src/Mod/Part/Gui/ViewProviderPrimitive.cpp | 3 +- .../Part/Gui/ViewProviderProjectOnSurface.cpp | 3 +- src/Mod/PartDesign/Gui/Command.cpp | 66 ++- src/Mod/PartDesign/Gui/CommandBody.cpp | 15 +- src/Mod/PartDesign/Gui/CommandPrimitive.cpp | 8 +- src/Mod/PartDesign/Gui/DlgActiveBody.cpp | 4 +- src/Mod/PartDesign/Gui/SketchWorkflow.cpp | 3 +- .../PartDesign/Gui/TaskBooleanParameters.cpp | 28 +- .../PartDesign/Gui/TaskBooleanParameters.h | 4 +- .../PartDesign/Gui/TaskDressUpParameters.cpp | 41 +- .../PartDesign/Gui/TaskDressUpParameters.h | 4 + .../PartDesign/Gui/TaskFeatureParameters.cpp | 28 +- .../PartDesign/Gui/TaskFeatureParameters.h | 2 + src/Mod/PartDesign/Gui/TaskFeaturePick.cpp | 32 +- src/Mod/PartDesign/Gui/TaskFeaturePick.h | 5 +- .../Gui/TaskMultiTransformParameters.cpp | 8 +- src/Mod/PartDesign/Gui/TaskPipeParameters.cpp | 3 +- .../Gui/TaskPrimitiveParameters.cpp | 3 +- src/Mod/PartDesign/Gui/TaskShapeBinder.cpp | 16 +- src/Mod/PartDesign/Gui/TaskShapeBinder.h | 4 +- .../Gui/TaskTransformedParameters.cpp | 20 +- .../Gui/TaskTransformedParameters.h | 3 + src/Mod/PartDesign/Gui/ViewProvider.cpp | 9 +- src/Mod/PartDesign/Gui/ViewProviderBase.cpp | 4 +- .../PartDesign/Gui/ViewProviderBoolean.cpp | 1 + src/Mod/PartDesign/Gui/ViewProviderDatum.cpp | 2 +- .../PartDesign/Gui/ViewProviderPrimitive.cpp | 1 + .../Gui/ViewProviderShapeBinder.cpp | 9 +- .../Gui/FitBSplineCurve.cpp | 8 +- .../Gui/FitBSplineSurface.cpp | 12 +- src/Mod/ReverseEngineering/Gui/Poisson.cpp | 6 +- src/Mod/Show/SceneDetails/ClipPlane.py | 4 +- src/Mod/Show/mTempoVis.py | 3 +- src/Mod/Sketcher/Gui/Command.cpp | 19 +- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 417 ++++++++-------- src/Mod/Sketcher/Gui/CommandConstraints.h | 12 +- .../Sketcher/Gui/CommandSketcherBSpline.cpp | 12 +- src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 54 ++- .../Gui/CommandSketcherVirtualSpace.cpp | 13 +- src/Mod/Sketcher/Gui/DrawSketchController.h | 6 +- .../Sketcher/Gui/DrawSketchDefaultHandler.h | 6 +- .../Gui/DrawSketchDefaultWidgetController.h | 3 +- src/Mod/Sketcher/Gui/DrawSketchHandler.cpp | 317 +++++++------ src/Mod/Sketcher/Gui/DrawSketchHandler.h | 5 + src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h | 6 +- .../Gui/DrawSketchHandlerArcOfEllipse.h | 6 +- .../Gui/DrawSketchHandlerArcOfHyperbola.h | 6 +- .../Gui/DrawSketchHandlerArcOfParabola.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerArcSlot.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerBSpline.h | 20 +- .../Gui/DrawSketchHandlerCarbonCopy.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerCircle.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerEllipse.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerExtend.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerExternal.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerFillet.h | 14 +- src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerLineSet.h | 16 +- .../Sketcher/Gui/DrawSketchHandlerOffset.h | 4 +- src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerPolygon.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerRectangle.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerRotate.h | 6 +- src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h | 6 +- src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerSplitting.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerSymmetry.h | 6 +- src/Mod/Sketcher/Gui/DrawSketchHandlerText.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerTranslate.h | 6 +- .../Sketcher/Gui/DrawSketchHandlerTrimming.h | 6 +- src/Mod/Sketcher/Gui/EditDatumDialog.cpp | 12 +- src/Mod/Sketcher/Gui/EditDatumDialog.h | 5 +- src/Mod/Sketcher/Gui/EditTextDialog.cpp | 8 +- .../Sketcher/Gui/TaskSketcherConstraints.cpp | 28 +- src/Mod/Sketcher/Gui/Utils.cpp | 16 + src/Mod/Sketcher/Gui/Utils.h | 3 + src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 122 +++-- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 6 + src/Mod/Spreadsheet/Gui/DlgBindSheet.cpp | 13 +- src/Mod/Spreadsheet/Gui/DlgSheetConf.cpp | 13 +- src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp | 6 +- src/Mod/Spreadsheet/Gui/SheetModel.cpp | 6 +- src/Mod/Spreadsheet/Gui/SheetTableView.cpp | 35 +- src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp | 4 +- src/Mod/Spreadsheet/Gui/Workbench.cpp | 11 +- .../Surface/Gui/Blending/TaskBlendCurve.cpp | 26 +- src/Mod/Surface/Gui/Blending/TaskBlendCurve.h | 5 +- src/Mod/Surface/Gui/TaskFilling.cpp | 59 ++- src/Mod/Surface/Gui/TaskFilling.h | 5 + src/Mod/Surface/Gui/TaskFillingEdge.cpp | 25 +- src/Mod/Surface/Gui/TaskFillingEdge.h | 2 + src/Mod/Surface/Gui/TaskFillingVertex.cpp | 25 +- src/Mod/Surface/Gui/TaskFillingVertex.h | 1 + src/Mod/Surface/Gui/TaskGeomFillSurface.cpp | 28 +- src/Mod/Surface/Gui/TaskGeomFillSurface.h | 3 + src/Mod/Surface/Gui/TaskSections.cpp | 45 +- src/Mod/Surface/Gui/TaskSections.h | 4 + src/Mod/TechDraw/Gui/CommandAnnotate.cpp | 8 +- src/Mod/TechDraw/Gui/CommandCreateDims.cpp | 37 +- src/Mod/TechDraw/Gui/CommandExtensionDims.cpp | 78 +-- src/Mod/TechDraw/Gui/CommandExtensionPack.cpp | 64 +-- src/Mod/TechDraw/Gui/PagePrinter.cpp | 4 +- src/Mod/TechDraw/Gui/QGIRichAnno.cpp | 7 +- src/Mod/TechDraw/Gui/QGIRichAnno.h | 2 + src/Mod/TechDraw/Gui/QGIView.cpp | 6 +- src/Mod/TechDraw/Gui/QGIViewBalloon.cpp | 8 +- src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 8 +- src/Mod/TechDraw/Gui/QGSPage.cpp | 5 +- src/Mod/TechDraw/Gui/TaskActiveView.cpp | 19 +- src/Mod/TechDraw/Gui/TaskActiveView.h | 1 + src/Mod/TechDraw/Gui/TaskBalloon.cpp | 2 +- src/Mod/TechDraw/Gui/TaskCenterLine.cpp | 8 +- src/Mod/TechDraw/Gui/TaskComplexSection.cpp | 8 +- src/Mod/TechDraw/Gui/TaskCosVertex.cpp | 4 +- src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp | 8 +- src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp | 8 +- src/Mod/TechDraw/Gui/TaskCustomizeFormat.cpp | 5 +- src/Mod/TechDraw/Gui/TaskDetail.cpp | 8 +- src/Mod/TechDraw/Gui/TaskDimRepair.cpp | 4 +- src/Mod/TechDraw/Gui/TaskHatch.cpp | 8 +- src/Mod/TechDraw/Gui/TaskLeaderLine.cpp | 8 +- src/Mod/TechDraw/Gui/TaskProjGroup.cpp | 8 +- src/Mod/TechDraw/Gui/TaskProjection.cpp | 4 +- src/Mod/TechDraw/Gui/TaskRichAnno.cpp | 11 +- src/Mod/TechDraw/Gui/TaskRichAnno.h | 2 + src/Mod/TechDraw/Gui/TaskSectionView.cpp | 8 +- .../TechDraw/Gui/TaskSurfaceFinishSymbols.cpp | 4 +- src/Mod/TechDraw/Gui/TaskWeldingSymbol.cpp | 8 +- .../CommandAxoLengthDimension.py | 4 +- .../TechDrawTools/TaskAddOffsetVertex.py | 8 +- .../TechDrawTools/TaskFillTemplateFields.py | 6 +- .../TechDrawTools/TaskHoleShaftFit.py | 6 +- .../TechDraw/TechDrawTools/TaskMoveView.py | 6 +- .../TechDraw/TechDrawTools/TaskShareView.py | 6 +- src/Mod/Test/Document.py | 46 ++ tests/src/App/Property.cpp | 7 +- 245 files changed, 3543 insertions(+), 2042 deletions(-) create mode 100644 src/App/TransactionDefs.h diff --git a/src/App/Application.h b/src/App/Application.h index c2c0985793..d32d5df5c6 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -35,10 +35,12 @@ #include #include #include +#include #include #include #include +#include "TransactionDefs.h" // forward declarations using PyObject = struct _object; @@ -289,9 +291,11 @@ public: /** * @brief Setup a pending application-wide active transaction. * - * Call this function to setup an application-wide transaction. All current - * pending transactions of opening documents will be committed first. - * However, no new transaction is created by this call. Any subsequent + * Call this function to setup a transaction in the currently active document + * if no document is active, a global transaction is created. If the current + * active document already has a transaction setup it will either commit the + * current transaction or rename it, depending on the tmpName flag of the + * currently setup transaction. No new transaction is created by this call. Any subsequent * changes in any current opening document will auto create a transaction * with the given name and ID. If more than one document is changed, the * transactions will share the same ID, and will be undo/redo together. @@ -303,32 +307,45 @@ public: * * @return The new transaction ID. */ - int setActiveTransaction(const char* name, bool persist = false); - /** - * @brief Get the current active transaction name and ID. + int setActiveTransaction(TransactionName name); + + /// Return the current global transaction name and ID if such a global transaction is + /// setup (uncommon) + std::string getActiveTransaction(int *tid=nullptr) const; + + int openGlobalTransaction(TransactionName name); + int getGlobalTransaction() const; + + bool transactionIsActive(int tid) const; + std::string getTransactionName(int tid) const; + bool transactionTmpName(int tid) const; + Document* transactionInitiator(int tid) const; + std::optional transactionDescription(int tid) const; + void setTransactionDescription(int tid, const TransactionDescription& desc); + void setTransactionName(int tid, const TransactionName& name); + + /** Commit/abort current active transactions * * If there is no active transaction, an empty string is returned. * - * @param[out] tid If not `nullptr`, the current active transaction ID is - * returned through this pointer. - * @return The current active transaction name. - */ - const char* getActiveTransaction(int* tid = nullptr) const; - - /** - * @brief Commit/abort current active transactions. - * * @param[in] abort: whether to abort or commit the transactions - * @param[in] id: by default 0 meaning that the current active transaction ID is used. - * - * Besides calling this function directly, it will be called by - * automatically if 1) any new transaction is created with a different ID, - * or 2) any transaction with the current active transaction ID is either - * committed or aborted. + * @param[in] id: by default 0 meaning that the current global transaction ID is used. + * + * Bsides calling this function directly, it will be called by automatically + * if 1) any new transaction is created with a different ID, or 2) any + * transaction with the current active transaction ID is either committed or + * aborted + * returns true if it succeeded in closing the transaction */ - void closeActiveTransaction(bool abort=false, int id=0); - /// @} + bool closeActiveTransaction(TransactionCloseMode mode = TransactionCloseMode::Commit, int id=0); + + /// Internally call closeActiveTransaction(), but it makes the call site clearer + bool commitTransaction(int tid); + bool abortTransaction(int tid); + + + //@} // NOLINTBEGIN // clang-format off @@ -1040,10 +1057,16 @@ private: friend class AutoTransaction; - std::string _activeTransactionName; - int _activeTransactionID{0}; - int _activeTransactionGuard{0}; - bool _activeTransactionTmpName{false}; + std::map _activeTransactionDescriptions; // Maps transaction ID to transaction name + + int currentlyClosingID {0}; + + // This is the transaction ID for a global transaction + // Documents will take this ID if it is non-zero + // and generate their own otherwise + int _globalTransactionID { 0 }; + bool _globalTransactionTmpName {false}; + std::string _globalTransactionName; Base::ProgressIndicator _progressIndicator; diff --git a/src/App/ApplicationPy.cpp b/src/App/ApplicationPy.cpp index bb015f1b08..36ffbf05ae 100644 --- a/src/App/ApplicationPy.cpp +++ b/src/App/ApplicationPy.cpp @@ -242,9 +242,7 @@ PyMethodDef ApplicationPy::Methods[] = { METH_VARARGS, "setActiveTransaction(name, persist=False) -- setup active transaction with the given name\n\n" "name: the transaction name\n" - "persist(False): by default, if the calling code is inside any invocation of a command, it\n" - " will be auto closed once all commands within the current stack exists. To\n" - " disable auto closing, set persist=True\n" + "persist(False): This parameter has no effect and is kept for compatibility reasonss" "Returns the transaction ID for the active transaction. An application-wide\n" "active transaction causes any document changes to open a transaction with\n" "the given name and ID."}, @@ -1201,14 +1199,14 @@ PyObject* ApplicationPy::sGetDependentObjects(PyObject* /*self*/, PyObject* args PyObject* ApplicationPy::sSetActiveTransaction(PyObject* /*self*/, PyObject* args) { char* name {}; - PyObject* persist = Py_False; + PyObject* persist = Py_False; // Not used if (!PyArg_ParseTuple(args, "s|O!", &name, &PyBool_Type, &persist)) { return nullptr; } PY_TRY { - Py::Long ret(GetApplication().setActiveTransaction(name, Base::asBoolean(persist))); + Py::Long ret(GetApplication().setActiveTransaction(TransactionName {.name=name, .temporary=false})); return Py::new_reference_to(ret); } PY_CATCH; @@ -1223,8 +1221,8 @@ PyObject* ApplicationPy::sGetActiveTransaction(PyObject* /*self*/, PyObject* arg PY_TRY { int id = 0; - const char* name = GetApplication().getActiveTransaction(&id); - if (!name || id <= 0) { + std::string name = GetApplication().getActiveTransaction(&id); + if (name.empty() || id <= 0) { Py_Return; } Py::Tuple ret(2); @@ -1245,7 +1243,10 @@ PyObject* ApplicationPy::sCloseActiveTransaction(PyObject* /*self*/, PyObject* a PY_TRY { - GetApplication().closeActiveTransaction(Base::asBoolean(abort), id); + TransactionCloseMode mode = Base::asBoolean(abort) + ? TransactionCloseMode::Abort + : TransactionCloseMode::Commit; + GetApplication().closeActiveTransaction(mode, id); Py_Return; } PY_CATCH; diff --git a/src/App/AutoTransaction.cpp b/src/App/AutoTransaction.cpp index b0bb27c57a..e4f09cbdec 100644 --- a/src/App/AutoTransaction.cpp +++ b/src/App/AutoTransaction.cpp @@ -35,204 +35,217 @@ FC_LOG_LEVEL_INIT("App", true, true) using namespace App; -static int _TransactionLock; -static int _TransactionClosed; - -AutoTransaction::AutoTransaction(const char* name, bool tmpName) +AutoTransaction::AutoTransaction(int tid) + : tid(tid) { - auto& app = GetApplication(); - if (name && app._activeTransactionGuard >= 0) { - if (!app.getActiveTransaction() || (!tmpName && app._activeTransactionTmpName)) { - FC_LOG("auto transaction '" << name << "', " << tmpName); - tid = app.setActiveTransaction(name); - app._activeTransactionTmpName = tmpName; - } - } - // We use negative transaction guard to disable auto transaction from here - // and any stack below. This is to support user setting active transaction - // before having any existing AutoTransaction on stack, or 'persist' - // transaction that can out live AutoTransaction. - if (app._activeTransactionGuard < 0) { - --app._activeTransactionGuard; - } - else if (tid || app._activeTransactionGuard > 0) { - ++app._activeTransactionGuard; - } - else if (app.getActiveTransaction()) { - FC_LOG("auto transaction disabled because of '" << app._activeTransactionName << "'"); - --app._activeTransactionGuard; - } - else { - ++app._activeTransactionGuard; - } - FC_TRACE("construct auto Transaction " << app._activeTransactionGuard); + +} +AutoTransaction::AutoTransaction(Document* doc, const std::string& name) + : AutoTransaction(doc->openTransaction(name)) +{ + } AutoTransaction::~AutoTransaction() { - auto& app = GetApplication(); - FC_TRACE("before destruct auto Transaction " << app._activeTransactionGuard); - if (app._activeTransactionGuard < 0) { - ++app._activeTransactionGuard; - } - else if (!app._activeTransactionGuard) { -#ifdef FC_DEBUG - FC_ERR("Transaction guard error"); -#endif - } - else if (--app._activeTransactionGuard == 0) { - try { - // We don't call close() here, because close() only closes - // transaction that we opened during construction time. However, - // when _activeTransactionGuard reaches zero here, we are supposed - // to close any transaction opened. - app.closeActiveTransaction(); - } - catch (Base::Exception& e) { - e.reportException(); - } - catch (...) { - } - } - FC_TRACE("destruct auto Transaction " << app._activeTransactionGuard); + close(TransactionCloseMode::Commit); } -void AutoTransaction::close(bool abort) +void AutoTransaction::close(TransactionCloseMode mode) { - if (tid || abort) { - GetApplication().closeActiveTransaction(abort, abort ? 0 : tid); + if (tid != NullTransaction) { + GetApplication().closeActiveTransaction(mode, tid); tid = 0; } } -void AutoTransaction::setEnable(bool enable) +int Application::setActiveTransaction(TransactionName name) { - auto& app = GetApplication(); - if (!app._activeTransactionGuard) { - return; + if (name.name.empty()) { + name.name = "Command"; } - if ((enable && app._activeTransactionGuard > 0) - || (!enable && app._activeTransactionGuard < 0)) { - return; - } - app._activeTransactionGuard = -app._activeTransactionGuard; - FC_TRACE("toggle auto Transaction " << app._activeTransactionGuard); - if (!enable && app._activeTransactionTmpName) { - bool close = true; - for (auto& v : app.DocMap) { - if (v.second->hasPendingTransaction()) { - close = false; - break; - } - } - if (close) { - app.closeActiveTransaction(); - } + + if (_pActiveDoc != nullptr) { + return _pActiveDoc->setActiveTransaction(name); } + return openGlobalTransaction(name); } -int Application::setActiveTransaction(const char* name, bool persist) +std::string Application::getActiveTransaction(int* id) const { + if (id != nullptr) { + *id = _globalTransactionID; + } + return _globalTransactionID != 0 ? getTransactionName(_globalTransactionID) : ""; +} +int Application::openGlobalTransaction(TransactionName name) +{ + if (name.name.empty()) { + name.name = "Command"; + } - if (!name || !name[0]) { - name = "Command"; - } - - this->signalBeforeOpenTransaction(name); - if (_activeTransactionGuard > 0 && getActiveTransaction()) { - if (_activeTransactionTmpName) { - FC_LOG("transaction rename to '" << name << "'"); - for (auto& v : DocMap) { - v.second->renameTransaction(name, _activeTransactionID); + FC_WARN("Setting a global transaction with name='" << name.name); + if (_globalTransactionID != 0 && transactionTmpName(_globalTransactionID)) { + setTransactionName(_globalTransactionID, name); + } else { + FC_LOG("set global transaction '" << name.name << "'"); + + if (_globalTransactionID != 0 && !commitTransaction(_globalTransactionID)) { + FC_WARN("could not close current global transaction"); + return _globalTransactionID; + } + + _globalTransactionID = Transaction::getNewID(); + setTransactionDescription( + _globalTransactionID, + TransactionDescription { + .initiator = nullptr, + .name = name } - } - else { - if (persist) { - AutoTransaction::setEnable(false); - } - return 0; - } + ); } - else if (_TransactionLock) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Transaction locked, ignore new transaction '" << name << "'"); - } - return 0; + + return _globalTransactionID; +} +int Application::getGlobalTransaction() const +{ + return _globalTransactionID; +} +bool Application::transactionIsActive(int tid) const +{ + return transactionDescription(tid) != std::nullopt; +} +std::string Application::getTransactionName(int tid) const +{ + auto desc = transactionDescription(tid); + return desc ? desc->name.name : ""; +} +bool Application::transactionTmpName(int tid) const +{ + auto desc = transactionDescription(tid); + return desc ? desc->name.temporary : false; +} +Document* Application::transactionInitiator(int tid) const +{ + auto desc = transactionDescription(tid); + return desc ? desc->initiator : nullptr; +} +std::optional Application::transactionDescription(int tid) const +{ + if (tid == NullTransaction) { + return std::nullopt; } - else { - FC_LOG("set active transaction '" << name << "'"); - _activeTransactionID = 0; + auto found = _activeTransactionDescriptions.find(tid); + if (found != _activeTransactionDescriptions.end()) { + return std::optional(found->second); + } + return std::nullopt; +} +void Application::setTransactionDescription(int tid, const TransactionDescription& desc) +{ + if (tid == NullTransaction) { + return; + } + auto found = _activeTransactionDescriptions.find(tid); + bool wasPresent = (found != _activeTransactionDescriptions.end()); + + if (wasPresent && found->second.name.temporary) { for (auto& v : DocMap) { - v.second->_commitTransaction(); + v.second->renameTransaction(desc.name.name, tid); } - _activeTransactionID = Transaction::getNewID(); } - _activeTransactionTmpName = false; - _activeTransactionName = name; - if (persist) { - AutoTransaction::setEnable(false); + + if (!wasPresent || found->second.name.temporary) { + _activeTransactionDescriptions[tid] = desc; + FC_LOG("transaction rename to '" << desc.name.name << "'"); } - return _activeTransactionID; +} +void Application::setTransactionName(int tid, const TransactionName& name) +{ + if (tid == NullTransaction || !transactionIsActive(tid)) { + return; + } + auto found = _activeTransactionDescriptions.find(tid); + if (found == _activeTransactionDescriptions.end() || found->second.name.temporary) { + _activeTransactionDescriptions[tid].name = name; + FC_LOG("transaction rename to '" << name.name << "'"); + for (auto& v : DocMap) { + v.second->renameTransaction(name.name, tid); + } + } } -const char* Application::getActiveTransaction(int* id) const +bool Application::closeActiveTransaction(TransactionCloseMode mode, int id) { - int tid = 0; - if (Transaction::getLastID() == _activeTransactionID) { - tid = _activeTransactionID; - } - if (id) { - *id = tid; - } - return tid ? _activeTransactionName.c_str() : nullptr; -} - -void Application::closeActiveTransaction(bool abort, int id) -{ - if (!id) { - id = _activeTransactionID; - } - if (!id) { - return; - } - - if (_activeTransactionGuard > 0 && !abort) { - FC_LOG("ignore close transaction"); - return; - } - - if (_TransactionLock) { - if (_TransactionClosed >= 0) { - _TransactionLock = abort ? -1 : 1; + bool abort = (mode == TransactionCloseMode::Abort); + + if (id == NullTransaction) { + if (_pActiveDoc != nullptr && _pActiveDoc->getBookedTransactionID() != NullTransaction) { + id = _pActiveDoc->getBookedTransactionID(); + } else { + id = _globalTransactionID; } - FC_LOG("pending " << (abort ? "abort" : "close") << " transaction"); - return; } + if (id == NullTransaction || id == currentlyClosingID) { + return false; + } + currentlyClosingID = id; - FC_LOG("close transaction '" << _activeTransactionName << "' " << abort); - _activeTransactionID = 0; - - TransactionSignaller signaller(abort, false); - for (auto& v : DocMap) { - if (v.second->getTransactionID(true) != id) { + std::vector docsToPoke; + for (auto& docNameAndDoc : DocMap) { + if (docNameAndDoc.second->getBookedTransactionID() != id) { continue; } + if(docNameAndDoc.second->isTransactionLocked() || docNameAndDoc.second->transacting()) { + FC_LOG("pending " << (abort ? "abort" : "close") << " transaction"); + currentlyClosingID = 0; + return false; + } + if(docNameAndDoc.second->transacting()) { + FC_LOG("pending " << (abort ? "abort" : "close") << " transaction"); + currentlyClosingID = 0; + return false; + } + docsToPoke.push_back(docNameAndDoc.second); + } + + FC_LOG("close transaction '" << _activeTransactionDescriptions[id].name.name << "' " << abort); + _activeTransactionDescriptions.erase(id); + if (id == _globalTransactionID) { + _globalTransactionID = 0; + } + + TransactionSignaller signaller(abort, false); + + for (auto& doc : docsToPoke) { if (abort) { - v.second->_abortTransaction(); + doc->_abortTransaction(); } else { - v.second->_commitTransaction(); + doc->_commitTransaction(); } } + currentlyClosingID = 0; + + return true; +} +bool Application::commitTransaction(int tid) +{ + return closeActiveTransaction(TransactionCloseMode::Commit, tid); +} +bool Application::abortTransaction(int tid) +{ + return closeActiveTransaction(TransactionCloseMode::Abort, tid); } //////////////////////////////////////////////////////////////////////// -TransactionLocker::TransactionLocker(bool lock) +TransactionLocker::TransactionLocker(Document* doc, bool lock) : active(lock) + , doc(doc) { if (lock) { - ++_TransactionLock; + doc->lockTransaction(); } } @@ -267,22 +280,9 @@ void TransactionLocker::activate(bool enable) active = enable; if (active) { - ++_TransactionLock; + doc->lockTransaction(); return; } - if (--_TransactionLock != 0) { - return; - } - - if (_TransactionClosed) { - bool abort = (_TransactionClosed < 0); - _TransactionClosed = 0; - GetApplication().closeActiveTransaction(abort); - } -} - -bool TransactionLocker::isLocked() -{ - return _TransactionLock > 0; + doc->unlockTransaction(); } diff --git a/src/App/AutoTransaction.h b/src/App/AutoTransaction.h index d2cdd703f4..720ff73fc7 100644 --- a/src/App/AutoTransaction.h +++ b/src/App/AutoTransaction.h @@ -26,11 +26,15 @@ #include #include +#include + +#include "TransactionDefs.h" namespace App { class Application; +class Document; /** * @brief A helper class to manage transactions (i.e. undo/redo). @@ -45,59 +49,31 @@ public: void* operator new(std::size_t) = delete; public: - /** - * @brief Construct an auto transaction. - * - * @param[in] name: optional new transaction name on construction - * @param[in] tmpName: if true and a new transaction is setup, the name given is - * considered as temporary, and subsequent construction of this class (or - * calling Application::setActiveTransaction()) can override the transaction - * name. - * - * The constructor increments an internal counter - * (Application::_activeTransactionGuard). The counter prevents any new - * active transactions being setup. It also prevents to close - * (i.e. commits) the current active transaction until it reaches zero. It - * does not have any effect on aborting transactions though. + /** Constructor + * + * @param tid the ID of the transaction to manage + * + * No action is done in the constructor */ - AutoTransaction(const char* name = nullptr, bool tmpName = false); - - /** - * @brief Destruct an auto transaction. - * - * This destructor decrease an internal counter - * (Application::_activeTransactionGuard), and will commit any current - * active transaction when the counter reaches zero. + explicit AutoTransaction(int tid); + AutoTransaction(Document* doc, const std::string& name); + + /** Destructor + * + * This destructor attempts to commit the transaction it manages */ ~AutoTransaction(); /** * @brief Close or abort the transaction. * - * This function can be used to explicitly close (i.e. commit) the - * transaction, if the current transaction ID matches the one created inside - * the constructor. For aborting, it will abort any current transaction. - * - * @param[in] abort: if true, abort the transaction; otherwise, commit it. + * This function can be used to explicitly close (i.e. commit / abort) the + * transaction, */ - void close(bool abort = false); - - /** - * @brief Enable/Disable any AutoTransaction instance on the current stack. - * - * Once disabled, any empty temporary named transaction is closed. If there - * are non-empty or non-temporary named active transaction, it will not be - * auto closed. - * - * This function may be used in, for example, Gui::Document::setEdit() to - * allow a transaction live past any command scope. - * - * @param[in] enable: if true, enable the AutoTransaction; otherwise, disable it. - */ - static void setEnable(bool enable); + void close(TransactionCloseMode mode = TransactionCloseMode::Commit); private: - int tid = 0; + int tid { 0 }; }; @@ -116,7 +92,7 @@ public: * * @param[in] lock: whether to activate the lock */ - TransactionLocker(bool lock = true); + TransactionLocker(Document* doc, bool lock = true); /** * @brief Destruct a transaction locker. @@ -142,10 +118,7 @@ public: { return active; } - - /// Check if transaction is being locked. - static bool isLocked(); - + friend class Application; public: @@ -154,6 +127,7 @@ public: private: bool active; + Document* doc; }; } // namespace App \ No newline at end of file diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 8830be4367..fda7d13f45 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -190,6 +191,7 @@ bool Document::undo(const int id) mRedoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction; mRedoTransactions.push_back(d->activeUndoTransaction); d->activeUndoTransaction = nullptr; + d->bookedTransaction = 0; mUndoMap.erase(mUndoTransactions.back()->getID()); delete mUndoTransactions.back(); @@ -242,6 +244,7 @@ bool Document::redo(const int id) mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction; mUndoTransactions.push_back(d->activeUndoTransaction); d->activeUndoTransaction = nullptr; + d->bookedTransaction = 0; mRedoMap.erase(mRedoTransactions.back()->getID()); delete mRedoTransactions.back(); @@ -271,10 +274,10 @@ void Document::changePropertyOfObject(TransactionalObject* obj, } if ((d->iUndoMode != 0) && !isPerformingTransaction() && !d->activeUndoTransaction) { if (!testStatus(Restoring) || testStatus(Importing)) { - int tid = 0; - const char* name = GetApplication().getActiveTransaction(&tid); - if (name && tid > 0) { - _openTransaction(name, tid); + if (d->bookedTransaction == NullTransaction) { + d->bookedTransaction = GetApplication().getGlobalTransaction(); + } else { + _openTransaction(GetApplication().getTransactionName(d->bookedTransaction), d->bookedTransaction); } } } @@ -329,71 +332,92 @@ std::vector Document::getAvailableRedoNames() const return vList; } -void Document::openTransaction(const char* name) // NOLINT +int Document::openTransaction(TransactionName name, int tid) // NOLINT { - if (isPerformingTransaction() || d->committing) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Cannot open transaction while transacting"); - } - return; + if (tid != NullTransaction && tid == d->bookedTransaction) { + return tid; // Early exit without warning + } + if (isTransactionLocked()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Transaction locked, ignore new transaction '" << name.name << "'"); + } + return 0; } - - GetApplication().setActiveTransaction(name ? name : ""); -} - -int Document::_openTransaction(const char* name, int id) -{ if (isPerformingTransaction() || d->committing) { if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Cannot open transaction while transacting"); } return 0; } - - if (d->iUndoMode != 0) { - // Avoid recursive calls that is possible while - // clearing the redo transactions and will cause - // a double deletion of some transaction and thus - // a segmentation fault - if (d->opentransaction) { - return 0; - } - Base::FlagToggler<> flag(d->opentransaction); - - if ((id != 0) && mUndoMap.find(id) != mUndoMap.end()) { - throw Base::RuntimeError("invalid transaction id"); - } - if (d->activeUndoTransaction) { - _commitTransaction(true); - } - _clearRedos(); - - d->activeUndoTransaction = new Transaction(id); - if (!name) { - name = ""; - } - d->activeUndoTransaction->Name = name; - mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction; - id = d->activeUndoTransaction->getID(); - - signalOpenTransaction(*this, name); - - auto& app = GetApplication(); - auto activeDoc = app.getActiveDocument(); - if (activeDoc && activeDoc != this && !activeDoc->hasPendingTransaction()) { - std::string aname("-> "); - aname += d->activeUndoTransaction->Name; - FC_LOG("auto transaction " << getName() << " -> " << activeDoc->getName()); - activeDoc->_openTransaction(aname.c_str(), id); - } - return id; + if (name.name.empty()) { + name.name = ""; } - return 0; + return setActiveTransaction(name, tid); +} +int Document::openTransaction(std::string name, int tid) +{ + return openTransaction(TransactionName {.name = name, .temporary = false}, tid); } -void Document::renameTransaction(const char* name, const int id) const +int Document::_openTransaction(std::string name, int id) { - if (name && d->activeUndoTransaction && d->activeUndoTransaction->getID() == id) { + if (isTransactionLocked() && id != d->bookedTransaction) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Transaction locked, ignore new transaction '" << name << "'"); + } + } + if (isPerformingTransaction() || d->committing) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Cannot open transaction while transacting"); + } + return 0; + } + if (d->iUndoMode == 0) { + return 0; + } + + // Avoid recursive calls that is possible while + // clearing the redo transactions and will cause + // a double deletion of some transaction and thus + // a segmentation fault + if (d->opentransaction) { + return 0; + } + Base::FlagToggler<> flag(d->opentransaction); + + if ((id != 0) && mUndoMap.find(id) != mUndoMap.end()) { + throw Base::RuntimeError("invalid transaction id"); + } + if (d->activeUndoTransaction) { + _commitTransaction(true); + } + _clearRedos(); + + // When id == 0, this creates a new id + // for instance, when there is no global transaction + // from the application to stick to + d->activeUndoTransaction = new Transaction(id); + if (name.empty()) { + name = ""; + } + d->activeUndoTransaction->Name = name; + mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction; + id = d->activeUndoTransaction->getID(); + + signalOpenTransaction(*this, name); + + Document* transactionInitiator = GetApplication().transactionInitiator(id); + if (transactionInitiator && transactionInitiator != this && !transactionInitiator->hasPendingTransaction()) { + std::string aname = std::format("-> {}", d->activeUndoTransaction->Name); + FC_LOG("auto transaction " << getName() << " -> " << transactionInitiator->getName()); + transactionInitiator->_openTransaction(aname, id); + } + return id; +} + +void Document::renameTransaction(const std::string& name, const int id) const +{ + if (!name.empty() && d->activeUndoTransaction && d->activeUndoTransaction->getID() == id) { if (boost::starts_with(d->activeUndoTransaction->Name, "-> ")) { d->activeUndoTransaction->Name.resize(3); } @@ -403,48 +427,108 @@ void Document::renameTransaction(const char* name, const int id) const d->activeUndoTransaction->Name += name; } } +int Document::setActiveTransaction(TransactionName name, int tid) +{ + // Probably a group transaction situation + if (tid != NullTransaction) { + if (!GetApplication().transactionIsActive(tid)) { + FC_LOG("Could not set active transaction to inactive ID"); + return NullTransaction; + } + if (d->bookedTransaction != NullTransaction && d->bookedTransaction != tid && !_commitTransaction(true)) { + FC_LOG("Could not book transaction for document"); + return NullTransaction; + } + d->bookedTransaction = tid; + + if (GetApplication().transactionTmpName(d->bookedTransaction)) { + GetApplication().setTransactionName(d->bookedTransaction, name); + } + return d->bookedTransaction; + } + + // Rename the transaction if it had a tmp name + if (d->bookedTransaction != NullTransaction && GetApplication().transactionTmpName(d->bookedTransaction)) { + GetApplication().setTransactionName(d->bookedTransaction, name); + return d->bookedTransaction; + } + if (d->bookedTransaction != NullTransaction && !_commitTransaction(true)) { + FC_LOG("Could not book transaction for document"); + return NullTransaction; + } + d->bookedTransaction = Transaction::getNewID(); + + GetApplication().setTransactionDescription(d->bookedTransaction, TransactionDescription {.initiator = this, .name = name}); + return d->bookedTransaction; +} + +void Document::lockTransaction() +{ + d->TransactionLock++; +} +void Document::unlockTransaction() +{ + if (d->TransactionLock > 0) { + d->TransactionLock--; + } +} +bool Document::isTransactionLocked() const +{ + return d->TransactionLock > 0; +} +bool Document::transacting() const +{ + return isPerformingTransaction() || d->committing; +} void Document::_checkTransaction(DocumentObject* pcDelObj, const Property* What, int line) { // if the undo is active but no transaction open, open one! - if ((d->iUndoMode != 0) && !isPerformingTransaction()) { - if (!d->activeUndoTransaction) { - if (!testStatus(Restoring) || testStatus(Importing)) { - int tid = 0; - const char* name = GetApplication().getActiveTransaction(&tid); - if (name && tid > 0) { - bool ignore = false; - if (What && What->testStatus(Property::NoModify)) { - ignore = true; - } - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - if (What) { - FC_LOG((ignore ? "ignore" : "auto") - << " transaction (" << line << ") '" << What->getFullName()); - } - else { - FC_LOG((ignore ? "ignore" : "auto") << " transaction (" << line << ") '" - << name << "' in " << getName()); - } - } - if (!ignore) { - _openTransaction(name, tid); - } - return; + if (d->iUndoMode == 0 || isPerformingTransaction() || d->activeUndoTransaction) { + return; + } + + if (!testStatus(Restoring) || testStatus(Importing)) { + + // Priority to a transaction that has been booked + // explicitly for this document, it there are none + // get a sticky transaction from application + if (!d->bookedTransaction) { + d->bookedTransaction = GetApplication().getGlobalTransaction(); + } + + if (d->bookedTransaction != NullTransaction) { + std::string name = GetApplication().getTransactionName(d->bookedTransaction); + bool ignore = false; + if (What && What->testStatus(Property::NoModify)) { + ignore = true; + } + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + if (What) { + FC_LOG((ignore ? "ignore" : "auto") + << " transaction (" << line << ") '" << What->getFullName()); + } + else { + FC_LOG((ignore ? "ignore" : "auto") << " transaction (" << line << ") '" + << name << "' in " << getName()); } } - if (!pcDelObj) { - return; - } - // When the object is going to be deleted we have to check if it has already been added - // to the undo transactions - std::list::iterator it; - for (it = mUndoTransactions.begin(); it != mUndoTransactions.end(); ++it) { - if ((*it)->hasObject(pcDelObj)) { - _openTransaction("Delete"); - break; - } + if (!ignore) { + _openTransaction(name, d->bookedTransaction); } + return; + } + } + if (!pcDelObj) { + return; + } + // When the object is going to be deleted we have to check if it has already been added + // to the undo transactions + std::list::iterator it; + for (it = mUndoTransactions.begin(); it != mUndoTransactions.end(); ++it) { + if ((*it)->hasObject(pcDelObj)) { + _openTransaction("Delete"); + break; } } } @@ -473,29 +557,36 @@ void Document::commitTransaction() // NOLINT } if (d->activeUndoTransaction) { - GetApplication().closeActiveTransaction(false, d->activeUndoTransaction->getID()); + // This will iterate over all documents and ask them to + // commit their transaction if their ID matches + GetApplication().commitTransaction(d->activeUndoTransaction->getID()); + } else { + d->bookedTransaction = 0; // Reset booked transaction even if it was not used } } -void Document::_commitTransaction(const bool notify) +bool Document::_commitTransaction(const bool notify) { if (isPerformingTransaction()) { if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Cannot commit transaction while transacting"); } - return; + return false; } if (d->committing) { // for a recursive call return without printing a warning - return; + return false; } + d->bookedTransaction = 0; if (d->activeUndoTransaction) { Base::FlagToggler<> flag(d->committing); Application::TransactionSignaller signaller(false, true); const int id = d->activeUndoTransaction->getID(); + mUndoTransactions.push_back(d->activeUndoTransaction); d->activeUndoTransaction = nullptr; + // check the stack for the limits if (mUndoTransactions.size() > d->UndoMaxStackSize) { mUndoMap.erase(mUndoTransactions.front()->getID()); @@ -504,11 +595,12 @@ void Document::_commitTransaction(const bool notify) } signalCommitTransaction(*this); - // closeActiveTransaction() may call again _commitTransaction() + // commitTransaction() may call again _commitTransaction() if (notify) { - GetApplication().closeActiveTransaction(false, id); + GetApplication().commitTransaction(id); } } + return true; } void Document::abortTransaction() const @@ -520,7 +612,9 @@ void Document::abortTransaction() const return; } if (d->activeUndoTransaction) { - GetApplication().closeActiveTransaction(true, d->activeUndoTransaction->getID()); + GetApplication().abortTransaction(d->activeUndoTransaction->getID()); + } else { + d->bookedTransaction = 0; // Reset booked transaction even if it was not used } } @@ -532,6 +626,7 @@ void Document::_abortTransaction() } } + d->bookedTransaction = 0; if (d->activeUndoTransaction) { Base::FlagToggler flag(d->rollback); Application::TransactionSignaller signaller(true, true); @@ -575,7 +670,10 @@ int Document::getTransactionID(const bool undo, unsigned pos) const for (; pos != 0U; ++rit, --pos) {} return (*rit)->getID(); } - +int Document::getBookedTransactionID() const +{ + return d->bookedTransaction; +} bool Document::isTransactionEmpty() const { return !d->activeUndoTransaction; @@ -3312,7 +3410,7 @@ void Document::_removeObject(DocumentObject* pcObject, RemoveObjectOptions optio return; } - TransactionLocker tlock; + TransactionLocker tlock(this); _checkTransaction(pcObject, nullptr, __LINE__); diff --git a/src/App/Document.h b/src/App/Document.h index 5e64b72db4..3c2665c9fc 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -35,6 +35,7 @@ #include "PropertyLinks.h" #include "PropertyStandard.h" #include "ExportInfo.h" +#include "TransactionDefs.h" #include #include @@ -865,7 +866,19 @@ public: * setup a potential transaction that will only be created if there are * actual changes. */ - void openTransaction(const char* name = nullptr); + int openTransaction(TransactionName name, int tid = 0); + int openTransaction(std::string name, int tid = 0); + + // If the tid != 0, it will take the transaction id if it exists + int setActiveTransaction(TransactionName name, int tid = 0); + + void lockTransaction(); + void unlockTransaction(); + bool isTransactionLocked() const; + + bool transacting() const; + + int getBookedTransactionID() const; /** * @brief Rename the current transaction. @@ -875,7 +888,7 @@ public: * @param[in] name The new name of the transaction. * @param[in] id The transaction ID to match. */ - void renameTransaction(const char* name, int id) const; + void renameTransaction(const std::string& name, int id) const; /** * @brief Commit the Command transaction. @@ -1244,7 +1257,7 @@ public: /// Check if there is any document restoring/importing. static bool isAnyRestoring(); - + /// Register a new label. void registerLabel(const std ::string& newLabel); /// Unregister a label. @@ -1409,8 +1422,7 @@ protected: * This function creates an actual transaction regardless of Application * AutoTransaction setting. */ - int _openTransaction(const char* name = nullptr, int id = 0); - + int _openTransaction(std::string name = "", int id = 0); /** * @brief Commit the Command transaction. * @@ -1419,8 +1431,7 @@ protected: * * @param notify If true, notify the application to close the transaction. */ - void _commitTransaction(bool notify = false); - + bool _commitTransaction(bool notify = false); /** * @brief Abort the running transaction. * diff --git a/src/App/Document.pyi b/src/App/Document.pyi index 2b9943a4d5..61ce6a249c 100644 --- a/src/App/Document.pyi +++ b/src/App/Document.pyi @@ -447,3 +447,12 @@ class Document(PropertyContainer): sort: whether to topologically sort the return list """ ... + + def getBookedTransactionID(self) -> int: + """ + getBookedTransactionID() -> int + + Returns the currently booked transaction id, which is the id of the current transaction OR the id + the next transaction will stick to if no change has occured yet + """ + ... \ No newline at end of file diff --git a/src/App/DocumentPyImp.cpp b/src/App/DocumentPyImp.cpp index 55d767b1d8..9737338708 100644 --- a/src/App/DocumentPyImp.cpp +++ b/src/App/DocumentPyImp.cpp @@ -1175,6 +1175,15 @@ PyObject* DocumentPy::getDependentDocuments(PyObject* args) } PY_CATCH; } +PyObject* DocumentPy::getBookedTransactionID(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) { + return nullptr; + } + int tid = getDocumentPtr()->getBookedTransactionID(); + return Py::new_reference_to(Py::Long(tid)); +} + Py::Boolean DocumentPy::getRestoring() const { diff --git a/src/App/PropertyStandard.cpp b/src/App/PropertyStandard.cpp index a2cf11b440..6797462a9f 100644 --- a/src/App/PropertyStandard.cpp +++ b/src/App/PropertyStandard.cpp @@ -1546,11 +1546,11 @@ void PropertyString::setValue(const char* newValue) // OnProposedLabelChange has changed the new value to what the current value is return; } - if (!propChanges.empty() && !GetApplication().getActiveTransaction()) { + if (!propChanges.empty() && obj->getDocument()->getBookedTransactionID() == 0) { commit = true; std::ostringstream str; str << "Change " << obj->getNameInDocument() << ".Label"; - GetApplication().setActiveTransaction(str.str().c_str()); + obj->getDocument()->openTransaction(str.str().c_str()); } } @@ -1563,7 +1563,7 @@ void PropertyString::setValue(const char* newValue) } if (commit) { - GetApplication().closeActiveTransaction(); + obj->getDocument()->commitTransaction(); } } diff --git a/src/App/TransactionDefs.h b/src/App/TransactionDefs.h new file mode 100644 index 0000000000..8883c0b033 --- /dev/null +++ b/src/App/TransactionDefs.h @@ -0,0 +1,35 @@ + +#ifndef APP_TRANSACTIONDEFS_H_ +#define APP_TRANSACTIONDEFS_H_ + +#include + +namespace App { + +class Document; + +constexpr int NullTransaction = 0; + +struct TransactionName { + // Name of the transaction as it will appear in the GUI + std::string name; + + // If true, the transaction is allowed to be renamed + bool temporary { false }; +}; + +struct TransactionDescription { + // Document on which the transaction was first created + // useful for mass transaction (e.g. delete from an assembly) + // where other documents may use the same transaction id + Document* initiator { nullptr }; + TransactionName name; +}; + +enum class TransactionCloseMode { + Commit = 0, + Abort = 1, +}; +} + +#endif \ No newline at end of file diff --git a/src/App/Transactions.cpp b/src/App/Transactions.cpp index c840b3e76a..d48fa2d071 100644 --- a/src/App/Transactions.cpp +++ b/src/App/Transactions.cpp @@ -556,4 +556,4 @@ TransactionObject* TransactionFactory::createTransaction(const Base::Type& type) Base::Console().log("Cannot create transaction object from %s\n", type.getName()); return nullptr; -} +} \ No newline at end of file diff --git a/src/App/Transactions.h b/src/App/Transactions.h index 738fb6b025..d03b9bf128 100644 --- a/src/App/Transactions.h +++ b/src/App/Transactions.h @@ -30,6 +30,7 @@ #include #include #include +#include "TransactionDefs.h" #include #include @@ -61,6 +62,7 @@ class AppExport Transaction: public Base::Persistence { TYPESYSTEM_HEADER_WITH_OVERRIDE(); + public: /** * @brief Construct a transaction. @@ -71,7 +73,7 @@ public: * transactions from different document, so that they can be undone/redone * together. */ - explicit Transaction(int id = 0); + explicit Transaction(int id = NullTransaction); ~Transaction() override; diff --git a/src/App/private/DocumentP.h b/src/App/private/DocumentP.h index 66fe180a0d..3490e2ad07 100644 --- a/src/App/private/DocumentP.h +++ b/src/App/private/DocumentP.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,11 @@ struct DocumentP int iUndoMode {0}; unsigned int UndoMemSize {0}; unsigned int UndoMaxStackSize {20}; + unsigned int TransactionLock {0}; + // Id and name that the next transaction will take + // as soon as there is a change to the document + int bookedTransaction { 0 }; + std::string programVersion; mutable HasherMap hashers; std::multimap> diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index fb74c7bd23..0fe22dd343 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -227,7 +227,7 @@ struct ApplicationP std::map documents; /// Active document Gui::Document* activeDocument {nullptr}; - Gui::Document* editDocument {nullptr}; + std::vector editDocuments; MacroManager* macroMngr; PreferencePackManager* prefPackManager; @@ -1285,6 +1285,9 @@ void Application::slotActiveDocument(const App::Document& Doc) Py::Module("FreeCADGui").setAttr(std::string("ActiveDocument"), Py::None()); } } + if (!d->activeDocument->workbench().empty()) { + activateWorkbench(d->activeDocument->workbench().c_str()); + } // Update the application to show the unit change ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( @@ -1491,26 +1494,62 @@ Gui::Document* Application::activeDocument() const Gui::Document* Application::editDocument() const { - return d->editDocument; -} - -Gui::MDIView* Application::editViewOfNode(SoNode* node) const -{ - return d->editDocument ? d->editDocument->getViewOfNode(node) : nullptr; -} - -void Application::setEditDocument(Gui::Document* doc) -{ - if (!doc) { - d->editDocument = nullptr; + if (d->editDocuments.empty()) { + return nullptr; } - else if (doc == d->editDocument) { + return d->editDocuments[0]; +} +Gui::Document* Application::editDocument(const std::function& eval) +{ + auto found = std::ranges::find_if(d->editDocuments, eval); + + return found == d->editDocuments.end() ? nullptr : *found; +} +std::vector Application::editDocuments() const +{ + return d->editDocuments; +} +bool Application::isInEdit(Gui::Document* pcDocument) const +{ + return std::ranges::find(d->editDocuments, pcDocument) != d->editDocuments.end(); +} +void Application::unsetEditDocument(Gui::Document* pcDocument) +{ + if (std::erase(d->editDocuments, pcDocument) == 0) { return; } - for (auto& v : d->documents) { - v.second->_resetEdit(); + + pcDocument->_resetEdit(); + updateActions(); +} +void Application::unsetEditDocumentIf(const std::function& eval) +{ + for (auto editDoc : d->editDocuments) { + if (eval(editDoc)) { + unsetEditDocument(editDoc); + } } - d->editDocument = doc; +} +Gui::MDIView* Application::editViewOfNode(SoNode* node) const +{ + for (auto editDoc : d->editDocuments) { + if (Gui::MDIView* view = editDoc->getViewOfNode(node)) { + return view; + } + } + return nullptr; +} + +void Application::setEditDocument(Gui::Document* pcDocument) +{ + if (pcDocument == nullptr) { + return; + } + if (std::ranges::find(d->editDocuments, pcDocument) != d->editDocuments.end()) { + return; + } + d->editDocuments.push_back(pcDocument); + updateActions(); } @@ -1531,12 +1570,18 @@ void Application::setActiveDocument(Gui::Document* pcDocument) return; } } + if (d->activeDocument) { + d->activeDocument->setIsActive(false); + } d->activeDocument = pcDocument; + std::string nameApp, nameGui; // This adds just a line to the macro file but does not set the active document // Macro recording of this is problematic, thus it's written out as comment. if (pcDocument) { + pcDocument->setIsActive(true); + nameApp += "App.setActiveDocument(\""; nameApp += pcDocument->getDocument()->getName(); nameApp += "\")\n"; @@ -1893,6 +1938,9 @@ bool Application::activateWorkbench(const char* name) } newWb->activated(); } + if (activeDocument()) { + activeDocument()->setWorkbench(name); + } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text diff --git a/src/Gui/Application.h b/src/Gui/Application.h index b99a740b24..01f7d8e2ab 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -181,11 +181,26 @@ public: Gui::Document* activeDocument() const; /// Set the active document void setActiveDocument(Gui::Document* pcDocument); - /// Getter for the editing document + + /// Getter for the editing document, will be removed soon Gui::Document* editDocument() const; + /// Getter for the first editing document that matches a functor + Gui::Document* editDocument(const std::function& eval); + /// Getter for all currently editing documents, all pointers are guaranteed to be non-null + std::vector editDocuments() const; + + // Returns true if the document is in edit (will make more sense once the edit document it is a + // vector) + bool isInEdit(Gui::Document* pcDocument) const; + // Reset edit if eval returns true for a document in edit + Gui::MDIView* editViewOfNode(SoNode* node) const; - /// Set editing document, which will reset editing of all other document + /// Adds a document in edit void setEditDocument(Gui::Document* pcDocument); + // After this, isInEdit(pcDocument) returns false + void unsetEditDocument(Gui::Document* pcDocument); + void unsetEditDocumentIf(const std::function& eval); + /** Retrieves a pointer to the Gui::Document whose App::Document has the name \a name. * If no such document exists 0 is returned. */ diff --git a/src/Gui/Command.cpp b/src/Gui/Command.cpp index 9e35ab0eff..4e76a867c3 100644 --- a/src/Gui/Command.cpp +++ b/src/Gui/Command.cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include #include @@ -45,6 +45,7 @@ #include "Command.h" #include "Action.h" +#include "App/Application.h" #include "Application.h" #include "BitmapFactory.h" #include "Control.h" @@ -447,11 +448,6 @@ void Command::invoke(int i, TriggerSource trigger) void Command::_invoke(int id, bool disablelog) { try { - // Because Transaction now captures ViewObject changes, auto named - // transaction is disabled here to avoid too many unnecessary transactions. - // - App::AutoTransaction committer(nullptr, true); - // set the application module type for the macro getGuiApplication()->macroManager()->setModule(sAppModule); @@ -463,7 +459,6 @@ void Command::_invoke(int id, bool disablelog) // check if it really works NOW (could be a delay between click deactivation of the button) if (isActive()) { auto manager = getGuiApplication()->macroManager(); - auto editDoc = getGuiApplication()->editDocument(); if (!logdisabler) { activated(id); @@ -499,12 +494,10 @@ void Command::_invoke(int id, bool disablelog) } getMainWindow()->updateActions(); - - // If this command starts an editing, let the transaction persist - if (!editDoc && getGuiApplication()->editDocument()) { - committer.setEnable(false); - } } + // here we assume that the overriden activated() function + // commited, aborted or gave the transaction id to a dialog + currentTransactionID = App::NullTransaction; // Get ready for next invoke } catch (const Base::SystemExitException&) { throw; @@ -550,9 +543,10 @@ void Command::testActive() if (!(eType & ForEdit)) { // special case for commands which are only in some edit modes active - if ((!Gui::Control().isAllowedAlterDocument() && eType & AlterDoc) - || (!Gui::Control().isAllowedAlterView() && eType & Alter3DView) - || (!Gui::Control().isAllowedAlterSelection() && eType & AlterSelection)) { + App::Document* doc = getDocument(); + if ((!Gui::Control().isAllowedAlterDocument(doc) && eType & AlterDoc) + || (!Gui::Control().isAllowedAlterView(doc) && eType & Alter3DView) + || (!Gui::Control().isAllowedAlterSelection(doc) && eType & AlterSelection)) { _pcAction->setEnabled(false); return; } @@ -681,27 +675,67 @@ QString Command::translatedGroupName() const * operation default is the Command name. * @see CommitCommand(),AbortCommand() */ -void Command::openCommand(const char* sCmdName) +int Command::openCommand(App::TransactionName name) { - if (!sCmdName) { - sCmdName = "Command"; + currentTransactionID = openActiveDocumentCommand(name); + return currentTransactionID; +} +int Command::openCommand(std::string name) +{ + return openCommand(App::TransactionName {.name = name, .temporary = false}); +} +int Command::openActiveDocumentCommand(App::TransactionName name, int tid) +{ + if (Gui::Document* guidoc = getGuiApplication()->activeDocument()) { + return guidoc->getDocument()->setActiveTransaction(name, tid); } - App::GetApplication().setActiveTransaction(sCmdName); + return 0; +} +int Command::openActiveDocumentCommand(std::string name, int tid) +{ + return openActiveDocumentCommand(App::TransactionName {.name = name, .temporary = false}, tid); +} +void Command::rename(const std::string& name) +{ + App::GetApplication().setTransactionName( + currentTransactionID, + App::TransactionName {.name = name, .temporary = false} + ); } void Command::commitCommand() { - App::GetApplication().closeActiveTransaction(); + commitCommand(currentTransactionID); + currentTransactionID = App::NullTransaction; +} +void Command::commitCommand(int tid) +{ + if (tid != App::NullTransaction) { + App::GetApplication().commitTransaction(tid); + } } - void Command::abortCommand() { - App::GetApplication().closeActiveTransaction(true); + abortCommand(currentTransactionID); + currentTransactionID = App::NullTransaction; +} +void Command::abortCommand(int tid) +{ + if (tid != App::NullTransaction) { + App::GetApplication().abortTransaction(tid); + } +} +int Command::transactionID() const +{ + return currentTransactionID; +} +void Command::resetTransactionID() +{ + currentTransactionID = App::NullTransaction; } - bool Command::hasPendingCommand() { - return !!App::GetApplication().getActiveTransaction(); + return App::GetApplication().getActiveDocument()->getBookedTransactionID() != 0; } bool Command::_blockCmd = false; diff --git a/src/Gui/Command.h b/src/Gui/Command.h index 939d8c78d2..8295561ca6 100644 --- a/src/Gui/Command.h +++ b/src/Gui/Command.h @@ -473,11 +473,24 @@ public: /** @name Helper methods for the Undo/Redo and Update handling */ //@{ /// Open a new Undo transaction on the active document - static void openCommand(const char* sName = nullptr); + int openCommand(App::TransactionName name); + int openCommand(std::string name); + static int openActiveDocumentCommand(App::TransactionName name, int tid = App::NullTransaction); + static int openActiveDocumentCommand(std::string name, int tid = App::NullTransaction); + + void rename(const std::string& name); + /// Commit the Undo transaction on the active document - static void commitCommand(); + void commitCommand(); + static void commitCommand(int tid); + /// Abort the Undo transaction on the active document - static void abortCommand(); + void abortCommand(); + static void abortCommand(int tid); + + int transactionID() const; + void resetTransactionID(); + /// Check if an Undo transaction is open on the active document static bool hasPendingCommand(); /// Updates the (active) document (propagate changes) @@ -715,6 +728,8 @@ protected: /// Indicate if the command shall log to MacroManager bool bCanLog; //@} + + int currentTransactionID {0}; // TransactionID created in _invoke private: static int _busy; bool bEnabled; diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index aa0f1e297d..7e4ada2e52 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -710,6 +710,7 @@ StdCmdNew::StdCmdNew() sStatusTip = sToolTipText; sPixmap = "document-new"; sAccel = keySequenceToAccel(QKeySequence::New); + eType = NoTransaction; } void StdCmdNew::activated(int iMsg) @@ -1446,34 +1447,50 @@ void StdCmdDelete::activated(int iMsg) { Q_UNUSED(iMsg); - std::set docs; + int tid = 0; try { - openCommand(QT_TRANSLATE_NOOP("Command", "Delete")); + std::set docs; + std::vector tlocks; + auto manageDocCommand = [&tid, &tlocks](App::Document* doc) { + // The tid will not be updated if non-zero + tid = doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Delete"), tid); + tlocks.emplace_back(doc); + }; + if (getGuiApplication()->sendHasMsgToFocusView(getName())) { - commitCommand(); + // no command has been opened yet so we can skip this commit + // commitCommand(); return; } - - App::TransactionLocker tlock; + // Ensure that the document from which we send the command + // can undo it (e.g delete a subobject of an assembly + // from the assembly file) + manageDocCommand(getActiveGuiDocument()->getDocument()); Gui::getMainWindow()->setUpdatesEnabled(false); - auto editDoc = Application::Instance->editDocument(); - ViewProviderDocumentObject* vpedit = nullptr; - if (editDoc) { - vpedit = freecad_cast(editDoc->getInEdit()); - } - if (vpedit && !vpedit->acceptDeletionsInEdit()) { - for (auto& sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) { - if (sel.getObject() == vpedit->getObject()) { - if (!sel.getSubNames().empty()) { - vpedit->onDelete(sel.getSubNames()); - docs.insert(editDoc->getDocument()); + + bool deletedSelectionOfEditDocument = false; + std::vector editDocs = Application::Instance->editDocuments(); + for (auto& editDoc : editDocs) { + auto vpedit = freecad_cast(editDoc->getInEdit()); + + // In practice, no ViewProviderDocumentObject accepts deletion in edit - 2025-06-17 + if (vpedit && !vpedit->acceptDeletionsInEdit()) { + for (auto& sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) { + if (sel.getObject() == vpedit->getObject()) { + if (!sel.getSubNames().empty()) { + deletedSelectionOfEditDocument = true; + manageDocCommand(editDoc->getDocument()); + vpedit->onDelete(sel.getSubNames()); + docs.insert(editDoc->getDocument()); + } + break; } - break; } } } - else { + + if (!deletedSelectionOfEditDocument) { std::set affectedLabels; bool more = false; auto sels = Selection().getSelectionEx(); @@ -1548,6 +1565,7 @@ void StdCmdDelete::activated(int iMsg) auto obj = sel.getObject(); Gui::ViewProvider* vp = Application::Instance->getViewProvider(obj); if (vp) { + manageDocCommand(obj->getDocument()); // ask the ViewProvider if it wants to do some clean up if (vp->onDelete(sel.getSubNames())) { docs.insert(obj->getDocument()); @@ -1581,6 +1599,8 @@ void StdCmdDelete::activated(int iMsg) QString::fromLatin1(e.what()) ); e.reportException(); + App::GetApplication().abortTransaction(tid); + tid = 0; } catch (...) { QMessageBox::critical( @@ -1588,8 +1608,11 @@ void StdCmdDelete::activated(int iMsg) QObject::tr("Delete Failed"), QStringLiteral("Unknown error") ); + App::GetApplication().abortTransaction(tid); + tid = 0; } - commitCommand(); + + App::GetApplication().commitTransaction(tid); Gui::getMainWindow()->setUpdatesEnabled(true); Gui::getMainWindow()->update(); } @@ -1635,7 +1658,8 @@ void StdCmdRefresh::activated([[maybe_unused]] int iMsg) return; } - App::AutoTransaction trans((eType & NoTransaction) ? nullptr : "Recompute"); + App::AutoTransaction trans((eType & NoTransaction) ? 0 : openActiveDocumentCommand("Recompute")); + try { doCommand(Doc, "App.activeDocument().recompute(None,True,True)"); } @@ -1738,7 +1762,7 @@ void StdCmdPlacement::activated(int iMsg) plm->clearSelection(); } } - Gui::Control().showDialog(plm); + Gui::Control().showDialog(plm, getDocument()); } bool StdCmdPlacement::isActive() @@ -2133,9 +2157,10 @@ protected: return; } - openCommand(QT_TRANSLATE_NOOP("Command", "Paste expressions")); + int tid = App::NullTransaction; try { for (auto& v : exprs) { + tid = v.first->openTransaction(QT_TRANSLATE_NOOP("Command", "Paste expressions"), tid); for (auto& v2 : v.second) { auto& expressions = v2.second; auto old = v2.first->getExpressions(); @@ -2152,13 +2177,13 @@ protected: } } } - commitCommand(); + App::GetApplication().commitTransaction(tid); } catch (const Base::Exception& e) { - abortCommand(); + App::GetApplication().abortTransaction(tid); QMessageBox::critical( getMainWindow(), - QObject::tr("Failed to Paste Expressions"), + QObject::tr("Failed to paste expressions"), QString::fromLatin1(e.what()) ); e.reportException(); diff --git a/src/Gui/CommandLink.cpp b/src/Gui/CommandLink.cpp index bd9e25e002..71ac668969 100644 --- a/src/Gui/CommandLink.cpp +++ b/src/Gui/CommandLink.cpp @@ -476,7 +476,7 @@ static void linkConvert(bool unlink) // now, do actual operation const char* transactionName = unlink ? "Unlink" : "Replace with link"; - Command::openCommand(transactionName); + int tid = 0; try { std::unordered_map recomputeSet; for (auto& v : infos) { @@ -493,6 +493,12 @@ static void linkConvert(bool unlink) recomputeSet.emplace(parent, parent); } auto doc = parent->getDocument(); + + tid = doc->openTransaction( + App::TransactionName {.name = transactionName, .temporary = false}, + tid + ); + App::DocumentObject* replaceObj; if (unlink) { replaceObj = obj->getLinkedObject(false); @@ -552,10 +558,10 @@ static void linkConvert(bool unlink) recomputes.front()->getDocument()->recompute(recomputes); } - Command::commitCommand(); + App::GetApplication().commitTransaction(tid); } catch (const Base::Exception& e) { - Command::abortCommand(); + App::GetApplication().abortTransaction(tid); auto title = unlink ? QObject::tr("Unlink failed") : QObject::tr("Replace link failed"); QMessageBox::critical(getMainWindow(), title, QString::fromLatin1(e.what())); e.reportException(); diff --git a/src/Gui/CommandStructure.cpp b/src/Gui/CommandStructure.cpp index 194f3629dc..b75b83b4f8 100644 --- a/src/Gui/CommandStructure.cpp +++ b/src/Gui/CommandStructure.cpp @@ -101,6 +101,8 @@ void StdCmdPart::activated(int iMsg) PartName.c_str() ); + commitCommand(); + updateActive(); } diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index aefbcc72df..c3b1e1948e 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -3431,7 +3431,7 @@ StdCmdTextureMapping::StdCmdTextureMapping() void StdCmdTextureMapping::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Control().showDialog(new Gui::Dialog::TaskTextureMapping); + Gui::Control().showDialog(new Gui::Dialog::TaskTextureMapping, getDocument()); } bool StdCmdTextureMapping::isActive() diff --git a/src/Gui/Control.cpp b/src/Gui/Control.cpp index ea1ca91c91..671204bf00 100644 --- a/src/Gui/Control.cpp +++ b/src/Gui/Control.cpp @@ -26,10 +26,12 @@ #include #include -#include +#include +#include #include #include #include +#include #include "Control.h" #include "BitmapFactory.h" @@ -45,8 +47,7 @@ using namespace std; ControlSingleton* ControlSingleton::_pcSingleton = nullptr; ControlSingleton::ControlSingleton() - : ActiveDialog(nullptr) - , oldTabIndex(-1) + : oldTabIndex(-1) {} ControlSingleton::~ControlSingleton() = default; @@ -145,14 +146,28 @@ void ControlSingleton::showModelView() } } -void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg) +void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg, App::Document* attachTo) { + attachTo = docOrDefault(attachTo); + if (!attachTo) { + qWarning() << "ControlSingleton::showDialog: Cannot attach to nullptr document"; + return; + } + + Gui::TaskView::TaskView* taskView = taskPanel(); + // should return the pointer to combo view + if (!taskView) { + return; + } + // only one dialog at a time, print a warning instead of raising an assert - if (ActiveDialog && ActiveDialog != dlg) { + TaskView::TaskDialog* foundDialog = taskView->dialog(attachTo); + if (!dlg || foundDialog == dlg) { if (dlg) { qWarning() << "ControlSingleton::showDialog: Can't show " << dlg->metaObject()->className() - << " since there is already an active task dialog"; + << " since there is already an active task dialog in Document " + << (attachTo ? attachTo->getName() : "''"); } else { qWarning() << "ControlSingleton::showDialog: Task dialog is null"; @@ -160,69 +175,87 @@ void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg) return; } - // Since the caller sets up a modeless task panel, it indicates intention - // for prolonged editing. So disable auto transaction in the current call - // stack. - // Do this before showing the dialog because its open() function is called - // which may open a transaction but fails when auto transaction is still active. - App::AutoTransaction::setEnable(false); + bool addedDialog = taskView->showDialog(dlg, attachTo); - Gui::TaskView::TaskView* taskView = taskPanel(); - // should return the pointer to combo view - if (taskView) { - taskView->showDialog(dlg); - - // make sure that the combo view is shown - auto dw = qobject_cast(taskView->parentWidget()); - if (dw) { - aboutToShowDialog(dw); - dw->setVisible(true); - dw->toggleViewAction()->setVisible(true); - dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); - } - - if (ActiveDialog == dlg) { - return; // dialog is already defined - } - ActiveDialog = dlg; - connect(dlg, &TaskView::TaskDialog::aboutToBeDestroyed, this, &ControlSingleton::closedDialog); + // make sure that the combo view is shown + if (auto dw = qobject_cast(taskView->parentWidget())) { + aboutToShowDialog(dw); + dw->setVisible(true); + dw->toggleViewAction()->setVisible(true); + dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); } + + if (!addedDialog) { + return; // dialog is already defined + } + + connect(dlg, &TaskView::TaskDialog::aboutToBeDestroyed, this, [this, attachTo] { + closedDialog(attachTo); + }); } -Gui::TaskView::TaskDialog* ControlSingleton::activeDialog() const +Gui::TaskView::TaskDialog* ControlSingleton::activeDialog(App::Document* attachedTo) const { - return ActiveDialog; + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + return nullptr; + } + + Gui::TaskView::TaskView* taskView = taskPanel(); + + if (taskView) { + return taskView->dialog(attachedTo); + } + return nullptr; } -void ControlSingleton::accept() +void ControlSingleton::accept(App::Document* attachedTo) { + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + qWarning() << "ControlSingleton::accept: Cannot accept dialog of nullptr document"; + return; + } + Gui::TaskView::TaskView* taskView = taskPanel(); if (taskView) { - taskView->accept(); + taskView->accept(attachedTo); qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); } } -void ControlSingleton::reject() +void ControlSingleton::reject(App::Document* attachedTo) { + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + qWarning() << "ControlSingleton::reject: Cannot reject dialog of nullptr document"; + return; + } + + Gui::TaskView::TaskView* taskView = taskPanel(); if (taskView) { - taskView->reject(); + taskView->reject(attachedTo); qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); } } -void ControlSingleton::closeDialog() +void ControlSingleton::closeDialog(App::Document* attachedTo) { + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + qWarning() << "ControlSingleton::closeDialog: Cannot close dialog of nullptr document"; + return; + } + Gui::TaskView::TaskView* taskView = taskPanel(); if (taskView) { - taskView->removeDialog(); + taskView->removeDialog(attachedTo); } } -void ControlSingleton::closedDialog() +void ControlSingleton::closedDialog(App::Document* attachedTo) { - ActiveDialog = nullptr; Gui::TaskView::TaskView* taskView = taskPanel(); assert(taskView); @@ -237,31 +270,57 @@ void ControlSingleton::closedDialog() } } -bool ControlSingleton::isAllowedAlterDocument() const +bool ControlSingleton::isAllowedAlterDocument(App::Document* attachedTo) const { - if (ActiveDialog) { - return ActiveDialog->isAllowedAlterDocument(); + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + return true; + } + Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo); + + if (dlg) { + return dlg->isAllowedAlterDocument(); } return true; } - -bool ControlSingleton::isAllowedAlterView() const +bool ControlSingleton::isAllowedAlterView(App::Document* attachedTo) const { - if (ActiveDialog) { - return ActiveDialog->isAllowedAlterView(); + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + return true; + } + Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo); + + if (dlg) { + return dlg->isAllowedAlterView(); } return true; } -bool ControlSingleton::isAllowedAlterSelection() const +bool ControlSingleton::isAllowedAlterSelection(App::Document* attachedTo) const { - if (ActiveDialog) { - return ActiveDialog->isAllowedAlterSelection(); + attachedTo = docOrDefault(attachedTo); + if (!attachedTo) { + return true; + } + + Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo); + + if (dlg) { + return dlg->isAllowedAlterSelection(); } return true; } +App::Document* ControlSingleton::docOrDefault(App::Document* attachedTo) +{ + if (!attachedTo && Application::Instance->activeDocument()) { + attachedTo = Application::Instance->activeDocument()->getDocument(); + } + return attachedTo; +} + // ------------------------------------------- ControlSingleton& ControlSingleton::instance() diff --git a/src/Gui/Control.h b/src/Gui/Control.h index faf9d0457a..8941021b7e 100644 --- a/src/Gui/Control.h +++ b/src/Gui/Control.h @@ -64,8 +64,9 @@ public: */ //@{ /// This method starts a task dialog in the task view - void showDialog(Gui::TaskView::TaskDialog* dlg); - Gui::TaskView::TaskDialog* activeDialog() const; + /// The dialog is relative to a specific document + void showDialog(Gui::TaskView::TaskDialog* dlg, App::Document* attachTo = nullptr); + Gui::TaskView::TaskDialog* activeDialog(App::Document* attachedTo = nullptr) const; // void closeDialog(); //@} @@ -81,28 +82,29 @@ public: If a task dialog is open then it indicates whether this task dialog allows other commands to modify the document while it is open. If no task dialog is open true is returned. */ - bool isAllowedAlterDocument() const; + bool isAllowedAlterDocument(App::Document* attachedTo = nullptr) const; /*! If a task dialog is open then it indicates whether this task dialog allows other commands to modify the 3d view while it is open. If no task dialog is open true is returned. */ - bool isAllowedAlterView() const; + bool isAllowedAlterView(App::Document* attachedTo = nullptr) const; /*! If a task dialog is open then it indicates whether this task dialog allows other commands to modify the selection while it is open. If no task dialog is open true is returned. */ - bool isAllowedAlterSelection() const; + bool isAllowedAlterSelection(App::Document* attachedTo = nullptr) const; public Q_SLOTS: - void accept(); - void reject(); - void closeDialog(); + void accept(App::Document* attachedTo = nullptr); + void reject(App::Document* attachedTo = nullptr); + void closeDialog(App::Document* attachedTo = nullptr); + /// raises the task view panel void showTaskView(); -private Q_SLOTS: +private: /// This get called by the TaskView when the Dialog is finished - void closedDialog(); + void closedDialog(App::Document* attachedTo = nullptr); private: struct status @@ -112,7 +114,7 @@ private: std::stack StatusStack; - Gui::TaskView::TaskDialog* ActiveDialog; + std::map ActiveDialogs; int oldTabIndex; private: @@ -125,6 +127,9 @@ private: void aboutToShowDialog(QDockWidget* widget); void aboutToHideDialog(QDockWidget* widget); + // Returns attachTo if not nullptr, otherwise return the active document + static App::Document* docOrDefault(App::Document* attachedTo); + static ControlSingleton* _pcSingleton; }; diff --git a/src/Gui/Dialogs/DlgAddProperty.cpp b/src/Gui/Dialogs/DlgAddProperty.cpp index d0e561e6db..1e4f412a46 100644 --- a/src/Gui/Dialogs/DlgAddProperty.cpp +++ b/src/Gui/Dialogs/DlgAddProperty.cpp @@ -920,7 +920,9 @@ void DlgAddProperty::valueChanged() */ void DlgAddProperty::openTransaction() { - transactionID = App::GetApplication().setActiveTransaction("Add property"); + transactionID = App::GetApplication().setActiveTransaction( + App::TransactionName {.name = "Add property", .temporary = false} + ); } void DlgAddProperty::critical(const QString& title, const QString& text) @@ -993,7 +995,12 @@ void DlgAddProperty::closeTransaction(TransactionOption option) return; } - App::GetApplication().closeActiveTransaction(static_cast(option), transactionID); + if (option == TransactionOption::Abort) { + App::GetApplication().abortTransaction(transactionID); + } + else { + App::GetApplication().commitTransaction(transactionID); + } transactionID = 0; } diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 29b0a0771c..ae373f9003 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -90,6 +90,7 @@ struct DocumentP bool _isClosing; bool _isModified; bool _isTransacting; + bool _isActive; bool _changeViewTouchDocument; bool _editWantsRestore; bool _editWantsRestorePrevious; @@ -101,6 +102,7 @@ struct DocumentP ViewProviderDocumentObject* _editViewProviderParent; std::string _editSubname; std::string _editSubElement; + std::string _workbenchName; // Name of the workbench acting on this document Base::Matrix4D _editingTransform; View3DInventorViewer* _editingViewer; std::set _editObjs; @@ -284,6 +286,7 @@ struct DocumentP { _editingObject = sobj; _editMode = ModNum; + _editViewProvider = svp; // Helps the viewprovider find the correct in editDocument _editViewProvider = svp->startEditing(ModNum); if (!_editViewProvider) { _editViewProviderParent = nullptr; @@ -313,7 +316,7 @@ struct DocumentP void setDocumentNameOfTaskDialog(App::Document* doc) { - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(_pcDocument); if (dlg) { dlg->setDocumentName(doc->getName()); } @@ -432,6 +435,7 @@ Document::Document(App::Document* pcDocument, Application* app) d->_isClosing = false; d->_isModified = false; d->_isTransacting = false; + d->_isActive = false; d->_pcAppWnd = app; d->_pcDocument = pcDocument; d->_editViewProvider = nullptr; @@ -690,7 +694,6 @@ bool Document::trySetEdit(Gui::ViewProvider* p, int ModNum, const char* subname) d->setEditingViewerIfPossible(view3d, ModNum); d->signalEditMode(); - App::AutoTransaction::setEnable(false); return true; } @@ -717,7 +720,7 @@ void Document::resetEdit() Gui::ViewProvider* vpToRestore = d->_editViewProviderPrevious; bool shouldRestorePrevious = d->_editWantsRestorePrevious; - Application::Instance->setEditDocument(nullptr); + Application::Instance->unsetEditDocument(this); if (vpIsNotNull && vpHasChanged && shouldRestorePrevious) { setEdit(vpToRestore, modeToRestore); @@ -755,18 +758,16 @@ void Document::_resetEdit() // The logic below is not necessary anymore, because this method is // changed into a private one, _resetEdit(). And the exposed - // resetEdit() above calls into Application->setEditDocument(0) which + // resetEdit() above calls into Application->unsetEditDocument() which // will prevent recursive calling. - App::GetApplication().closeActiveTransaction(); + App::GetApplication().commitTransaction(getDocument()->getBookedTransactionID()); } d->_editViewProviderParent = nullptr; d->_editingViewer = nullptr; d->_editObjs.clear(); d->_editingObject = nullptr; - if (Application::Instance->editDocument() == this) { - Application::Instance->setEditDocument(nullptr); - } + Application::Instance->unsetEditDocument(this); } ViewProvider* Document::getInEdit( @@ -799,6 +800,10 @@ ViewProvider* Document::getInEdit( return nullptr; } +ViewProvider* Document::getEditViewProvider() const +{ + return d->_editViewProvider; +} void Document::setInEdit(ViewProviderDocumentObject* parentVp, const char* subname) { @@ -1065,6 +1070,16 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj) return; } + if (d->_editViewProvider == viewProvider || d->_editViewProviderParent == viewProvider) { + _resetEdit(); + } + else { + Application::Instance->unsetEditDocumentIf([&viewProvider](Gui::Document* editdoc) { + return editdoc->d->_editViewProvider == viewProvider + || editdoc->d->_editViewProviderParent == viewProvider; + }); + } + if (d->_editViewProvider == viewProvider || d->_editViewProviderParent == viewProvider) { _resetEdit(); } @@ -1096,15 +1111,13 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj) void Document::beforeDelete() { - auto editDoc = Application::Instance->editDocument(); - if (editDoc) { + Application::Instance->unsetEditDocumentIf([this](Gui::Document* editDoc) { auto vp = freecad_cast(editDoc->d->_editViewProvider); auto vpp = freecad_cast(editDoc->d->_editViewProviderParent); - if (editDoc == this || (vp && vp->getDocument() == this) - || (vpp && vpp->getDocument() == this)) { - Application::Instance->setEditDocument(nullptr); - } - } + + return editDoc == this || (vp && vp->getDocument() == this) + || (vpp && vpp->getDocument() == this); + }); for (auto& v : d->_ViewProviderMap) { v.second->beforeDelete(); } @@ -1251,6 +1264,18 @@ void Document::slotSkipRecompute(const App::Document& doc, const std::vectorisInEdit(this)) { + auto vp = freecad_cast(getInEdit()); + if (vp) { + obj = vp->getObject(); + } + } + if (objs.size() > 1 || App::GetApplication().getActiveDocument() != &doc + || !doc.testStatus(App::Document::AllowPartialRecompute)) { + return; + } + auto editDoc = Application::Instance->editDocument(); if (editDoc) { auto vp = freecad_cast(editDoc->getInEdit()); @@ -1329,6 +1354,14 @@ bool Document::isModified() const { return d->_isModified; } +void Document::setWorkbench(const std::string& name) +{ + d->_workbenchName = name; +} +std::string Document::workbench() const +{ + return d->_workbenchName; +} bool Document::isAboutToClose() const { @@ -1399,6 +1432,17 @@ App::Document* Document::getDocument() const { return d->_pcDocument; } +void Document::setIsActive(bool active) +{ + d->_isActive = active; + if (d->_editViewProvider) { + d->_editViewProvider->setActive(active); + } +} +bool Document::isActive() const +{ + return d->_isActive; +} static bool checkCanonicalPath(const std::map& docs) { @@ -1568,7 +1612,7 @@ bool Document::save() for (auto doc : docs) { // Changed 'mustExecute' status may be triggered by saving external document if (!dmap[doc] && doc->mustExecute()) { - App::AutoTransaction trans("Recompute"); + App::AutoTransaction trans(doc, "Recompute"); Command::doCommand( Command::Doc, "App.getDocument(\"%s\").recompute()", @@ -1711,7 +1755,7 @@ void Document::saveAll() try { // Changed 'mustExecute' status may be triggered by saving external document if (!dmap[doc] && doc->mustExecute()) { - App::AutoTransaction trans("Recompute"); + App::AutoTransaction trans(doc, "Recompute"); Command::doCommand(Command::Doc, "App.getDocument('%s').recompute()", doc->getName()); } Command::doCommand(Command::Doc, "App.getDocument('%s').save()", doc->getName()); @@ -2476,8 +2520,8 @@ bool Document::canClose(bool checkModify, bool checkLink) // If a task dialog is open that doesn't allow other commands to modify // the document it must be closed by resetting the edit mode of the // corresponding view provider. - if (!Gui::Control().isAllowedAlterDocument()) { - std::string name = Gui::Control().activeDialog()->getDocumentName(); + if (!Gui::Control().isAllowedAlterDocument(getDocument())) { + std::string name = Gui::Control().activeDialog(getDocument())->getDocumentName(); if (name == this->getDocument()->getName()) { // getInEdit() only checks if the currently active MDI view is // a 3D view and that it is in edit mode. However, when closing a @@ -2733,9 +2777,9 @@ Gui::MDIView* Document::getEditingViewOfViewProvider(Gui::ViewProvider* vp) cons * operation default is the command name. * @see CommitCommand(),AbortCommand() */ -void Document::openCommand(const char* sName) +int Document::openCommand(const char* sName) { - getDocument()->openTransaction(sName); + return getDocument()->openTransaction(App::TransactionName {.name = sName, .temporary = false}); } void Document::commitCommand() diff --git a/src/Gui/Document.h b/src/Gui/Document.h index fee6623073..4da7b5727a 100644 --- a/src/Gui/Document.h +++ b/src/Gui/Document.h @@ -191,12 +191,21 @@ public: void setModified(bool); bool isModified() const; + /// getter-setter for workbench name + void setWorkbench(const std::string& name); + std::string workbench() const; + /// Returns true if the document is about to be closed, false otherwise bool isAboutToClose() const; /// Getter for the App Document App::Document* getDocument() const; + /// Notify the document when it becomes + /// the active document/stops being the active document + void setIsActive(bool active); + bool isActive() const; + /** @name methods for View handling */ //@{ /// Getter for the active view @@ -287,6 +296,8 @@ public: int* mode = nullptr, std::string* subElement = nullptr ) const; + ViewProvider* getEditViewProvider() const; // Returns the _editViewProvider even if it is not + // in edit at the moment /// set the in edit ViewProvider subname reference void setInEdit(ViewProviderDocumentObject* parentVp, const char* subname); /** Add or remove view provider from scene graphs of all views @@ -300,7 +311,7 @@ public: /** @name methods for the UNDO REDO handling */ //@{ /// Open a new Undo transaction on the document - void openCommand(const char* sName = nullptr); + int openCommand(const char* sName = nullptr); /// Commit the Undo transaction on the document void commitCommand(); /// Abort the Undo transaction on the document diff --git a/src/Gui/Document.pyi b/src/Gui/Document.pyi index 31493815dd..a2183f70ca 100644 --- a/src/Gui/Document.pyi +++ b/src/Gui/Document.pyi @@ -187,6 +187,31 @@ class Document(Persistence): obj : Gui.ViewProvider """ ... + + def openCommand(self, name: str) -> int: + """ + openCommand(name) -> int + + Opens a named transaction for the document and returns it's + id or 0 on failure + """ + ... + + def commitCommand(self) -> None: + """ + commitCommand() -> None + + Commits the current transaction of the document + """ + ... + + def abortCommand(self) -> None: + """ + abortCommand() -> None + + Aborts the current transaction of the document + """ + ... ActiveObject: Any = ... """The active object of the document.""" diff --git a/src/Gui/DocumentPyImp.cpp b/src/Gui/DocumentPyImp.cpp index 363f04693f..ea70b03a86 100644 --- a/src/Gui/DocumentPyImp.cpp +++ b/src/Gui/DocumentPyImp.cpp @@ -577,6 +577,34 @@ Py::Long DocumentPy::getEditMode() const return Py::Long(mode); } +PyObject* DocumentPy::openCommand(PyObject* arg, PyObject* kwd) +{ + const char* name = nullptr; + if (!PyArg_ParseTuple(arg, "s", &name)) { + throw Py::Exception(); + } + int tid = getDocumentPtr()->openCommand(name); + + return Py::new_reference_to(Py::Long(tid)); +} +PyObject* DocumentPy::commitCommand(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) { + return nullptr; + } + getDocumentPtr()->commitCommand(); + + Py_Return; +} +PyObject* DocumentPy::abortCommand(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) { + return nullptr; + } + getDocumentPtr()->abortCommand(); + + Py_Return; +} Py::Boolean DocumentPy::getTransacting() const { return {getDocumentPtr()->isPerformingTransaction()}; diff --git a/src/Gui/ExpressionBinding.cpp b/src/Gui/ExpressionBinding.cpp index 5e04b45ef5..4fa30d5123 100644 --- a/src/Gui/ExpressionBinding.cpp +++ b/src/Gui/ExpressionBinding.cpp @@ -75,11 +75,11 @@ void Gui::ExpressionBinding::setExpression(std::shared_ptr expr) lastExpression = getExpression(); - bool transaction = !App::GetApplication().getActiveTransaction(); + bool transaction = docObj->getDocument()->getBookedTransactionID() == 0; if (transaction) { std::ostringstream ss; ss << (expr ? "Set" : "Discard") << " expression " << docObj->Label.getValue(); - App::GetApplication().setActiveTransaction(ss.str().c_str()); + docObj->getDocument()->openTransaction(ss.str().c_str()); } docObj->ExpressionEngine.setValue(path, expr); @@ -89,7 +89,7 @@ void Gui::ExpressionBinding::setExpression(std::shared_ptr expr) } if (transaction) { - App::GetApplication().closeActiveTransaction(); + docObj->getDocument()->commitTransaction(); } } @@ -214,11 +214,11 @@ bool ExpressionBinding::apply(const std::string& propName) throw Base::RuntimeError("Document object not found."); } - bool transaction = !App::GetApplication().getActiveTransaction(); + bool transaction = docObj->getDocument()->getBookedTransactionID() == 0; if (transaction) { std::ostringstream ss; ss << "Set expression " << docObj->Label.getValue(); - App::GetApplication().setActiveTransaction(ss.str().c_str()); + docObj->getDocument()->openTransaction(ss.str().c_str()); } Gui::Command::doCommand( Gui::Command::Doc, @@ -229,7 +229,7 @@ bool ExpressionBinding::apply(const std::string& propName) getEscapedExpressionString().c_str() ); if (transaction) { - App::GetApplication().closeActiveTransaction(); + docObj->getDocument()->commitTransaction(); } return true; } @@ -242,11 +242,11 @@ bool ExpressionBinding::apply(const std::string& propName) } if (lastExpression) { - bool transaction = !App::GetApplication().getActiveTransaction(); + bool transaction = docObj->getDocument()->getBookedTransactionID() == 0; if (transaction) { std::ostringstream ss; ss << "Discard expression " << docObj->Label.getValue(); - App::GetApplication().setActiveTransaction(ss.str().c_str()); + docObj->getDocument()->openTransaction(ss.str().c_str()); } Gui::Command::doCommand( Gui::Command::Doc, @@ -256,7 +256,7 @@ bool ExpressionBinding::apply(const std::string& propName) path.toEscapedString().c_str() ); if (transaction) { - App::GetApplication().closeActiveTransaction(); + docObj->getDocument()->commitTransaction(); } } } diff --git a/src/Gui/OverlayWidgets.cpp b/src/Gui/OverlayWidgets.cpp index 8edd50c1ad..7e9225b2da 100644 --- a/src/Gui/OverlayWidgets.cpp +++ b/src/Gui/OverlayWidgets.cpp @@ -1244,16 +1244,16 @@ bool OverlayTabWidget::checkAutoHide() const } } + bool activeDocInEdit = Application::Instance->isInEdit(Application::Instance->activeDocument()); if (autoMode == AutoMode::EditShow) { - return !Application::Instance->editDocument() - && (!Control().taskPanel() || Control().taskPanel()->isEmpty(false)); + return !activeDocInEdit && (!Control().taskPanel() || Control().taskPanel()->isEmpty(false)); } if (autoMode == AutoMode::TaskShow) { return (!Control().taskPanel() || Control().taskPanel()->isEmpty()); } - if (autoMode == AutoMode::EditHide && Application::Instance->editDocument()) { + if (autoMode == AutoMode::EditHide && activeDocInEdit) { return true; } diff --git a/src/Gui/QSint/actionpanel/actionpanel.cpp b/src/Gui/QSint/actionpanel/actionpanel.cpp index f623dd80e1..df51b0884d 100644 --- a/src/Gui/QSint/actionpanel/actionpanel.cpp +++ b/src/Gui/QSint/actionpanel/actionpanel.cpp @@ -93,10 +93,5 @@ ActionGroup *ActionPanel::createGroup(const QPixmap &icon, const QString &title, return group; } -QSize ActionPanel::minimumSizeHint() const -{ - return {200,150}; -} - } // namespace QSint diff --git a/src/Gui/QSint/actionpanel/actionpanel.h b/src/Gui/QSint/actionpanel/actionpanel.h index 7b4f42a3f9..cb9519d693 100644 --- a/src/Gui/QSint/actionpanel/actionpanel.h +++ b/src/Gui/QSint/actionpanel/actionpanel.h @@ -89,12 +89,6 @@ public: */ void setScheme(ActionPanelScheme *scheme); - /** - * @brief Returns the recommended minimum size for the panel. - * @return The minimum size hint. - */ - QSize minimumSizeHint() const override; - protected: /** @brief The color scheme used by the panel. */ ActionPanelScheme *myScheme; diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index 21f22a91e8..4244a21f13 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -225,7 +225,7 @@ class GuiExport SelectionObserver public: /** Constructor * - * @param attach: whether to attach this observer on construction + * @param attach: whether to attach this observer on construction * @param resolve: sub-object resolving mode. */ explicit SelectionObserver(bool attach = true, ResolveMode resolve = ResolveMode::OldStyleElement); diff --git a/src/Gui/TaskElementColors.cpp b/src/Gui/TaskElementColors.cpp index b824eb42f8..ddee1dc2ff 100644 --- a/src/Gui/TaskElementColors.cpp +++ b/src/Gui/TaskElementColors.cpp @@ -25,8 +25,8 @@ #include #include - #include +#include #include #include "TaskElementColors.h" @@ -64,6 +64,7 @@ public: bool busy; long onTopMode; bool touched; + int tid {0}; // Transaction id std::string editDoc; std::string editObj; @@ -77,15 +78,14 @@ public: , vpDoc(vp->getDocument()) , editElement(element) { - auto doc = Application::Instance->editDocument(); - if (doc) { - auto editVp = doc->getInEdit(&vpParent, &editSub); - if (editVp == vp) { - auto obj = vpParent->getObject(); - editDoc = obj->getDocument()->getName(); - editObj = obj->getNameInDocument(); - editSub = Data::noElementName(editSub.c_str()); - } + if (auto editDoc = Application::Instance->editDocument([this, &vp](Gui::Document* editDoc) { + return editDoc->getInEdit(&vpParent, &editSub) == vp; + })) { + + auto obj = vpParent->getObject(); + this->editDoc = obj->getDocument()->getName(); + this->editObj = obj->getNameInDocument(); + this->editSub = Data::noElementName(editSub.c_str()); } if (editDoc.empty()) { vpParent = vp; @@ -198,8 +198,8 @@ public: std::string sub = qPrintable(item->data(Qt::UserRole + 1).value()); info.emplace(sub, Base::Color::fromValue(col)); } - if (!App::GetApplication().getActiveTransaction()) { - App::GetApplication().setActiveTransaction("Set colors"); + if (tid == App::NullTransaction) { + tid = vpDoc->openCommand(QT_TRANSLATE_NOOP("Command", "Set colors")); } vp->setElementColors(info); touched = true; @@ -209,8 +209,12 @@ public: void reset() { touched = false; - App::GetApplication().closeActiveTransaction(true); + App::GetApplication().abortTransaction(tid); Selection().clearSelection(); + + Application::Instance->unsetEditDocumentIf([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == vp; + }); } void accept() @@ -221,7 +225,11 @@ public: obj->getDocument()->recompute(obj->getInListRecursive()); touched = false; } - App::GetApplication().closeActiveTransaction(); + + App::GetApplication().commitTransaction(tid); + Application::Instance->unsetEditDocumentIf([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == vp; + }); } void removeAll() @@ -435,14 +443,14 @@ void ElementColors::onTopClicked(bool checked) void ElementColors::slotDeleteDocument(const Document& Doc) { if (d->vpDoc == &Doc || d->editDoc == Doc.getDocument()->getName()) { - Control().closeDialog(); + Control().closeDialog(Doc.getDocument()); } } void ElementColors::slotDeleteObject(const ViewProvider& obj) { if (d->vp == &obj) { - Control().closeDialog(); + Control().closeDialog(d->vpDoc->getDocument()); } } @@ -543,14 +551,12 @@ void ElementColors::onRemoveAllClicked() bool ElementColors::accept() { d->accept(); - Application::Instance->setEditDocument(nullptr); return true; } bool ElementColors::reject() { d->reset(); - Application::Instance->setEditDocument(nullptr); return true; } diff --git a/src/Gui/TaskView/TaskDialog.cpp b/src/Gui/TaskView/TaskDialog.cpp index f6d613c09b..0f69bac4e7 100644 --- a/src/Gui/TaskView/TaskDialog.cpp +++ b/src/Gui/TaskView/TaskDialog.cpp @@ -158,5 +158,11 @@ void TaskDialog::onUndo() void TaskDialog::onRedo() {} +void TaskDialog::activate() +{} + +void TaskDialog::deactivate() +{} + #include "moc_TaskDialog.cpp" diff --git a/src/Gui/TaskView/TaskDialog.h b/src/Gui/TaskView/TaskDialog.h index f6fd9970a2..919786bd16 100644 --- a/src/Gui/TaskView/TaskDialog.h +++ b/src/Gui/TaskView/TaskDialog.h @@ -209,6 +209,13 @@ public: /// is called by the framework if the user press the redo button virtual void onRedo(); + /// Called by the framework when it becomes the shown dialog + /// of the stacked task panel (e.g. when it's document becomes active) + virtual void activate(); + /// Called by the framework when it stops being the shown dialog + /// of the stacked task panel (e.g. when it's document stops being active) + virtual void deactivate(); + void emitDestructionSignal() { Q_EMIT aboutToBeDestroyed(); diff --git a/src/Gui/TaskView/TaskDialogPython.cpp b/src/Gui/TaskView/TaskDialogPython.cpp index e1d11927e7..b2526c64e9 100644 --- a/src/Gui/TaskView/TaskDialogPython.cpp +++ b/src/Gui/TaskView/TaskDialogPython.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "TaskDialogPython.h" #include "TaskView.h" @@ -142,43 +143,67 @@ Py::Object ControlPy::repr() Py::Object ControlPy::showDialog(const Py::Tuple& args) { - PyObject* arg0; - if (!PyArg_ParseTuple(args.ptr(), "O", &arg0)) { + PyObject* arg0 = nullptr; + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "O|O!", &arg0, &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - Gui::TaskView::TaskDialog* act = Gui::Control().activeDialog(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + Gui::TaskView::TaskDialog* act = Gui::Control().activeDialog(doc); if (act) { throw Py::RuntimeError("Active task dialog found"); } auto dlg = new TaskDialogPython(Py::Object(arg0)); - Gui::Control().showDialog(dlg); + Gui::Control().showDialog(dlg, doc); return (Py::asObject(new TaskDialogPy(dlg))); } Py::Object ControlPy::activeDialog(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(doc); return Py::Boolean(dlg != nullptr); } Py::Object ControlPy::activeTaskDialog(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(doc); return (dlg ? Py::asObject(new TaskDialogPy(dlg)) : Py::None()); } Py::Object ControlPy::closeDialog(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - Gui::Control().closeDialog(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + Gui::Control().closeDialog(doc); return Py::None(); } @@ -217,28 +242,46 @@ Py::Object ControlPy::clearTaskWatcher(const Py::Tuple& args) Py::Object ControlPy::isAllowedAlterDocument(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - bool ok = Gui::Control().isAllowedAlterDocument(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + bool ok = Gui::Control().isAllowedAlterDocument(doc); return Py::Boolean(ok); } Py::Object ControlPy::isAllowedAlterView(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - bool ok = Gui::Control().isAllowedAlterView(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + bool ok = Gui::Control().isAllowedAlterView(doc); return Py::Boolean(ok); } Py::Object ControlPy::isAllowedAlterSelection(const Py::Tuple& args) { - if (!PyArg_ParseTuple(args.ptr(), "")) { + PyObject* docPy = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &(Gui::DocumentPy::Type), &docPy)) { throw Py::Exception(); } - bool ok = Gui::Control().isAllowedAlterSelection(); + + App::Document* doc = docPy + ? static_cast(docPy)->getDocumentPtr()->getDocument() + : nullptr; + + bool ok = Gui::Control().isAllowedAlterSelection(doc); return Py::Boolean(ok); } diff --git a/src/Gui/TaskView/TaskView.cpp b/src/Gui/TaskView/TaskView.cpp index 88c84f12f6..0b028b55f2 100644 --- a/src/Gui/TaskView/TaskView.cpp +++ b/src/Gui/TaskView/TaskView.cpp @@ -234,37 +234,7 @@ void TaskBox::actionEvent(QActionEvent* e) //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TaskPanel::TaskPanel(QWidget* parent) - : QSint::ActionPanel(parent) -{} - -TaskPanel::~TaskPanel() = default; - -QSize TaskPanel::minimumSizeHint() const -{ - // ActionPanel returns a size of 200x150 which leads to problems - // when there are several task groups in the panel and the first - // one is collapsed. In this case the task panel doesn't expand to - // the actually required size and all the remaining groups are - // squeezed into the available space and thus the widgets in there - // often can't be used any more. - // To fix this problem minimumSizeHint() is implemented to again - // respect the layout's minimum size. - QSize s1 = QSint::ActionPanel::minimumSizeHint(); - QSize s2 = QWidget::minimumSizeHint(); - return {qMax(s1.width(), s2.width()), qMax(s1.height(), s2.height())}; -} - - -//************************************************************************** -//************************************************************************** -// TaskView -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -TaskView::TaskView(QWidget* parent) : QWidget(parent) - , ActiveDialog(nullptr) - , ActiveCtrl(nullptr) - , hGrp(Gui::WindowParameter::getDefaultParameter()->GetGroup("General")) { mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); @@ -280,19 +250,55 @@ TaskView::TaskView(QWidget* parent) dialogLayout->setSpacing(0); mainLayout->addLayout(dialogLayout, 1); - taskPanel = new TaskPanel(scrollArea); + actionPanel = new QSint::ActionPanel(scrollArea); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(taskPanel->sizePolicy().hasHeightForWidth()); - taskPanel->setSizePolicy(sizePolicy); - taskPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + sizePolicy.setHeightForWidth(actionPanel->sizePolicy().hasHeightForWidth()); + actionPanel->setSizePolicy(sizePolicy); + actionPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); - scrollArea->setWidget(taskPanel); + scrollArea->setWidget(actionPanel); scrollArea->setWidgetResizable(true); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setMinimumWidth(200); dialogLayout->addWidget(scrollArea, 1); +} + +TaskPanel::~TaskPanel() +{ + for (QWidget* panel : contextualPanels) { + delete panel; + } +} + +QSize TaskPanel::minimumSizeHint() const +{ + // ActionPanel returns a size of 200x150 which leads to problems + // when there are several task groups in the panel and the first + // one is collapsed. In this case the task panel doesn't expand to + // the actually required size and all the remaining groups are + // squeezed into the available space and thus the widgets in there + // often can't be used any more. + // To fix this problem minimumSizeHint() is implemented to again + // respect the layout's minimum size. + QSize s1 = actionPanel->minimumSizeHint(); + QSize s2 = QWidget::minimumSizeHint(); + return {qMax(s1.width(), s2.width()), qMax(s1.height(), s2.height())}; +} + + +//************************************************************************** +//************************************************************************** +// TaskView +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskView::TaskView(QWidget* parent) + : QStackedWidget(parent) + , hGrp(Gui::WindowParameter::getDefaultParameter()->GetGroup("General")) +{ + TaskWatcherPanel = new TaskPanel(this); + addWidget(TaskWatcherPanel); Gui::Selection().Attach(this); @@ -329,7 +335,6 @@ TaskView::TaskView(QWidget* parent) updateWatcher(); } - TaskView::~TaskView() { connectApplicationActiveDocument.disconnect(); @@ -341,17 +346,22 @@ TaskView::~TaskView() connectShowTaskWatcherSetting.disconnect(); Gui::Selection().Detach(this); - for (QWidget* panel : contextualPanels) { - delete panel; + // if well behaved, we should not have nay taskInfo at this point + for (auto& taskInfo : taskInfos) { + delete taskInfo.ActiveCtrl; + delete taskInfo.ActiveDialog; + delete taskInfo.taskPanel; } } bool TaskView::isEmpty(bool includeWatcher) const { - if (ActiveCtrl || ActiveDialog) { + std::optional active = currentTaskInfo(); + if (active) { return false; } + // There is no active task in the document if (includeWatcher) { for (auto* watcher : ActiveWatcher) { if (watcher->shouldShow()) { @@ -397,7 +407,8 @@ bool TaskView::event(QEvent* event) void TaskView::keyPressEvent(QKeyEvent* ke) { - if (ActiveCtrl && ActiveDialog) { + std::optional active = currentTaskInfo(); + if (active) { if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) { // get all buttons of the complete task dialog QList list = this->findChildren(); @@ -419,9 +430,9 @@ void TaskView::keyPressEvent(QKeyEvent* ke) } } } - else if (ke->key() == Qt::Key_Escape && ActiveDialog->isEscapeButtonEnabled()) { + else if (ke->key() == Qt::Key_Escape && active->ActiveDialog->isEscapeButtonEnabled()) { // get only the buttons of the button box - QDialogButtonBox* box = ActiveCtrl->standardButtons(); + QDialogButtonBox* box = active->ActiveCtrl->standardButtons(); QList list = box->buttons(); for (auto pb : list) { if (box->buttonRole(pb) == QDialogButtonBox::RejectRole) { @@ -447,7 +458,7 @@ void TaskView::keyPressEvent(QKeyEvent* ke) auto func = new Gui::TimerFunction(); func->setAutoDelete(true); Gui::Document* doc = Gui::Application::Instance->getDocument( - ActiveDialog->getDocumentName().c_str() + active->ActiveDialog->getDocumentName().c_str() ); if (doc) { func->setFunction([doc]() { doc->resetEdit(); }); @@ -474,105 +485,96 @@ void TaskView::adjustMinimumSizeHint() QSize TaskView::minimumSizeHint() const { - QSize ms = QWidget::minimumSizeHint(); + QSize ms = currentWidget()->minimumSizeHint(); int spacing = 0; - if (QLayout* layout = taskPanel->layout()) { + + if (QLayout* layout = currentWidget()->layout()) { spacing = 2 * layout->spacing(); } - ms.setWidth(taskPanel->minimumSizeHint().width() + spacing); + ms.setWidth(ms.width() + spacing); return ms; } void TaskView::slotActiveDocument(const App::Document& doc) { - Q_UNUSED(doc); - if (!ActiveDialog) { + auto foundTaskInfo = std::ranges::find(taskInfos, &doc, &TaskInfo::Document); + if (foundTaskInfo != taskInfos.end()) { + setShownTaskInfo((foundTaskInfo - taskInfos.begin())); + } + else { + setShownTaskInfo(-1); + } + + if (foundTaskInfo == taskInfos.end()) { // at this point, active object of the active view returns None. // which is a problem if shouldShow of a watcher rely on the presence // of an active object (example Assembly). QTimer::singleShot(100, this, &TaskView::updateWatcher); } } - void TaskView::slotInEdit(const Gui::ViewProviderDocumentObject& vp) { - Q_UNUSED(vp); - if (!ActiveDialog) { + App::Document* doc = vp.getDocument()->getDocument(); + if (std::ranges::find(taskInfos, doc, &TaskInfo::Document) == taskInfos.end()) { updateWatcher(); } } void TaskView::slotDeletedDocument(const App::Document& doc) { - if (ActiveDialog) { - if (ActiveDialog->isAutoCloseOnDeletedDocument()) { - std::string name = ActiveDialog->getDocumentName(); - if (name.empty()) { - Base::Console().warning( - std::string("TaskView::slotDeletedDocument"), - "No document name set\n" - ); - } - - if (name == doc.getName()) { - ActiveDialog->autoClosedOnDeletedDocument(); - removeDialog(); - } - } + auto foundTaskInfo = std::ranges::find(taskInfos, &doc, &TaskInfo::Document); + bool hasDialog = foundTaskInfo != taskInfos.end(); + if (hasDialog && foundTaskInfo->ActiveDialog->isAutoCloseOnDeletedDocument()) { + foundTaskInfo->ActiveDialog->autoClosedOnDeletedDocument(); + removeDialog(foundTaskInfo); + hasDialog = false; } - if (!ActiveDialog) { + if (!hasDialog) { updateWatcher(); } } void TaskView::slotViewClosed(const Gui::MDIView* view) { + auto foundTaskInfo = std::ranges::find_if(taskInfos, [view](const TaskInfo& info) { + return info.ActiveDialog->getAssociatedView() == view; + }); + bool hasDialog = foundTaskInfo != taskInfos.end(); // It can happen that only a view is closed an not the document - if (ActiveDialog) { - if (ActiveDialog->isAutoCloseOnClosedView()) { - const Gui::MDIView* associatedView = ActiveDialog->getAssociatedView(); - if (!associatedView) { - Base::Console().warning(std::string("TaskView::slotViewClosed"), "No view associated\n"); - } - - if (associatedView == view) { - ActiveDialog->autoClosedOnClosedView(); - removeDialog(); - } - } + if (hasDialog && foundTaskInfo->ActiveDialog->isAutoCloseOnClosedView()) { + foundTaskInfo->ActiveDialog->autoClosedOnClosedView(); + removeDialog(foundTaskInfo); + hasDialog = false; } - if (!ActiveDialog) { + if (!hasDialog) { updateWatcher(); } } void TaskView::transactionChangeOnDocument(const App::Document& doc, bool undo) { - if (ActiveDialog) { - std::string name = ActiveDialog->getDocumentName(); - if (name == doc.getName()) { - undo ? ActiveDialog->onUndo() : ActiveDialog->onRedo(); + auto foundTaskInfo = std::ranges::find(taskInfos, &doc, &TaskInfo::Document); + bool hasDialog = foundTaskInfo != taskInfos.end(); + + if (hasDialog) { + if (undo) { + foundTaskInfo->ActiveDialog->onUndo(); + } + else { + foundTaskInfo->ActiveDialog->onRedo(); } - if (ActiveDialog->isAutoCloseOnTransactionChange()) { - if (name.empty()) { - Base::Console().warning( - std::string("TaskView::transactionChangeOnDocument"), - "No document name set\n" - ); - } - - if (name == doc.getName()) { - ActiveDialog->autoClosedOnTransactionChange(); - removeDialog(); - } + if (foundTaskInfo->ActiveDialog->isAutoCloseOnTransactionChange()) { + foundTaskInfo->ActiveDialog->autoClosedOnTransactionChange(); + removeDialog(foundTaskInfo); + hasDialog = false; } } - if (!ActiveDialog) { + if (!hasDialog) { updateWatcher(); } } @@ -600,72 +602,75 @@ void TaskView::OnChange( || Reason.Type == SelectionChanges::SetSelection || Reason.Type == SelectionChanges::RmvSelection) { - if (!ActiveDialog) { + if (!currentTaskInfo()) { updateWatcher(); } } } /// @endcond -void TaskView::showDialog(TaskDialog* dlg) +bool TaskView::showDialog(TaskDialog* dlg, App::Document* doc) { + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); // if trying to open the same dialog twice nothing needs to be done - if (ActiveDialog == dlg) { - return; + if (foundTaskInfo != taskInfos.end() && foundTaskInfo->ActiveDialog == dlg) { + return false; } - assert(!ActiveDialog); - assert(!ActiveCtrl); - - // remove the TaskWatcher as long as the Dialog is up - removeTaskWatcher(); + assert(foundTaskInfo == taskInfos.end()); + TaskInfo outInfo {.Document = doc}; // first create the control element, set it up and wire it: - ActiveCtrl = new TaskEditControl(this); - ActiveCtrl->buttonBox->setStandardButtons(dlg->getStandardButtons()); - TaskDialogAttorney::setButtonBox(dlg, ActiveCtrl->buttonBox); - - // clang-format off - // make connection to the needed signals - connect(ActiveCtrl->buttonBox, &QDialogButtonBox::accepted, - this, &TaskView::accept); - connect(ActiveCtrl->buttonBox, &QDialogButtonBox::rejected, - this, &TaskView::reject); - connect(ActiveCtrl->buttonBox, &QDialogButtonBox::helpRequested, - this, &TaskView::helpRequested); - connect(ActiveCtrl->buttonBox, &QDialogButtonBox::clicked, - this, &TaskView::clicked); - // clang-format on + outInfo.ActiveCtrl = new TaskEditControl(this); + outInfo.ActiveCtrl->buttonBox->setStandardButtons(dlg->getStandardButtons()); + TaskDialogAttorney::setButtonBox(dlg, outInfo.ActiveCtrl->buttonBox); const std::vector& cont = dlg->getDialogContent(); // give to task dialog to customize the button box - dlg->modifyStandardButtons(ActiveCtrl->buttonBox); + dlg->modifyStandardButtons(outInfo.ActiveCtrl->buttonBox); + outInfo.taskPanel = new TaskPanel(this); if (dlg->buttonPosition() == TaskDialog::North) { // Add button box to the top of the main layout - dialogLayout->insertWidget(0, ActiveCtrl); + outInfo.taskPanel->dialogLayout->insertWidget(0, outInfo.ActiveCtrl); for (const auto& it : cont) { - taskPanel->addWidget(it); + outInfo.taskPanel->actionPanel->addWidget(it); } } else { for (const auto& it : cont) { - taskPanel->addWidget(it); + outInfo.taskPanel->actionPanel->addWidget(it); } // Add button box to the bottom of the main layout - dialogLayout->addWidget(ActiveCtrl); + outInfo.taskPanel->dialogLayout->addWidget(outInfo.ActiveCtrl); } - taskPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + outInfo.taskPanel->actionPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); if (!dlg->needsFullSpace()) { - taskPanel->addStretch(); + outInfo.taskPanel->actionPanel->addStretch(); } // set as active Dialog - ActiveDialog = dlg; + outInfo.ActiveDialog = dlg; + outInfo.ActiveDialog->open(); - ActiveDialog->open(); + // clang-format off + // make connection to the needed signals + connect(outInfo.ActiveCtrl->buttonBox, &QDialogButtonBox::accepted, + this, [doc, this]{ accept(doc); }); + connect(outInfo.ActiveCtrl->buttonBox, &QDialogButtonBox::rejected, + this, [doc, this]{ reject(doc); }); + connect(outInfo.ActiveCtrl->buttonBox, &QDialogButtonBox::helpRequested, + this, [doc, this]{ helpRequested(doc); }); + connect(outInfo.ActiveCtrl->buttonBox, &QDialogButtonBox::clicked, + this, [doc, this](QAbstractButton *button) { clicked(button, doc); }); + // clang-format on + + // This will hide whatever was shown in the taskview + taskInfos.push_back(outInfo); + addWidget(outInfo.taskPanel); + setShownTaskInfo(taskInfos.size() - 1); saveCurrentWidth(); getMainWindow()->updateActions(); @@ -675,43 +680,51 @@ void TaskView::showDialog(TaskDialog* dlg) Q_EMIT taskUpdate(); OverlayManager::instance()->refresh(); + + return true; } -void TaskView::removeDialog() +void TaskView::removeDialog(App::Document* doc) { + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (foundTaskInfo != taskInfos.end()) { + removeDialog(foundTaskInfo); + } +} +void TaskView::removeDialog(std::vector::iterator infoIt) +{ + if (infoIt == taskInfos.end()) { + return; + } getMainWindow()->updateActions(); - if (ActiveCtrl) { - dialogLayout->removeWidget(ActiveCtrl); - delete ActiveCtrl; - ActiveCtrl = nullptr; - } - - TaskDialog* remove = nullptr; - if (ActiveDialog) { + std::optional remove = std::nullopt; + if (infoIt->ActiveDialog) { // See 'accept' and 'reject' - if (ActiveDialog->property("taskview_accept_or_reject").isNull()) { - const std::vector& cont = ActiveDialog->getDialogContent(); + if (infoIt->ActiveDialog->property("taskview_accept_or_reject").isNull()) { + const std::vector& cont = infoIt->ActiveDialog->getDialogContent(); for (const auto& it : cont) { - taskPanel->removeWidget(it); + infoIt->taskPanel->actionPanel->removeWidget(it); } - remove = ActiveDialog; - ActiveDialog = nullptr; + remove = *infoIt; + taskInfos.erase(infoIt); + removeWidget(remove->taskPanel); } else { - ActiveDialog->setProperty("taskview_remove_dialog", true); + infoIt->ActiveDialog->setProperty("taskview_remove_dialog", true); } } - taskPanel->removeStretch(); - // put the watcher back in control + removeTaskWatcher(); addTaskWatcher(); if (remove) { - remove->closed(); - remove->emitDestructionSignal(); - delete remove; + remove->ActiveDialog->closed(); + remove->ActiveDialog->emitDestructionSignal(); + delete remove->ActiveCtrl; + delete remove->ActiveDialog; + delete remove->taskPanel; } tryRestoreWidth(); @@ -741,7 +754,7 @@ void TaskView::updateWatcher() if (ActiveWatcher.empty()) { auto panel = Gui::Control().taskPanel(); - if (panel && panel->ActiveWatcher.size()) { + if (panel && !panel->ActiveWatcher.empty()) { takeTaskWatcher(panel); } } @@ -797,9 +810,7 @@ void TaskView::addTaskWatcher(const std::vector& Watcher) } ActiveWatcher = Watcher; - if (!ActiveCtrl && !ActiveDialog) { - addTaskWatcher(); - } + addTaskWatcher(); } void TaskView::takeTaskWatcher(TaskView* other) @@ -823,33 +834,34 @@ void TaskView::clearTaskWatcher() void TaskView::addTaskWatcher() { if (!showTaskWatcher) { + setShownTaskInfo(-1); // Switch to the empty taskwatcher panel return; } // add all widgets for all watcher to the task view for (TaskWatcher* tw : ActiveWatcher) { std::vector& cont = tw->getWatcherContent(); for (QWidget* w : cont) { - taskPanel->addWidget(w); + TaskWatcherPanel->actionPanel->addWidget(w); } } if (!ActiveWatcher.empty()) { - taskPanel->addStretch(); + TaskWatcherPanel->actionPanel->addStretch(); } - updateWatcher(); // Workaround to avoid a crash in Qt. See also // https://forum.freecad.org/viewtopic.php?f=8&t=39187 // // Notify the button box about a style change so that it can // safely delete the style animation of its push buttons. - auto box = taskPanel->findChild(); + auto box = TaskWatcherPanel->mainLayout->findChild(); if (box) { QEvent event(QEvent::StyleChange); QApplication::sendEvent(box, &event); } - taskPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + TaskWatcherPanel->actionPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + setShownTaskInfo(-1); } void TaskView::saveCurrentWidth() @@ -879,6 +891,44 @@ bool TaskView::shouldRestoreWidth() const { return restoreWidth; } +std::optional TaskView::currentTaskInfo() const +{ + // Index 0 is taskWatcher's panel + if (currentIndex() <= 0) { + return std::nullopt; + } + return taskInfos[currentIndex() - 1]; +} +TaskDialog* TaskView::dialog(App::Document* doc) +{ + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + return foundTaskInfo == taskInfos.end() ? nullptr : foundTaskInfo->ActiveDialog; +} +void TaskView::setShownTaskInfo(int index) +{ + int stackedIndex = 0; + int initIndex = currentIndex(); + if (index < 0 || index >= taskInfos.size()) { + updateWatcher(); + stackedIndex = 0; // Show task watcher + } + else { + stackedIndex = index + 1; + } + if (stackedIndex == initIndex) { + return; // Nothing to be done + } + + if (initIndex > 0) { + Gui::Selection().rmvSelectionGate(); + taskInfos[initIndex - 1].ActiveDialog->deactivate(); + } + + if (stackedIndex > 0) { + taskInfos[stackedIndex - 1].ActiveDialog->activate(); + } + setCurrentIndex(stackedIndex); +} void TaskView::removeTaskWatcher() { @@ -904,92 +954,109 @@ void TaskView::removeTaskWatcher() std::vector& cont = tw->getWatcherContent(); for (QWidget* w : cont) { w->hide(); - taskPanel->removeWidget(w); + TaskWatcherPanel->actionPanel->removeWidget(w); } } - taskPanel->removeStretch(); + TaskWatcherPanel->actionPanel->removeStretch(); } -void TaskView::accept() +void TaskView::accept(App::Document* doc) { - if (!ActiveDialog) { // Protect against segfaults due to out-of-order deletions + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (foundTaskInfo == taskInfos.end()) { // Protect against segfaults due to out-of-order deletions Base::Console().warning("ActiveDialog was null in call to TaskView::accept()\n"); return; } // Make sure that if 'accept' calls 'closeDialog' the deletion is postponed until // the dialog leaves the 'accept' method - ActiveDialog->setProperty("taskview_accept_or_reject", true); - bool success = ActiveDialog->accept(); - ActiveDialog->setProperty("taskview_accept_or_reject", QVariant()); - if (success || ActiveDialog->property("taskview_remove_dialog").isValid()) { - removeDialog(); + foundTaskInfo->ActiveDialog->setProperty("taskview_accept_or_reject", true); + bool success = foundTaskInfo->ActiveDialog->accept(); + foundTaskInfo->ActiveDialog->setProperty("taskview_accept_or_reject", QVariant()); + if (success || foundTaskInfo->ActiveDialog->property("taskview_remove_dialog").isValid()) { + removeDialog(doc); } } -void TaskView::reject() +void TaskView::reject(App::Document* doc) { - if (!ActiveDialog) { // Protect against segfaults due to out-of-order deletions + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (foundTaskInfo == taskInfos.end()) { // Protect against segfaults due to out-of-order deletions Base::Console().warning("ActiveDialog was null in call to TaskView::reject()\n"); return; } // Make sure that if 'reject' calls 'closeDialog' the deletion is postponed until // the dialog leaves the 'reject' method - ActiveDialog->setProperty("taskview_accept_or_reject", true); - bool success = ActiveDialog->reject(); - ActiveDialog->setProperty("taskview_accept_or_reject", QVariant()); - if (success || ActiveDialog->property("taskview_remove_dialog").isValid()) { - removeDialog(); + foundTaskInfo->ActiveDialog->setProperty("taskview_accept_or_reject", true); + bool success = foundTaskInfo->ActiveDialog->reject(); + foundTaskInfo->ActiveDialog->setProperty("taskview_accept_or_reject", QVariant()); + if (success || foundTaskInfo->ActiveDialog->property("taskview_remove_dialog").isValid()) { + removeDialog(doc); } } -void TaskView::helpRequested() +void TaskView::helpRequested(App::Document* doc) { - ActiveDialog->helpRequested(); + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (foundTaskInfo != taskInfos.end()) { + foundTaskInfo->ActiveDialog->helpRequested(); + } } -void TaskView::clicked(QAbstractButton* button) +void TaskView::clicked(QAbstractButton* button, App::Document* doc) { - int id = ActiveCtrl->buttonBox->standardButton(button); - ActiveDialog->clicked(id); + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (foundTaskInfo != taskInfos.end()) { + int id = foundTaskInfo->ActiveCtrl->buttonBox->standardButton(button); + foundTaskInfo->ActiveDialog->clicked(id); + } } void TaskView::clearActionStyle() { + std::optional current = currentTaskInfo(); + TaskPanel* panel = current ? current->taskPanel : TaskWatcherPanel; static_cast(QSint::ActionPanelScheme::defaultScheme())->clearActionStyle(); - taskPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + panel->actionPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); } void TaskView::restoreActionStyle() { + std::optional current = currentTaskInfo(); + TaskPanel* panel = current ? current->taskPanel : TaskWatcherPanel; static_cast(QSint::ActionPanelScheme::defaultScheme()) ->restoreActionStyle(); - taskPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); + panel->actionPanel->setScheme(QSint::ActionPanelScheme::defaultScheme()); } -void TaskView::addContextualPanel(QWidget* panel) +void TaskView::addContextualPanel(QWidget* panel, App::Document* doc) { - if (!panel || contextualPanels.contains(panel)) { + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (!panel || foundTaskInfo == taskInfos.end() + || foundTaskInfo->taskPanel->contextualPanels.contains(panel)) { return; } - contextualPanelsLayout->addWidget(panel); - contextualPanels.append(panel); + foundTaskInfo->taskPanel->contextualPanelsLayout->addWidget(panel); + foundTaskInfo->taskPanel->contextualPanels.append(panel); panel->show(); triggerMinimumSizeHint(); Q_EMIT taskUpdate(); } -void TaskView::removeContextualPanel(QWidget* panel) +void TaskView::removeContextualPanel(QWidget* panel, App::Document* doc) { - if (!panel || !contextualPanels.contains(panel)) { + auto foundTaskInfo = std::ranges::find(taskInfos, doc, &TaskInfo::Document); + if (!panel || foundTaskInfo == taskInfos.end() + || !foundTaskInfo->taskPanel->contextualPanels.contains(panel)) { return; } - contextualPanelsLayout->removeWidget(panel); - contextualPanels.removeOne(panel); + + foundTaskInfo->taskPanel->contextualPanelsLayout->removeWidget(panel); + foundTaskInfo->taskPanel->contextualPanels.removeOne(panel); panel->deleteLater(); triggerMinimumSizeHint(); Q_EMIT taskUpdate(); diff --git a/src/Gui/TaskView/TaskView.h b/src/Gui/TaskView/TaskView.h index f20e710fde..171662fab2 100644 --- a/src/Gui/TaskView/TaskView.h +++ b/src/Gui/TaskView/TaskView.h @@ -25,7 +25,9 @@ #pragma once #include +#include #include +#include #include #include @@ -118,7 +120,7 @@ private: bool wasShown; }; -class GuiExport TaskPanel: public QSint::ActionPanel +class GuiExport TaskPanel: public QWidget { Q_OBJECT @@ -126,6 +128,14 @@ public: explicit TaskPanel(QWidget* parent = nullptr); ~TaskPanel() override; QSize minimumSizeHint() const override; + +public: + QVBoxLayout* mainLayout; + QScrollArea* scrollArea; + QVBoxLayout* contextualPanelsLayout; + QVBoxLayout* dialogLayout; + QList contextualPanels; + QSint::ActionPanel* actionPanel; }; /// Father class of content of a Free widget (without header and Icon), shut be an exception! @@ -138,12 +148,20 @@ public: ~TaskWidget() override; }; +struct TaskInfo +{ + TaskPanel* taskPanel {nullptr}; + TaskDialog* ActiveDialog {nullptr}; + TaskEditControl* ActiveCtrl {nullptr}; + App::Document* Document {nullptr}; +}; + /** TaskView class * handles the FreeCAD task view panel. Keeps track of the inserted content elements. * This elements get injected mostly by the ViewProvider classes of the selected * DocumentObjects. */ -class GuiExport TaskView: public QWidget, public Gui::SelectionSingleton::ObserverType +class GuiExport TaskView: public QStackedWidget, public Gui::SelectionSingleton::ObserverType { Q_OBJECT @@ -160,6 +178,7 @@ public: friend class Gui::DockWnd::ComboView; friend class Gui::ControlSingleton; + /// sets the task watcher and shows it void addTaskWatcher(const std::vector& Watcher); void clearTaskWatcher(); void takeTaskWatcher(TaskView* other); @@ -170,8 +189,8 @@ public: void restoreActionStyle(); /// Add a persistent panel at the top of the task view, independent of the active dialog. - void addContextualPanel(QWidget* panel); - void removeContextualPanel(QWidget* panel); + void addContextualPanel(QWidget* panel, App::Document* doc); + void removeContextualPanel(QWidget* panel, App::Document* doc); QSize minimumSizeHint() const override; @@ -179,14 +198,22 @@ public: void setRestoreWidth(bool on); bool shouldRestoreWidth() const; + std::optional currentTaskInfo() const; + + TaskDialog* dialog(App::Document* doc); + + // Show the task info at the index + // or taskwatcher if index = -1 + void setShownTaskInfo(int index); + Q_SIGNALS: void taskUpdate(); -protected Q_SLOTS: - void accept(); - void reject(); - void helpRequested(); - void clicked(QAbstractButton* button); +protected: + void accept(App::Document* doc); + void reject(App::Document* doc); + void helpRequested(App::Document* doc); + void clicked(QAbstractButton* button, App::Document* doc); private: void triggerMinimumSizeHint(); @@ -200,11 +227,6 @@ private: void slotUndoDocument(const App::Document&); void slotRedoDocument(const App::Document&); void transactionChangeOnDocument(const App::Document&, bool undo); - QVBoxLayout* mainLayout; - QScrollArea* scrollArea; - QVBoxLayout* contextualPanelsLayout; - QVBoxLayout* dialogLayout; - QList contextualPanels; protected: void keyPressEvent(QKeyEvent* event) override; @@ -214,18 +236,19 @@ protected: void removeTaskWatcher(); /// update the visibility of the TaskWatcher accordant to the selection void updateWatcher(); - /// used by Gui::Control to register Dialogs - void showDialog(TaskDialog* dlg); + /// used by Gui::Control to register Dialogs, returns true if the dialog was not already there + bool showDialog(TaskDialog* dlg, App::Document* doc); // removes the running dialog after accept() or reject() from the TaskView - void removeDialog(); + void removeDialog(App::Document* doc); + void removeDialog(std::vector::iterator infoIt); void setShowTaskWatcher(bool show); std::vector ActiveWatcher; + TaskPanel* TaskWatcherPanel; - QSint::ActionPanel* taskPanel; - TaskDialog* ActiveDialog; - TaskEditControl* ActiveCtrl; + // First index of the stack is reserved to the active watcher + std::vector taskInfos; bool restoreWidth = false; int currentWidth = 0; ParameterGrp::handle hGrp; diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index bdf0484671..6f2e4bc653 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -1352,10 +1353,10 @@ void TreeWidget::showEvent(QShowEvent* ev) void TreeWidget::onCreateGroup() { QString name = tr("Group"); - App::AutoTransaction trans("Create group"); if (this->contextItem->type() == DocumentType) { auto docitem = static_cast(this->contextItem); App::Document* doc = docitem->document()->getDocument(); + App::AutoTransaction trans(doc, "Create group"); QString cmd = QStringLiteral( "App.getDocument(\"%1\").addObject" "(\"App::DocumentObjectGroup\",\"Group\").Label=\"%2\"" @@ -1367,6 +1368,7 @@ void TreeWidget::onCreateGroup() auto objitem = static_cast(this->contextItem); App::DocumentObject* obj = objitem->object()->getObject(); App::Document* doc = obj->getDocument(); + App::AutoTransaction trans(doc, "Create group"); QString cmd = QStringLiteral( "App.getDocument(\"%1\").getObject(\"%2\")" ".newObject(\"App::DocumentObjectGroup\",\"Group\").Label=\"%3\"" @@ -1558,7 +1560,8 @@ void TreeWidget::onRecomputeObject() if (objs.empty()) { return; } - App::AutoTransaction committer("Recompute object"); + + App::AutoTransaction committer(objs.front()->getDocument()->openTransaction("Recompute object")); objs.front()->getDocument()->recompute(objs, true); } @@ -2027,7 +2030,10 @@ void TreeWidget::mouseDoubleClickEvent(QMouseEvent* event) auto objitem = static_cast(item); ViewProviderDocumentObject* vp = objitem->object(); - objitem->getOwnerDocument()->document()->setActiveView(vp); + Gui::Document* guidoc = objitem->getOwnerDocument()->document(); + App::Document* appdoc = guidoc->getDocument(); + + guidoc->setActiveView(vp); auto manager = Application::Instance->macroManager(); auto lines = manager->getLines(); @@ -2036,8 +2042,7 @@ void TreeWidget::mouseDoubleClickEvent(QMouseEvent* event) const char* commandText = vp->getTransactionText(); if (commandText) { - auto editDoc = Application::Instance->editDocument(); - App::AutoTransaction committer(commandText, true); + appdoc->openTransaction(commandText); if (!vp->doubleClicked()) { QTreeWidget::mouseDoubleClickEvent(event); @@ -2045,11 +2050,6 @@ void TreeWidget::mouseDoubleClickEvent(QMouseEvent* event) else if (lines == manager->getLines()) { manager->addLine(MacroManager::Gui, ss.str().c_str()); } - - // If the double click starts an editing, let the transaction persist - if (!editDoc && Application::Instance->editDocument()) { - committer.setEnable(false); - } } else { if (!vp->doubleClicked()) { @@ -2423,23 +2423,27 @@ bool TreeWidget::dropInDocument( infos.reserve(items.size()); bool syncPlacement = TreeParams::getSyncPlacement(); - App::AutoTransaction committer( - da == Qt::LinkAction ? "Link object" - : da == Qt::CopyAction ? "Copy object" - : "Move object" - ); + int tid = 0; + std::string transName = da == Qt::LinkAction ? "Link object" + : da == Qt::CopyAction ? "Copy object" + : "Move object"; // check if items can be dragged for (auto& v : items) { auto item = v.first; auto obj = item->object()->getObject(); auto parentItem = item->getParentItem(); + + tid = obj->getDocument()->openTransaction( + transName, + tid + ); // If the same document already has this transaction opened, it is ignored if (parentItem) { bool allParentsOK = canDragFromParents(parentItem, obj, nullptr); if (!allParentsOK || !parentItem->object()->canDragObjects() || !parentItem->object()->canDragObject(obj)) { - committer.close(true); + App::GetApplication().abortTransaction(tid); TREE_ERR( "'" << obj->getFullName() << "' cannot be dragged out of '" << parentItem->object()->getObject()->getFullName() << "'" @@ -2641,7 +2645,7 @@ bool TreeWidget::dropInDocument( errMsg = "Unknown exception"; } if (!errMsg.empty()) { - committer.close(true); + App::GetApplication().abortTransaction(tid); QMessageBox::critical( getMainWindow(), QObject::tr("Drag & drop failed"), @@ -2649,6 +2653,8 @@ bool TreeWidget::dropInDocument( ); return false; } + + App::GetApplication().commitTransaction(tid); return touched; } @@ -2716,14 +2722,12 @@ bool TreeWidget::dropInObject( ); } - // Open command - App::AutoTransaction committer("Drop object"); - bool syncPlacement = TreeParams::getSyncPlacement() && targetItemObj->isGroup(); bool setSelection = true; std::vector draggedObjects; std::vector> droppedObjects; std::vector infos; + int tid = 0; // Only keep text names here, because you never know when doing drag // and drop some object may delete other objects. infos.reserve(items.size()); @@ -2732,6 +2736,7 @@ bool TreeWidget::dropInObject( auto& info = infos.back(); auto item = v.first; App::DocumentObject* obj = item->object()->getObject(); + tid = obj->getDocument()->openTransaction("Drop object"); std::ostringstream str; App::DocumentObject* topParent = nullptr; @@ -2768,7 +2773,7 @@ bool TreeWidget::dropInObject( info.parentDoc = vpp->getObject()->getDocument()->getName(); } else { - committer.close(true); + App::GetApplication().abortTransaction(tid); return false; } } @@ -2778,13 +2783,13 @@ bool TreeWidget::dropInObject( && !vp->canDropObjectEx(obj, owner, info.subname.c_str(), item->mySubs)) { if (event->possibleActions() & Qt::LinkAction) { if (items.size() > 1) { - committer.close(true); + App::GetApplication().abortTransaction(tid); TREE_TRACE("Cannot replace with more than one object"); return false; } auto ext = vp->getObject()->getExtensionByType(true); if ((!ext || !ext->getLinkedObjectProperty()) && !targetItemObj->getParentItem()) { - committer.close(true); + App::GetApplication().abortTransaction(tid); TREE_TRACE("Cannot replace without parent"); return false; } @@ -3037,7 +3042,7 @@ bool TreeWidget::dropInObject( errMsg = "Unknown exception"; } if (!errMsg.empty()) { - committer.close(true); + App::GetApplication().abortTransaction(tid); QMessageBox::critical( getMainWindow(), QObject::tr("Drag & drop failed"), @@ -3045,6 +3050,7 @@ bool TreeWidget::dropInObject( ); return false; } + App::GetApplication().commitTransaction(tid); return touched; } @@ -4346,13 +4352,13 @@ void DocumentItem::slotInEdit(const Gui::ViewProviderDocumentObject& v) QColor color(Base::Color::fromPackedRGB(col)); if (!getTree()->editingItem) { - auto doc = Application::Instance->editDocument(); - if (!doc) { + // In which cases would this return? theo-vt + if (!Application::Instance->isInEdit(document())) { return; } ViewProviderDocumentObject* parentVp = nullptr; std::string subname; - auto vp = doc->getInEdit(&parentVp, &subname); + auto vp = document()->getInEdit(&parentVp, &subname); if (!parentVp) { parentVp = freecad_cast(vp); } diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index a8183bd51a..aff655ece0 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -191,7 +191,10 @@ void ViewProvider::setEditViewer(View3DInventorViewer*, int ModNum) void ViewProvider::unsetEditViewer(View3DInventorViewer*) {} - +void ViewProvider::setActive(bool active) +{ + Q_UNUSED(active); +} bool ViewProvider::isUpdatesEnabled() const { return testStatus(UpdateData); diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index 4bc8179c73..2c4600cc80 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -559,6 +559,8 @@ public: virtual ViewProvider* startEditing(int ModNum = 0); bool isEditing() const; void finishEditing(); + virtual void setActive(bool active); + /// adjust viewer settings when editing a view provider virtual void setEditViewer(View3DInventorViewer*, int ModNum); /// restores viewer settings when leaving editing mode diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index 0090e3c3a7..0d1081fa2c 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -272,11 +272,11 @@ void ViewProviderDocumentObject::setShowable(bool enable) void ViewProviderDocumentObject::startDefaultEditMode() { - QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); - Gui::Command::openCommand(text.toUtf8()); - Gui::Document* document = this->getDocument(); if (document) { + QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); + document->openCommand(text.toUtf8()); // Command is opened here and individual dialogs have + // to close it document->setEdit(this, ViewProvider::Default); } } diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 0895d2d216..8d7335c28a 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -152,8 +152,10 @@ bool ViewProviderDragger::forwardToLink() ViewProviderDocumentObject* vpParent = nullptr; std::string subname; - auto doc = Application::Instance->editDocument(); - if (!doc) { + // since we don't want to edit another document, only forward if the + // current document is in edit + auto doc = getDocument(); + if (!Application::Instance->isInEdit(doc)) { return nullptr; } @@ -209,7 +211,7 @@ bool ViewProviderDragger::setEdit(int ModNum) transformDragger->addFinishCallback(dragFinishCallback, this); transformDragger->addMotionCallback(dragMotionCallback, this); - Gui::Control().showDialog(getTransformDialog()); + Gui::Control().showDialog(getTransformDialog(), getDocument()->getDocument()); updateDraggerPosition(); @@ -222,7 +224,7 @@ void ViewProviderDragger::unsetEdit(int ModNum) transformDragger.reset(); - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum) diff --git a/src/Gui/ViewProviderFeaturePython.h b/src/Gui/ViewProviderFeaturePython.h index f828611560..dbcf8fbe42 100644 --- a/src/Gui/ViewProviderFeaturePython.h +++ b/src/Gui/ViewProviderFeaturePython.h @@ -441,7 +441,8 @@ public: /// Starts to drag the object void dragObject(App::DocumentObject* obj) override { - App::AutoTransaction committer; + // AutoTransaction does not work the way it used to, plus the called method should deal with + // transaction itself - theo-vt switch (imp->dragObject(obj)) { case ViewProviderFeaturePythonImp::Accepted: case ViewProviderFeaturePythonImp::Rejected: @@ -477,7 +478,8 @@ public: /// If the dropped object type is accepted the object will be added as child void dropObject(App::DocumentObject* obj) override { - App::AutoTransaction committer; + // AutoTransaction does not work the way it used to, plus the called method should deal with + // transaction itself - theo-vt switch (imp->dropObject(obj)) { case ViewProviderFeaturePythonImp::Accepted: case ViewProviderFeaturePythonImp::Rejected: @@ -522,7 +524,8 @@ public: const std::vector& elements ) override { - App::AutoTransaction committer; + // AutoTransaction does not work the way it used to, plus the called method should deal with + // transaction itself - theo-vt std::string ret; if (!imp->dropObjectEx(obj, owner, subname, elements, ret)) { ret = ViewProviderT::dropObjectEx(obj, owner, subname, elements); @@ -676,7 +679,8 @@ protected: int replaceObject(App::DocumentObject* oldObj, App::DocumentObject* newObj) override { - App::AutoTransaction committer; + // AutoTransaction does not work the way it used to, plus the called method should deal with + // transaction itself - theo-vt switch (imp->replaceObject(oldObj, newObj)) { case ViewProviderFeaturePythonImp::Accepted: return 1; @@ -717,7 +721,8 @@ public: protected: bool doubleClicked() override { - App::AutoTransaction committer; + // AutoTransaction does not work the way it used to, plus the called method should deal with + // transaction itself - theo-vt switch (imp->doubleClicked()) { case ViewProviderFeaturePythonImp::Accepted: return true; diff --git a/src/Gui/ViewProviderImagePlane.cpp b/src/Gui/ViewProviderImagePlane.cpp index 181e70ff8e..1493c1cc1b 100644 --- a/src/Gui/ViewProviderImagePlane.cpp +++ b/src/Gui/ViewProviderImagePlane.cpp @@ -39,11 +39,12 @@ #include +#include +#include #include #include #include #include -#include #include "ViewProviderImagePlane.h" @@ -184,7 +185,7 @@ void ViewProviderImagePlane::manipulateImage() { auto dialog = new TaskImageDialog(getObject()); - Gui::Control().showDialog(dialog); + Gui::Control().showDialog(dialog, getDocument()->getDocument()); } void ViewProviderImagePlane::resizePlane(float xsize, float ysize) diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index efbb7af59b..fae9fea7bd 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -2972,6 +2972,8 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch return; } + Gui::Document* doc = getDocument(); + _setupContextMenu(ext, menu, receiver, member); Gui::ActionFunction* func = nullptr; @@ -3023,8 +3025,14 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch options |= App::Link::OnChangeCopyOptions::ApplyAll; } - App::AutoTransaction guard("Setup configurable object"); + int tid = 0; auto sels = dlg.getSelections(DlgObjectSelection::SelectionOptions::InvertSort); + + // Open transaction on all touched documents if there is more than one + for (const auto& sel : sels) { + tid = sel->getDocument()->openTransaction("Setup configurable object", tid); + } + for (const auto& exclude : excludes) { auto iter = std::lower_bound(sels.begin(), sels.end(), exclude); if (iter == sels.end() || *iter != exclude) { @@ -3053,6 +3061,8 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch } } Command::updateActive(); + + App::GetApplication().commitTransaction(tid); } catch (Base::Exception& e) { e.reportException(); @@ -3070,9 +3080,9 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch if (!func) { func = new Gui::ActionFunction(menu); } - func->trigger(act, [ext]() { + func->trigger(act, [ext, doc]() { try { - App::AutoTransaction guard("Enable Link copy on change"); + App::AutoTransaction guard(doc->openCommand("Enable Link copy on change")); ext->getLinkCopyOnChangeProperty()->setValue(1); Command::updateActive(); } @@ -3088,9 +3098,9 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch ) ); act->setData(-1); - func->trigger(act, [ext]() { + func->trigger(act, [ext, doc]() { try { - App::AutoTransaction guard("Enable Link tracking"); + App::AutoTransaction guard(doc->openCommand("Enable Link tracking")); ext->getLinkCopyOnChangeProperty()->setValue(3); Command::updateActive(); } @@ -3107,9 +3117,9 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch if (!func) { func = new Gui::ActionFunction(menu); } - func->trigger(act, [ext]() { + func->trigger(act, [ext, doc]() { try { - App::AutoTransaction guard("Disable copy on change"); + App::AutoTransaction guard(doc->openCommand("Disable copy on change")); ext->getLinkCopyOnChangeProperty()->setValue((long)0); Command::updateActive(); } @@ -3132,9 +3142,9 @@ void ViewProviderLink::setupContextMenu(QMenu* menu, QObject* receiver, const ch if (!func) { func = new Gui::ActionFunction(menu); } - func->trigger(act, [ext]() { + func->trigger(act, [ext, doc]() { try { - App::AutoTransaction guard("Link refresh"); + App::AutoTransaction guard(doc->openCommand("Link refresh")); ext->syncCopyOnChange(); Command::updateActive(); } @@ -3163,9 +3173,12 @@ void ViewProviderLink::_setupContextMenu( if (ext->getLinkedObjectProperty() && ext->_getShowElementProperty() && ext->_getElementCountValue() > 1) { - auto action = menu->addAction(QObject::tr("Toggle Array Elements"), [ext] { + Gui::Document* doc = getDocument(); + auto action = menu->addAction(QObject::tr("Toggle Array Elements"), [ext, doc] { try { - App::AutoTransaction guard(QT_TRANSLATE_NOOP("Command", "Toggle array elements")); + App::AutoTransaction guard( + doc->openCommand(QT_TRANSLATE_NOOP("Command", "Toggle array elements")) + ); ext->getShowElementProperty()->setValue(!ext->getShowElementValue()); Command::updateActive(); } @@ -3273,15 +3286,18 @@ bool ViewProviderLink::initDraggingPlacement() FC_ERR("no placement"); return false; } - auto doc = Application::Instance->editDocument(); - if (!doc) { - FC_ERR("no editing document"); + + // Used to check for the only document in edit + // now we specifically ask for the document to be the vp's document + // I think it makes sense but there may be cases I did not consider - theo-vt + if (!Application::Instance->isInEdit(getDocument())) { + FC_ERR("document is not in edit"); return false; } dragCtx = std::make_unique(); - dragCtx->preTransform = doc->getEditingTransform(); + dragCtx->preTransform = getDocument()->getEditingTransform(); const auto& pla = getObject()->getPlacementProperty()->getValue(); // Cancel out our own transformation from the editing transform, because @@ -3328,7 +3344,11 @@ ViewProvider* ViewProviderLink::startEditing(int mode) static thread_local bool _pendingTransform; static thread_local Matrix4D _editingTransform; - auto doc = Application::Instance->editDocument(); + // Used to take the document in edit when there could only be one at a time + // here we take the current document the vp's document if it is in edit + // I don't think there is a case where a this function is invoked in another document + // with the intent of modifying that other document - theo-vt + Gui::Document* doc = Application::Instance->isInEdit(getDocument()) ? getDocument() : nullptr; if (mode == ViewProvider::Transform) { if (_pendingTransform && doc) { @@ -3400,9 +3420,9 @@ bool ViewProviderLink::setEdit(int ModNum) if (!ext || !ext->getColoredElementsProperty()) { return false; } - TaskView::TaskDialog* dlg = Control().activeDialog(); + TaskView::TaskDialog* dlg = Control().activeDialog(getDocument()->getDocument()); if (dlg) { - Control().showDialog(dlg); + Control().showDialog(dlg, getDocument()->getDocument()); return false; } Selection().clearSelection(); @@ -3415,7 +3435,7 @@ bool ViewProviderLink::setEdit(int ModNum) void ViewProviderLink::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum) { if (ModNum == ViewProvider::Color) { - Gui::Control().showDialog(new TaskElementColors(this)); + Gui::Control().showDialog(new TaskElementColors(this), getDocument()->getDocument()); return; } diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index 7261d81337..d7070fadbc 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -32,10 +32,10 @@ #include #include -#include #include #include #include +#include #include "Document.h" #include "Tree.h" @@ -358,11 +358,6 @@ void PropertyEditor::openEditor(const QModelIndex& index) return; } - auto& app = App::GetApplication(); - if (app.getActiveTransaction()) { - FC_LOG("editor already transacting " << app.getActiveTransaction()); - return; - } auto item = static_cast(editingIndex.internalPointer()); auto items = item->getPropertyData(); for (auto propItem = item->parent(); items.empty() && propItem; propItem = propItem->parent()) { @@ -407,8 +402,8 @@ void PropertyEditor::openEditor(const QModelIndex& index) if (items.size() > 1) { str << "..."; } - transactionID = app.setActiveTransaction(str.str().c_str()); - FC_LOG("editor transaction " << app.getActiveTransaction()); + transactionID = obj->getDocument()->openTransaction(str.str().c_str()); + FC_LOG("editor transaction " << App::GetApplication().getActiveTransaction(&transactionID)); } void PropertyEditor::onItemActivated(const QModelIndex& index) @@ -462,13 +457,15 @@ void PropertyEditor::recomputeDocument(App::Document* doc) void PropertyEditor::closeTransaction() { - int tid = 0; - if (App::GetApplication().getActiveTransaction(&tid) && tid == transactionID) { + App::Document* doc = App::GetApplication().getActiveDocument(); + if (!doc) { + return; + } + if (doc->getBookedTransactionID() == transactionID) { if (autoupdate) { - App::Document* doc = App::GetApplication().getActiveDocument(); recomputeDocument(doc); } - App::GetApplication().closeActiveTransaction(); + doc->commitTransaction(); } } @@ -941,15 +938,20 @@ std::unordered_set PropertyEditor::acquireSelectedProperties() c void PropertyEditor::removeProperties(const std::unordered_set& props) { - App::AutoTransaction committer("Remove property"); + int tid = 0; for (auto prop : props) { try { + if (App::Document* doc = propertyDocument(prop->getContainer())) { + tid = doc->openTransaction("Remove property"); + } prop->getContainer()->removeDynamicProperty(prop->getName()); } catch (Base::Exception& e) { + App::GetApplication().abortTransaction(tid); e.reportException(); } } + App::GetApplication().commitTransaction(tid); } void PropertyEditor::contextMenuEvent(QContextMenuEvent*) @@ -1160,9 +1162,14 @@ void PropertyEditor::contextMenuEvent(QContextMenuEvent*) if (container == nullptr) { return; } - App::AutoTransaction committer("Add property"); + int tid = 0; + if (App::Document* doc = propertyDocument(container)) { + tid = doc->openTransaction("Add property"); + } Gui::Dialog::DlgAddProperty dlg(Gui::getMainWindow(), container); dlg.exec(); + + App::GetApplication().commitTransaction(tid); return; } case MA_EditPropTooltip: { @@ -1203,8 +1210,10 @@ void PropertyEditor::contextMenuEvent(QContextMenuEvent*) || prop->testStatus(App::Property::LockDynamic)) { break; } - - App::AutoTransaction committer("Rename property"); + int tid = 0; + if (App::Document* doc = propertyDocument(prop->getContainer())) { + tid = doc->openTransaction("Rename property"); + } const char* oldName = prop->getName(); QString res = QInputDialog::getText( Gui::getMainWindow(), @@ -1222,9 +1231,11 @@ void PropertyEditor::contextMenuEvent(QContextMenuEvent*) prop->getContainer()->renameDynamicProperty(prop, newName.c_str()); } catch (Base::Exception& e) { + App::GetApplication().abortTransaction(tid); e.reportException(); break; } + App::GetApplication().commitTransaction(tid); break; } case MA_EditPropGroup: { @@ -1324,5 +1335,18 @@ QModelIndex PropertyEditor::indexResizable(QPoint mouse_pos) } return QModelIndex(); } +App::Document* PropertyEditor::propertyDocument(App::PropertyContainer* cont) const +{ + if (auto* doc = dynamic_cast(cont)) { + return doc; + } + if (auto* docObj = dynamic_cast(cont)) { + return docObj->getDocument(); + } + if (auto* vp = dynamic_cast(cont)) { + return vp->getDocument()->getDocument(); + } + return nullptr; +} #include "moc_PropertyEditor.cpp" diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index e51b2e82e0..f666b03f85 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -154,6 +154,8 @@ private: // and return the index of that cell if found QModelIndex indexResizable(QPoint mouse_pos); + App::Document* propertyDocument(App::PropertyContainer* cont) const; + private: PropertyItemDelegate* delegate; PropertyModel* propertyModel; diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 7edebb0394..adfaf0c4e9 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -61,6 +61,7 @@ #include #include #include +#include // NOLINTBEGIN(cppcoreguidelines-pro-*,cppcoreguidelines-prefer-member-initializer) using namespace Gui::PropertyEditor; @@ -2650,12 +2651,14 @@ PlacementEditor::~PlacementEditor() = default; void PlacementEditor::browse() { - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog( + Gui::Application::Instance->activeDocument()->getDocument() + ); Gui::Dialog::TaskPlacement* task {}; task = qobject_cast(dlg); if (dlg && !task) { // there is already another task dialog which must be closed first - Gui::Control().showDialog(dlg); + Gui::Control().showDialog(dlg, Gui::Application::Instance->activeDocument()->getDocument()); return; } if (!task) { @@ -2669,7 +2672,7 @@ void PlacementEditor::browse() task->setPropertyName(propertyname); task->setSelection(Gui::Selection().getSelectionEx()); task->bindObject(); - Gui::Control().showDialog(task); + Gui::Control().showDialog(task, Gui::Application::Instance->activeDocument()->getDocument()); } void PlacementEditor::showValue(const QVariant& d) diff --git a/src/Mod/Assembly/CommandCreateAssembly.py b/src/Mod/Assembly/CommandCreateAssembly.py index c3ee92e51f..160c5e4c6a 100644 --- a/src/Mod/Assembly/CommandCreateAssembly.py +++ b/src/Mod/Assembly/CommandCreateAssembly.py @@ -68,7 +68,7 @@ class CommandCreateAssembly: return App.ActiveDocument is not None def Activated(self): - App.setActiveTransaction("New Assembly") + Gui.ActiveDocument.openCommand("New assembly") activeAssembly = UtilsAssembly.activeAssembly() Gui.addModule("UtilsAssembly") @@ -89,7 +89,7 @@ class CommandCreateAssembly: if not activeAssembly: Gui.doCommandGui("Gui.ActiveDocument.setEdit(assembly)") - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() class ActivateAssemblyTaskPanel: diff --git a/src/Mod/Assembly/CommandCreateBom.py b/src/Mod/Assembly/CommandCreateBom.py index 709b211745..0c28002a5f 100644 --- a/src/Mod/Assembly/CommandCreateBom.py +++ b/src/Mod/Assembly/CommandCreateBom.py @@ -115,7 +115,7 @@ class TaskAssemblyCreateBom(QtCore.QObject): pref = Preferences.preferences() if bomObj: - App.setActiveTransaction("Edit Bill Of Materials") + Gui.ActiveDocument.openCommand("Edit Bill Of Materials") for name in bomObj.columnsNames: if name in ColumnNames: @@ -126,7 +126,7 @@ class TaskAssemblyCreateBom(QtCore.QObject): self.bomObj = bomObj else: - App.setActiveTransaction("Create Bill Of Materials") + Gui.ActiveDocument.openCommand("Create Bill Of Materials") # Add the columns for name in TranslatedColumnNames: @@ -152,7 +152,7 @@ class TaskAssemblyCreateBom(QtCore.QObject): def accept(self): self.deactivate() - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() self.bomObj.recompute() @@ -162,7 +162,7 @@ class TaskAssemblyCreateBom(QtCore.QObject): def reject(self): self.deactivate() - App.closeActiveTransaction(True) + Gui.ActiveDocument.abortCommand() return True def deactivate(self): diff --git a/src/Mod/Assembly/CommandCreateJoint.py b/src/Mod/Assembly/CommandCreateJoint.py index 4d32057217..c9f6c3d391 100644 --- a/src/Mod/Assembly/CommandCreateJoint.py +++ b/src/Mod/Assembly/CommandCreateJoint.py @@ -455,7 +455,7 @@ class CommandToggleGrounded: if not selection: return - App.setActiveTransaction("Toggle grounded") + Gui.ActiveDocument("Toggle grounded") for sel in selection: # If you select 2 solids (bodies for example) within an assembly. # There'll be a single sel but 2 SubElementNames. @@ -500,7 +500,7 @@ class CommandToggleGrounded: # Create groundedJoint. createGroundedJoint(moving_part) - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() if App.GuiUp: diff --git a/src/Mod/Assembly/CommandCreateSimulation.py b/src/Mod/Assembly/CommandCreateSimulation.py index 9f69c9b85f..241d7cc641 100644 --- a/src/Mod/Assembly/CommandCreateSimulation.py +++ b/src/Mod/Assembly/CommandCreateSimulation.py @@ -821,10 +821,10 @@ class TaskAssemblyCreateSimulation(QtCore.QObject): if simFeaturePy: self.simFeaturePy = simFeaturePy - App.setActiveTransaction("Edit " + simFeaturePy.Label + " Simulation") + Gui.ActiveDocument.openCommand("Edit " + simFeaturePy.Label + " Simulation") self.onMotionsChanged() else: - App.setActiveTransaction("Create Simulation") + Gui.ActiveDocument.openCommand("Create Simulation") self.createSimulationObject() self.setUiInitialValues() @@ -860,12 +860,12 @@ class TaskAssemblyCreateSimulation(QtCore.QObject): def accept(self): self.deactivate() UtilsAssembly.restoreAssemblyPartsPlacements(self.assembly, self.initialPlcs) - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() return True def reject(self): self.deactivate() - App.closeActiveTransaction(True) + Gui.ActiveDocument.abortCommand() return True def deactivate(self): diff --git a/src/Mod/Assembly/CommandCreateView.py b/src/Mod/Assembly/CommandCreateView.py index 38806d0131..5c27f2f06d 100644 --- a/src/Mod/Assembly/CommandCreateView.py +++ b/src/Mod/Assembly/CommandCreateView.py @@ -628,14 +628,15 @@ class TaskAssemblyCreateView(QtCore.QObject): self.initialPlcs = UtilsAssembly.saveAssemblyPartsPlacements(self.assembly) if viewObj: - App.setActiveTransaction("Edit Exploded View") + Gui.ActiveDocument.openCommand("Edit Exploded View") + self.viewObj = viewObj for move in self.viewObj.Group: move.Visibility = True self.onMovesChanged() else: - App.setActiveTransaction("Create Exploded View") + Gui.ActiveDocument.openCommand("Create Exploded View") self.createExplodedViewObject() Gui.Selection.addSelectionGate( @@ -669,14 +670,15 @@ class TaskAssemblyCreateView(QtCore.QObject): more = UtilsAssembly.generatePropertySettings(move) commands = commands + more Gui.doCommand(commands[:-1]) # Don't use the last \n - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() self.viewObj.purgeTouched() + return True def reject(self): self.deactivate() - App.closeActiveTransaction(True) + Gui.ActiveDocument.abortCommand() App.activeDocument().recompute() return True diff --git a/src/Mod/Assembly/CommandInsertLink.py b/src/Mod/Assembly/CommandInsertLink.py index 882c4b4904..31aa08b2f5 100644 --- a/src/Mod/Assembly/CommandInsertLink.py +++ b/src/Mod/Assembly/CommandInsertLink.py @@ -140,7 +140,7 @@ class TaskAssemblyInsertLink(QtCore.QObject): self.buildPartList() - App.setActiveTransaction("Insert Component") + Gui.ActiveDocument.openCommand("Insert Component") # Listen for external deletions to keep the list in sync self.docObserver = InsertLinkObserver(self.onObjectDeleted) @@ -184,13 +184,13 @@ class TaskAssemblyInsertLink(QtCore.QObject): ) Gui.doCommandSkip(commands[:-1]) # Get rid of last \n - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() return True def reject(self): self.deactivated() - App.closeActiveTransaction(True) + Gui.ActiveDocument.abortCommand() return True def deactivated(self): diff --git a/src/Mod/Assembly/CommandInsertNewPart.py b/src/Mod/Assembly/CommandInsertNewPart.py index 9f578439f5..2c48a6055e 100644 --- a/src/Mod/Assembly/CommandInsertNewPart.py +++ b/src/Mod/Assembly/CommandInsertNewPart.py @@ -208,7 +208,7 @@ class TaskAssemblyNewPart(JointObject.TaskAssemblyCreateJoint): self.createPart() - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() return True diff --git a/src/Mod/Assembly/CommandSolveAssembly.py b/src/Mod/Assembly/CommandSolveAssembly.py index 3c2bdd5175..aa7c52b443 100644 --- a/src/Mod/Assembly/CommandSolveAssembly.py +++ b/src/Mod/Assembly/CommandSolveAssembly.py @@ -66,7 +66,7 @@ class CommandSolveAssembly: App.setActiveTransaction("Solve assembly") assembly.recompute(True) - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() if App.GuiUp: diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index ad164ea430..4c9bfab42f 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -297,9 +297,7 @@ bool ViewProviderAssembly::setEdit(int mode) this->getObject()->getNameInDocument() ); - setDragger(); - - attachSelection(); + setupActiveAndInEdit(); updateTaskPanel(true); @@ -337,8 +335,7 @@ void ViewProviderAssembly::unsetEdit(int mode) partMoving = false; docsToMove.clear(); - unsetDragger(); - detachSelection(); + unsetupActiveAndInEdit(); // Check if the view is still active before trying to deactivate the assembly. auto activeView = getDocument()->getActiveView(); @@ -422,6 +419,26 @@ bool ViewProviderAssembly::isInEditMode() const { return asmDragger != nullptr; } +void ViewProviderAssembly::setupActiveAndInEdit() +{ + setDragger(); + attachSelection(); +} +void ViewProviderAssembly::unsetupActiveAndInEdit() +{ + unsetDragger(); + detachSelection(); +} +void ViewProviderAssembly::setActive(bool active) +{ + if (active) { + setupActiveAndInEdit(); + } + else { + unsetupActiveAndInEdit(); + } +} + App::DocumentObject* ViewProviderAssembly::getActivePart() const { @@ -436,7 +453,7 @@ bool ViewProviderAssembly::keyPressed(bool pressed, int key) { if (key == SoKeyboardEvent::ESCAPE) { if (isInEditMode()) { - if (Gui::Control().activeDialog()) { + if (Gui::Control().activeDialog(nullptr)) { return true; } @@ -1083,7 +1100,7 @@ void ViewProviderAssembly::tryInitMove(const SbVec2s& cursorPos, Gui::View3DInve } if (moveInCommand) { - Gui::Command::openCommand(tr("Move part").toStdString().c_str()); + getDocument()->openCommand(tr("Move part").toStdString().c_str()); } partMoving = true; @@ -1146,7 +1163,7 @@ void ViewProviderAssembly::endMove() } if (moveInCommand) { - Gui::Command::commitCommand(); + getDocument()->commitCommand(); } } @@ -1875,11 +1892,11 @@ void ViewProviderAssembly::updateTaskPanel(bool show) if (show && !taskSolver) { taskSolver = new TaskAssemblyMessages(this); - taskView->addContextualPanel(taskSolver); + taskView->addContextualPanel(taskSolver, this->getObject()->getDocument()); UpdateSolverInformation(); } else if (!show && taskSolver) { - taskView->removeContextualPanel(taskSolver); + taskView->removeContextualPanel(taskSolver, this->getObject()->getDocument()); taskSolver = nullptr; } } diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.h b/src/Mod/Assembly/Gui/ViewProviderAssembly.h index 10e14ae7a2..7cf7af6f6c 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.h +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.h @@ -127,6 +127,10 @@ public: void setEditViewer(Gui::View3DInventorViewer*, int ModNum) override; bool isInEditMode() const; + void setActive(bool active) override; + void setupActiveAndInEdit(); + void unsetupActiveAndInEdit(); + /// Ask the view provider if it accepts object deletions while in edit bool acceptDeletionsInEdit() override { diff --git a/src/Mod/Assembly/Gui/ViewProviderAssemblyLink.cpp b/src/Mod/Assembly/Gui/ViewProviderAssemblyLink.cpp index fdd6e8a960..feea31e752 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssemblyLink.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssemblyLink.cpp @@ -137,14 +137,14 @@ void ViewProviderAssemblyLink::setupContextMenu(QMenu* menu, QObject* receiver, func->trigger(act, [this]() { auto* assemblyLink = dynamic_cast(getObject()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Toggle Rigid")); + getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Toggle Rigid")); Gui::cmdAppObjectArgs( assemblyLink, "Rigid = %s", assemblyLink->Rigid.getValue() ? "False" : "True" ); - Gui::Command::commitCommand(); + getDocument()->commitCommand(); Gui::Selection().clearSelection(); }); diff --git a/src/Mod/Assembly/JointObject.py b/src/Mod/Assembly/JointObject.py index 84bed3dfd0..539ff2b314 100644 --- a/src/Mod/Assembly/JointObject.py +++ b/src/Mod/Assembly/JointObject.py @@ -1186,7 +1186,7 @@ class ViewProviderJoint: return None def doubleClicked(self, vobj): - App.closeActiveTransaction(True) # Close the auto-transaction + App.ActiveDocument.abortTransaction() # Close the auto-transaction task = Gui.Control.activeTaskDialog() if task: @@ -1534,7 +1534,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject): self.creating = False self.joint = jointObj self.jointName = jointObj.Label - App.setActiveTransaction("Edit " + self.jointName + " Joint") + Gui.ActiveDocument.openCommand("Edit " + self.jointName + " Joint") self.updateTaskboxFromJoint() self.visibilityBackup = self.joint.Visibility @@ -1544,9 +1544,9 @@ class TaskAssemblyCreateJoint(QtCore.QObject): self.creating = True self.jointName = self.jForm.jointType.currentText().replace(" ", "") if self.activeType == "Part": - App.setActiveTransaction("Transform") + Gui.ActiveDocument.openCommand("Transform") else: - App.setActiveTransaction("Create " + self.jointName + " Joint") + Gui.ActiveDocument.openCommand("Create " + self.jointName + " Joint") self.refs = [] self.presel_ref = None @@ -1634,12 +1634,12 @@ class TaskAssemblyCreateJoint(QtCore.QObject): self.assembly.recompute(True) - App.closeActiveTransaction() + Gui.ActiveDocument.commitCommand() return True def reject(self): self.deactivate() - App.closeActiveTransaction(True) + Gui.ActiveDocument.abortCommand() self.assembly.recompute(True) return True @@ -1652,7 +1652,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject): Gui.Selection.removeSelectionGate() Gui.Selection.removeObserver(self) Gui.Selection.setSelectionStyle(Gui.Selection.SelectionStyle.NormalSelection) - App.closeActiveTransaction(True) + App.ActiveDocument.abortTransaction() def deactivate(self): global activeTask diff --git a/src/Mod/Assembly/UtilsAssembly.py b/src/Mod/Assembly/UtilsAssembly.py index 29a5ab4c1a..6bce084422 100644 --- a/src/Mod/Assembly/UtilsAssembly.py +++ b/src/Mod/Assembly/UtilsAssembly.py @@ -380,6 +380,9 @@ def getGlobalPlacement(ref, targetObj=None): def isThereOneRootAssembly(): + if Gui.activeDocument() is None: + return False + for part in Gui.activeDocument().TreeRootObjects: if part.TypeId == "Assembly::AssemblyObject": return True diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index 8c44f5a43d..a11b87a6f4 100644 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -1192,7 +1192,7 @@ static void DefineNodesCallback(void* ud, SoEventCallback* n) std::string str = getSelectedNodes(view); if (!str.empty()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Place robot")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Place robot")); Gui::Command::doCommand( Gui::Command::Doc, "App.ActiveDocument.addObject('Fem::FemSetNodesObject','NodeSet')" @@ -1208,7 +1208,7 @@ static void DefineNodesCallback(void* ud, SoEventCallback* n) Analysis->getNameInDocument() ); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } } @@ -1352,7 +1352,7 @@ static void DefineElementsCallback(void* ud, SoEventCallback* n) std::string str = getSelectedNodes(view); if (!str.empty()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Place robot")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Place robot")); Gui::Command::doCommand( Gui::Command::Doc, "App.ActiveDocument.addObject('Fem::FemSetElementNodesObject','ElementSet')" @@ -1368,7 +1368,7 @@ static void DefineElementsCallback(void* ud, SoEventCallback* n) Analysis->getNameInDocument() ); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } } diff --git a/src/Mod/Fem/Gui/TaskCreateNodeSet.cpp b/src/Mod/Fem/Gui/TaskCreateNodeSet.cpp index c905e6b99d..e633db5219 100644 --- a/src/Mod/Fem/Gui/TaskCreateNodeSet.cpp +++ b/src/Mod/Fem/Gui/TaskCreateNodeSet.cpp @@ -85,6 +85,16 @@ TaskCreateNodeSet::TaskCreateNodeSet(Fem::FemSetNodesObject* pcObject, QWidget* ui->groupBox_AngleSearch->setEnabled(false); } +void TaskCreateNodeSet::setSelectionGate() +{ + if (selectionMode == none) { + Gui::Selection().rmvSelectionGate(); + } + else if (selectionMode == PickElement) { + Gui::Selection().addSelectionGate(new FemSelectionGate(FemSelectionGate::Element)); + } +} + void TaskCreateNodeSet::Poly() { Gui::Document* doc = Gui::Application::Instance->activeDocument(); @@ -102,7 +112,7 @@ void TaskCreateNodeSet::Pick() if (selectionMode == none) { selectionMode = PickElement; Gui::Selection().clearSelection(); - Gui::Selection().addSelectionGate(new FemSelectionGate(FemSelectionGate::Element)); + setSelectionGate(); } } @@ -221,7 +231,7 @@ void TaskCreateNodeSet::onSelectionChanged(const Gui::SelectionChanges& msg) } selectionMode = none; - Gui::Selection().rmvSelectionGate(); + setSelectionGate(); MeshViewProvider->setHighlightNodes(tempSet); } diff --git a/src/Mod/Fem/Gui/TaskCreateNodeSet.h b/src/Mod/Fem/Gui/TaskCreateNodeSet.h index e111f9126f..adccd03706 100644 --- a/src/Mod/Fem/Gui/TaskCreateNodeSet.h +++ b/src/Mod/Fem/Gui/TaskCreateNodeSet.h @@ -58,6 +58,8 @@ public: explicit TaskCreateNodeSet(Fem::FemSetNodesObject* pcObject, QWidget* parent = nullptr); ~TaskCreateNodeSet() override; + void setSelectionGate(); + std::set tempSet; ViewProviderFemMesh* MeshViewProvider; diff --git a/src/Mod/Fem/Gui/TaskDlgCreateElementSet.cpp b/src/Mod/Fem/Gui/TaskDlgCreateElementSet.cpp index f79a9019d2..fd5aedb06c 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateElementSet.cpp +++ b/src/Mod/Fem/Gui/TaskDlgCreateElementSet.cpp @@ -21,6 +21,7 @@ * * ***************************************************************************/ +#include #include #include #include @@ -71,10 +72,14 @@ bool TaskDlgCreateElementSet::accept() param->MeshViewProvider->resetHighlightNodes(); FemSetElementNodesObject->Label.setValue(name->name); Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); + FemSetElementNodesObject->getDocument() + ->commitTransaction(); // Opened in ViewProviderDocumentObject::startDefaultEditMode() return true; } catch (const Base::Exception& e) { + FemSetElementNodesObject->getDocument() + ->abortTransaction(); // Opened in ViewProviderDocumentObject::startDefaultEditMode() Base::Console().warning("TaskDlgCreateElementSet::accept(): %s\n", e.what()); } @@ -85,7 +90,8 @@ bool TaskDlgCreateElementSet::reject() { FemSetElementNodesObject->execute(); param->MeshViewProvider->resetHighlightNodes(); - Gui::Command::abortCommand(); + FemSetElementNodesObject->getDocument() + ->abortTransaction(); // Opened in ViewProviderDocumentObject::startDefaultEditMode() Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); return true; @@ -94,4 +100,13 @@ bool TaskDlgCreateElementSet::reject() void TaskDlgCreateElementSet::helpRequested() {} +void TaskDlgCreateElementSet::activate() +{ + param->attachSelection(); +} +void TaskDlgCreateElementSet::deactivate() +{ + param->detachSelection(); +} + #include "moc_TaskDlgCreateElementSet.cpp" diff --git a/src/Mod/Fem/Gui/TaskDlgCreateElementSet.h b/src/Mod/Fem/Gui/TaskDlgCreateElementSet.h index 61faa15bdc..bda58583a0 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateElementSet.h +++ b/src/Mod/Fem/Gui/TaskDlgCreateElementSet.h @@ -64,6 +64,9 @@ public: /// is called by the framework if the user press the help button void helpRequested() override; + void activate() override; + void deactivate() override; + /// returns for Close and Help button QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp index ba9f73ccfa..0b0d1a4bd2 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp +++ b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp @@ -21,6 +21,7 @@ ***************************************************************************/ +#include #include #include #include @@ -73,10 +74,12 @@ bool TaskDlgCreateNodeSet::accept() param->MeshViewProvider->resetHighlightNodes(); FemSetNodesObject->Label.setValue(name->name); Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); + FemSetNodesObject->getDocument()->commitTransaction(); return true; } catch (const Base::Exception& e) { + FemSetNodesObject->getDocument()->abortTransaction(); Base::Console().warning("TaskDlgCreateNodeSet::accept(): %s\n", e.what()); } @@ -90,7 +93,7 @@ bool TaskDlgCreateNodeSet::reject() // if(doc) // doc->resetEdit(); param->MeshViewProvider->resetHighlightNodes(); - Gui::Command::abortCommand(); + FemSetNodesObject->getDocument()->abortTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); return true; @@ -99,4 +102,14 @@ bool TaskDlgCreateNodeSet::reject() void TaskDlgCreateNodeSet::helpRequested() {} +void TaskDlgCreateNodeSet::activate() +{ + param->attachSelection(); + param->setSelectionGate(); +} +void TaskDlgCreateNodeSet::deactivate() +{ + param->detachSelection(); +} + #include "moc_TaskDlgCreateNodeSet.cpp" diff --git a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h index a6e38bbc0a..201b658602 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h +++ b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h @@ -62,6 +62,9 @@ public: /// is called by the framework if the user press the help button void helpRequested() override; + void activate() override; + void deactivate() override; + /// returns for Close and Help button QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp b/src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp index 891546409e..e345cc2f7c 100644 --- a/src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp +++ b/src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp @@ -65,9 +65,9 @@ TaskDlgMeshShapeNetgen::~TaskDlgMeshShapeNetgen() = default; void TaskDlgMeshShapeNetgen::open() { // a transaction is already open at creation time of the mesh - if (!Gui::Command::hasPendingCommand()) { + if (!ViewProviderFemMeshShapeNetgen->getDocument()->hasPendingCommand()) { QString msg = tr("Edit FEM mesh"); - Gui::Command::openCommand((const char*)msg.toUtf8()); + FemMeshShapeNetgenObject->getDocument()->openTransaction((const char*)msg.toUtf8()); } } @@ -90,6 +90,7 @@ void TaskDlgMeshShapeNetgen::clicked(int button) bool TaskDlgMeshShapeNetgen::accept() { + App::Document* doc = FemMeshShapeNetgenObject->getDocument(); try { if (param->touched) { Gui::WaitCursor wc; @@ -112,14 +113,15 @@ bool TaskDlgMeshShapeNetgen::accept() } // FemSetNodesObject->Label.setValue(name->name); - App::Document* doc = FemMeshShapeNetgenObject->getDocument(); + doc->commitTransaction(); + Gui::cmdAppDocument(doc, "recompute()"); Gui::cmdGuiDocument(doc, "resetEdit()"); - Gui::Command::commitCommand(); return true; } catch (const Base::Exception& e) { + doc->abortTransaction(); Base::Console().warning("TaskDlgMeshShapeNetgen::accept(): %s\n", e.what()); } @@ -133,8 +135,8 @@ bool TaskDlgMeshShapeNetgen::reject() // //if(doc) // // doc->resetEdit(); // param->MeshViewProvider->resetHighlightNodes(); - Gui::Command::abortCommand(); App::Document* doc = FemMeshShapeNetgenObject->getDocument(); + doc->abortTransaction(); Gui::cmdGuiDocument(doc, "resetEdit()"); Gui::cmdAppDocument(doc, "recompute()"); diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.cpp b/src/Mod/Fem/Gui/TaskFemConstraint.cpp index a335357f1d..0850f09e79 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraint.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraint.cpp @@ -209,9 +209,9 @@ void TaskFemConstraint::createDeleteAction(QListWidget* parentList) void TaskDlgFemConstraint::open() { - if (!Gui::Command::hasPendingCommand()) { + if (!ConstraintView->getDocument()->hasPendingCommand()) { const char* typeName = ConstraintView->getObject()->getTypeId().getName(); - Gui::Command::openCommand(typeName); + ConstraintView->getDocument()->openCommand(typeName); ConstraintView->setVisible(true); } } @@ -252,9 +252,10 @@ bool TaskDlgFemConstraint::accept() throw Base::RuntimeError(ConstraintView->getObject()->getStatusString()); } Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); - Gui::Command::commitCommand(); + ConstraintView->getDocument()->commitCommand(); } catch (const Base::Exception& e) { + ConstraintView->getDocument()->abortCommand(); QMessageBox::warning(parameter, tr("Input error"), QString::fromLatin1(e.what())); return false; } @@ -265,11 +266,20 @@ bool TaskDlgFemConstraint::accept() bool TaskDlgFemConstraint::reject() { // roll back the changes - Gui::Command::abortCommand(); + ConstraintView->getDocument()->abortCommand(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); Gui::Command::updateActive(); return true; } +void TaskDlgFemConstraint::activate() +{ + parameter->attachSelection(); +} +void TaskDlgFemConstraint::deactivate() +{ + parameter->detachSelection(); +} + #include "moc_TaskFemConstraint.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.h b/src/Mod/Fem/Gui/TaskFemConstraint.h index 40efd3eb34..dd1746d704 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraint.h +++ b/src/Mod/Fem/Gui/TaskFemConstraint.h @@ -111,6 +111,10 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; + + void activate() override; + void deactivate() override; + bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/Fem/Gui/TaskFemConstraintInitialTemperature.cpp b/src/Mod/Fem/Gui/TaskFemConstraintInitialTemperature.cpp index b14b888a82..9676fcb1ce 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintInitialTemperature.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintInitialTemperature.cpp @@ -110,10 +110,14 @@ bool TaskDlgFemConstraintInitialTemperature::accept() if (!ConstraintView->getObject()->isValid()) { throw Base::RuntimeError(ConstraintView->getObject()->getStatusString()); } + + ConstraintView->getDocument()->commitCommand(); // Opened in + // ViewProviderDocumentObject::startDefaultEditMode() Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); - Gui::Command::commitCommand(); } catch (const Base::Exception& e) { + ConstraintView->getDocument()->abortCommand(); // Opened in + // ViewProviderDocumentObject::startDefaultEditMode() QMessageBox::warning(parameter, tr("Input error"), QString::fromLatin1(e.what())); return false; } diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.cpp b/src/Mod/Fem/Gui/TaskPostBoxes.cpp index 7213f5fb5a..3480c9f632 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.cpp +++ b/src/Mod/Fem/Gui/TaskPostBoxes.cpp @@ -353,9 +353,9 @@ void TaskDlgPost::connectSlots() void TaskDlgPost::open() { // only open a new command if none is pending (e.g. if the object was newly created) - if (!Gui::Command::hasPendingCommand()) { + if (!m_view->getDocument()->hasPendingCommand()) { auto text = std::string("Edit ") + m_view->getObject()->Label.getValue(); - Gui::Command::openCommand(text.c_str()); + m_view->getDocument()->openCommand(text.c_str()); } } @@ -387,8 +387,10 @@ bool TaskDlgPost::accept() } } } + m_view->getDocument()->commitCommand(); } catch (const Base::Exception& e) { + m_view->getDocument()->abortCommand(); QMessageBox::warning(nullptr, tr("Input error"), QString::fromLatin1(e.what())); return false; } @@ -400,7 +402,7 @@ bool TaskDlgPost::accept() bool TaskDlgPost::reject() { // roll back the done things - Gui::Command::abortCommand(); + m_view->getDocument()->abortCommand(); Gui::cmdGuiDocument(getDocumentName(), "resetEdit()"); return true; diff --git a/src/Mod/Fem/femguiutils/selection_widgets.py b/src/Mod/Fem/femguiutils/selection_widgets.py index 46719f4aec..ae5f28b174 100644 --- a/src/Mod/Fem/femguiutils/selection_widgets.py +++ b/src/Mod/Fem/femguiutils/selection_widgets.py @@ -495,6 +495,14 @@ class GeometryElementsSelection(QtGui.QWidget): # but close only one SelectionObserver on leaving the task panel self.sel_server = FemSelectionObserver(self.selectionParser, print_message) + def attachSelection(self): + if self.sel_server: + FreeCADGui.Selection.addObserver(self.sel_server) + + def detachSelection(self): + if self.sel_server: + FreeCADGui.Selection.removeObserver(self.sel_server) + def selectionParser(self, selection): if hasattr(selection[0], "Shape") and selection[1]: FreeCAD.Console.PrintMessage( diff --git a/src/Mod/Fem/femsolver/elmer/equations/equation.py b/src/Mod/Fem/femsolver/elmer/equations/equation.py index 0547869c26..0f260caf7c 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/equation.py +++ b/src/Mod/Fem/femsolver/elmer/equations/equation.py @@ -112,6 +112,12 @@ class _TaskPanel: self._recomputeAndRestore() return True + def activate(self): + self._selectionWidget.attachSelection() + + def deactivate(self): + self._selectionWidget.detachSelection() + def _restoreVisibility(self): if self._mesh is not None and self._part is not None: if self._meshVisible: diff --git a/src/Mod/Fem/femtaskpanels/base_femtaskpanel.py b/src/Mod/Fem/femtaskpanels/base_femtaskpanel.py index fdb562020c..2b8b889c11 100644 --- a/src/Mod/Fem/femtaskpanels/base_femtaskpanel.py +++ b/src/Mod/Fem/femtaskpanels/base_femtaskpanel.py @@ -53,3 +53,11 @@ class _BaseTaskPanel: gui_doc.Document.recompute() return True + + def activate(self): + if self._selectionWidget: + self._selectionWidget.attachSelection() + + def deactivate(self): + if self._selectionWidget: + self._selectionWidget.detachSelection() diff --git a/src/Mod/Measure/Gui/QuickMeasure.cpp b/src/Mod/Measure/Gui/QuickMeasure.cpp index e8206e950e..8a16f54b3c 100644 --- a/src/Mod/Measure/Gui/QuickMeasure.cpp +++ b/src/Mod/Measure/Gui/QuickMeasure.cpp @@ -107,7 +107,7 @@ void QuickMeasure::tryMeasureSelection() { Gui::Document* doc = Gui::Application::Instance->activeDocument(); measurement->clear(); - if (doc && Gui::Control().activeDialog() == nullptr) { + if (doc && Gui::Control().activeDialog(nullptr) == nullptr) { // we (still) have a doc and are not in a tool dialog where the user needs to click on stuff addSelectionToMeasurement(); } diff --git a/src/Mod/Measure/Gui/TaskMeasure.cpp b/src/Mod/Measure/Gui/TaskMeasure.cpp index 93129fd001..e3a0a44491 100644 --- a/src/Mod/Measure/Gui/TaskMeasure.cpp +++ b/src/Mod/Measure/Gui/TaskMeasure.cpp @@ -111,12 +111,7 @@ TaskMeasure::TaskMeasure() settings.beginGroup(QLatin1String(taskMeasureSettingsGroup)); delta = settings.value(QLatin1String(taskMeasureShowDeltaSettingsName), true).toBool(); mAutoSave = settings.value(QLatin1String(taskMeasureAutoSaveSettingsName), mAutoSave).toBool(); - if (settings.value(QLatin1String(taskMeasureGreedySelection), false).toBool()) { - Gui::Selection().setSelectionStyle(SelectionStyle::GreedySelection); - } - else { - Gui::Selection().setSelectionStyle(SelectionStyle::NormalSelection); - } + mGreedySelection = settings.value(QLatin1String(taskMeasureGreedySelection), false).toBool(); settings.endGroup(); showDelta = new QCheckBox(); @@ -220,7 +215,6 @@ TaskMeasure::TaskMeasure() Content.emplace_back(taskbox); // engage the selectionObserver - attachSelection(); if (auto* doc = App::GetApplication().getActiveDocument()) { m_deletedConnection = doc->signalDeletedObject.connect([this](auto&& obj) { @@ -228,8 +222,9 @@ TaskMeasure::TaskMeasure() }); } - if (!App::GetApplication().getActiveTransaction()) { - App::GetApplication().setActiveTransaction("Add Measurement"); + if (auto* doc = Gui::Application::Instance->activeDocument()) { + mTargetDoc = doc; + mTargetDoc->openCommand("Add Measurement"); } setAutoCloseOnDeletedDocument(true); @@ -593,8 +588,10 @@ bool TaskMeasure::apply(bool reset) } // Commit transaction - App::GetApplication().closeActiveTransaction(); - App::GetApplication().setActiveTransaction("Add Measurement"); + if (mTargetDoc) { + mTargetDoc->commitCommand(); + mTargetDoc->openCommand("Add Measurement"); + } return false; } @@ -604,7 +601,9 @@ bool TaskMeasure::reject() closeDialog(); // Abort transaction - App::GetApplication().closeActiveTransaction(true); + if (mTargetDoc) { + mTargetDoc->abortCommand(); + } return false; } @@ -624,6 +623,19 @@ void TaskMeasure::reset() this->update(); } +void TaskMeasure::activate() +{ + updateSelectionType(); + // engage the selectionObserver + attachSelection(); + qApp->installEventFilter(this); +} +void TaskMeasure::deactivate() +{ + Gui::Selection().setSelectionStyle(SelectionStyle::NormalSelection); + detachSelection(); + qApp->removeEventFilter(this); +} void TaskMeasure::removeObject() @@ -769,18 +781,23 @@ void TaskMeasure::newMeasurementBehaviourChanged(bool checked) { QSettings settings; settings.beginGroup(QLatin1String(taskMeasureSettingsGroup)); - if (!checked) { - Gui::Selection().setSelectionStyle(SelectionStyle::NormalSelection); - settings.setValue(QLatin1String(taskMeasureGreedySelection), false); + settings.setValue(QLatin1String(taskMeasureGreedySelection), true); + mGreedySelection = checked; + updateSelectionType(); + + settings.endGroup(); +} +void TaskMeasure::updateSelectionType() +{ + if (mGreedySelection) { + Gui::Selection().setSelectionStyle(SelectionStyle::GreedySelection); } else { - Gui::Selection().setSelectionStyle(SelectionStyle::GreedySelection); - settings.setValue(QLatin1String(taskMeasureGreedySelection), true); + Gui::Selection().setSelectionStyle(SelectionStyle::NormalSelection); } - settings.endGroup(); std::list hints; - if (checked) { + if (mGreedySelection) { hints = std::list { {tr("%1 new measurement, %2 toggle auto-save"), {{ModifierCtrl}, {ModifierShift}}} }; diff --git a/src/Mod/Measure/Gui/TaskMeasure.h b/src/Mod/Measure/Gui/TaskMeasure.h index 00a86d6419..125f86a8e2 100644 --- a/src/Mod/Measure/Gui/TaskMeasure.h +++ b/src/Mod/Measure/Gui/TaskMeasure.h @@ -68,6 +68,8 @@ public: bool reject() override; void reset(); void closed() override; + void activate() override; + void deactivate() override; bool hasSelection(); void clearSelection(); @@ -101,6 +103,7 @@ private: void showDeltaChanged(int checkState); void autoSaveChanged(bool checked); void newMeasurementBehaviourChanged(bool checked); + void updateSelectionType(); void setModeSilent(App::MeasureType* mode); App::MeasureType* getMeasureType(); void enableAnnotateButton(bool state); @@ -117,6 +120,8 @@ private: bool delta = true; bool mAutoSave = false; QString mLastUnitSelection = QLatin1String("-"); + bool mGreedySelection = false; + Gui::Document* mTargetDoc; }; } // namespace MeasureGui diff --git a/src/Mod/Mesh/Gui/DlgDecimating.cpp b/src/Mod/Mesh/Gui/DlgDecimating.cpp index 246402bd8f..0a14e07796 100644 --- a/src/Mod/Mesh/Gui/DlgDecimating.cpp +++ b/src/Mod/Mesh/Gui/DlgDecimating.cpp @@ -163,7 +163,7 @@ bool TaskDecimating::accept() Gui::Selection().clearSelection(); Gui::WaitCursor wc; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Mesh Decimating")); + float tolerance = float(widget->tolerance()); float reduction = float(widget->reduction()); @@ -172,6 +172,13 @@ bool TaskDecimating::accept() if (absolute) { targetSize = widget->targetNumberOfTriangles(); } + + // Here we assume that all meshes are in the same document + // if it turns out to not be the case then the transaction can be + // opened in the loop with the tid as an argument - theo-vt + int tid = meshes[0]->getDocument()->openTransaction( + QT_TRANSLATE_NOOP("Command", "Mesh Decimating") + ); for (auto mesh : meshes) { if (absolute) { Gui::cmdAppObjectArgs(mesh, "decimate(%i)", targetSize); @@ -180,8 +187,7 @@ bool TaskDecimating::accept() Gui::cmdAppObjectArgs(mesh, "decimate(%f, %f)", tolerance, reduction); } } - - Gui::Command::commitCommand(); + App::GetApplication().commitTransaction(tid); return true; } diff --git a/src/Mod/Mesh/Gui/DlgSmoothing.cpp b/src/Mod/Mesh/Gui/DlgSmoothing.cpp index cc17aa0890..d8fc3ff633 100644 --- a/src/Mod/Mesh/Gui/DlgSmoothing.cpp +++ b/src/Mod/Mesh/Gui/DlgSmoothing.cpp @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -166,11 +167,13 @@ bool TaskSmoothing::accept() } Gui::WaitCursor wc; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Mesh Smoothing")); + int tid = 0; bool hasSelection = false; for (auto it : meshes) { Mesh::Feature* mesh = static_cast(it); + tid = mesh->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Mesh Smoothing"), tid); + std::vector selection; if (widget->smoothSelection()) { // clear the selection before editing the mesh to avoid @@ -221,12 +224,12 @@ bool TaskSmoothing::accept() mesh->Mesh.finishEditing(); } - if (widget->smoothSelection() && !hasSelection) { - Gui::Command::abortCommand(); + if (widget->smoothSelection() && !hasSelection && tid) { + App::GetApplication().abortTransaction(tid); return false; } - Gui::Command::commitCommand(); + App::GetApplication().commitTransaction(tid); return true; } diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index cd55e75dbb..c1cc129543 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -234,7 +234,7 @@ void CmdPartPrimitives::activated(int iMsg) bool CmdPartPrimitives::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } namespace PartGui @@ -1424,7 +1424,7 @@ CmdPartBoolean::CmdPartBoolean() void CmdPartBoolean::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(getDocument()); if (!dlg) { dlg = new PartGui::TaskBooleanOperation(); } @@ -1433,7 +1433,7 @@ void CmdPartBoolean::activated(int iMsg) bool CmdPartBoolean::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1461,7 +1461,7 @@ void CmdPartExtrude::activated(int iMsg) bool CmdPartExtrude::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1490,7 +1490,7 @@ void CmdPartScale::activated(int iMsg) bool CmdPartScale::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1577,7 +1577,7 @@ void CmdPartRevolve::activated(int iMsg) bool CmdPartRevolve::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1605,7 +1605,7 @@ void CmdPartFillet::activated(int iMsg) bool CmdPartFillet::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1633,7 +1633,7 @@ void CmdPartChamfer::activated(int iMsg) bool CmdPartChamfer::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1661,7 +1661,7 @@ void CmdPartMirror::activated(int iMsg) bool CmdPartMirror::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1684,7 +1684,7 @@ CmdPartCrossSections::CmdPartCrossSections() void CmdPartCrossSections::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(getDocument()); if (!dlg) { std::vector shapes = PartGui::getShapesFromSelection(); Base::BoundBox3d bbox; @@ -1699,7 +1699,7 @@ void CmdPartCrossSections::activated(int iMsg) bool CmdPartCrossSections::isActive() { bool hasShapes = PartGui::hasShapesInSelection(); - return (hasShapes && !Gui::Control().activeDialog()); + return (hasShapes && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1729,7 +1729,7 @@ void CmdPartBuilder::activated(int iMsg) bool CmdPartBuilder::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1758,7 +1758,7 @@ void CmdPartLoft::activated(int iMsg) bool CmdPartLoft::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -1787,7 +1787,7 @@ void CmdPartSweep::activated(int iMsg) bool CmdPartSweep::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -2347,7 +2347,7 @@ void CmdCheckGeometry::activated(int iMsg) bool CmdCheckGeometry::isActive() { bool hasShapes = PartGui::hasShapesInSelection(); - return (hasShapes && !Gui::Control().activeDialog()); + return (hasShapes && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== @@ -2391,7 +2391,7 @@ void CmdColorPerFace::activated(int iMsg) bool CmdColorPerFace::isActive() { bool objectSelected = Gui::Selection().countObjectsOfType() == 1; - return (hasActiveDocument() && !Gui::Control().activeDialog() && objectSelected); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument()) && objectSelected); } //=========================================================================== @@ -2456,7 +2456,7 @@ void CmdPartProjectionOnSurface::activated(int iMsg) bool CmdPartProjectionOnSurface::isActive() { - return (hasActiveDocument() && !Gui::Control().activeDialog()); + return (hasActiveDocument() && !Gui::Control().activeDialog(getDocument())); } //=========================================================================== diff --git a/src/Mod/Part/Gui/CommandSimple.cpp b/src/Mod/Part/Gui/CommandSimple.cpp index 3fc1745a67..5b4e54e48e 100644 --- a/src/Mod/Part/Gui/CommandSimple.cpp +++ b/src/Mod/Part/Gui/CommandSimple.cpp @@ -247,7 +247,8 @@ CmdPartSimpleCopy::CmdPartSimpleCopy() static void _copyShape(const char* cmdName, bool resolve, bool needElement = false, bool refine = false) { Gui::WaitCursor wc; - Gui::Command::openCommand(cmdName); + + int tid = Gui::Command::openActiveDocumentCommand(cmdName); for (auto& sel : Gui::Selection().getSelectionEx( "*", App::DocumentObject::getClassTypeId(), @@ -300,7 +301,7 @@ static void _copyShape(const char* cmdName, bool resolve, bool needElement = fal Gui::Command::copyVisual(newObj, "PointColor", v.second); } } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); Gui::Command::updateActive(); } diff --git a/src/Mod/Part/Gui/DlgExtrusion.cpp b/src/Mod/Part/Gui/DlgExtrusion.cpp index 8068c712ac..f85dd1983d 100644 --- a/src/Mod/Part/Gui/DlgExtrusion.cpp +++ b/src/Mod/Part/Gui/DlgExtrusion.cpp @@ -116,6 +116,7 @@ DlgExtrusion::DlgExtrusion(QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl) , ui(new Ui_DlgExtrusion) , filter(nullptr) + , filterSelection(false) { ui->setupUi(this); setupConnections(); @@ -154,6 +155,7 @@ DlgExtrusion::~DlgExtrusion() if (filter) { Gui::Selection().rmvSelectionGate(); filter = nullptr; + filterSelection = false; } // no need to delete child widgets, Qt does it all for us @@ -222,9 +224,9 @@ void DlgExtrusion::onDirModeNormalToggled(bool on) void DlgExtrusion::onSelectEdgeClicked() { - if (!filter) { - filter = new EdgeSelection(); - Gui::Selection().addSelectionGate(filter); + if (!filterSelection) { + filterSelection = true; + setSelectionGate(); ui->btnSelectEdge->setText(tr("Selecting…")); // visibility automation @@ -254,6 +256,7 @@ void DlgExtrusion::onSelectEdgeClicked() else { Gui::Selection().rmvSelectionGate(); filter = nullptr; + filterSelection = false; ui->btnSelectEdge->setText(tr("Select")); // visibility automation @@ -894,6 +897,13 @@ void DlgExtrusion::writeParametersToFeature(App::DocumentObject& feature, App::D ui->spinTaperAngleRev->value().getValue() ); } +void DlgExtrusion::setSelectionGate() +{ + if (filterSelection) { + filter = new EdgeSelection(); + Gui::Selection().addSelectionGate(filter); + } +} // --------------------------------------- @@ -926,5 +936,14 @@ void TaskExtrusion::clicked(int id) }; } } +void TaskExtrusion::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskExtrusion::deactivate() +{ + widget->detachSelection(); +} #include "moc_DlgExtrusion.cpp" diff --git a/src/Mod/Part/Gui/DlgExtrusion.h b/src/Mod/Part/Gui/DlgExtrusion.h index 1f98ca9360..775f5e80a7 100644 --- a/src/Mod/Part/Gui/DlgExtrusion.h +++ b/src/Mod/Part/Gui/DlgExtrusion.h @@ -65,6 +65,8 @@ public: void writeParametersToFeature(App::DocumentObject& feature, App::DocumentObject* base) const; + void setSelectionGate(); + protected: void findShapes(); bool canExtrude(const TopoDS_Shape&) const; @@ -101,6 +103,7 @@ private: std::string document, label; class EdgeSelection; EdgeSelection* filter; + bool filterSelection; }; class TaskExtrusion: public Gui::TaskView::TaskDialog @@ -114,6 +117,8 @@ public: bool accept() override; bool reject() override; void clicked(int) override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index c8a4c4f375..de35b0184b 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -264,8 +264,7 @@ DlgFilletEdges::DlgFilletEdges( ui->filletEndRadius->setUnit(Base::Unit::Length); d->object = nullptr; - d->selection = new EdgeFaceSelection(d->object); - Gui::Selection().addSelectionGate(d->selection); + setSelectionGate(); d->fillet = fillet; // NOLINTBEGIN @@ -1126,6 +1125,11 @@ bool DlgFilletEdges::accept() Gui::Command::copyVisual(to, "PointColor", from); return true; } +void DlgFilletEdges::setSelectionGate() +{ + d->selection = new EdgeFaceSelection(d->object); + Gui::Selection().addSelectionGate(d->selection); +} // --------------------------------------- @@ -1193,6 +1197,15 @@ bool TaskFilletEdges::reject() Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); return true; } +void TaskFilletEdges::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskFilletEdges::deactivate() +{ + widget->detachSelection(); +} // -------------------------------------------------------------- diff --git a/src/Mod/Part/Gui/DlgFilletEdges.h b/src/Mod/Part/Gui/DlgFilletEdges.h index 2b89294047..798d00ed37 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.h +++ b/src/Mod/Part/Gui/DlgFilletEdges.h @@ -106,6 +106,7 @@ public: ); ~DlgFilletEdges() override; bool accept(); + void setSelectionGate(); protected: void findShapes(); @@ -186,6 +187,8 @@ public: void clicked(int) override; bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp index 2ce7958a86..cb778d002d 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp @@ -136,8 +136,6 @@ DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget* parent) : QWidget(parent) , ui(new Ui::DlgProjectionOnSurface) , m_projectionObjectName(tr("Projection object")) - , filterEdge(nullptr) - , filterFace(nullptr) { ui->setupUi(this); setupConnections(); @@ -298,22 +296,31 @@ void PartGui::DlgProjectionOnSurface::reject() m_partDocument->abortTransaction(); } } +void PartGui::DlgProjectionOnSurface::setSelectionGate() +{ + if (selectionMode == SelectionMode::Face) { + Gui::Selection().addSelectionGate(new FaceSelection()); + } + else if (selectionMode == SelectionMode::Edge) { + Gui::Selection().addSelectionGate(new EdgeSelection()); + } +} void PartGui::DlgProjectionOnSurface::onPushButtonAddFaceClicked() { if (ui->pushButtonAddFace->isChecked()) { m_currentSelection = "add_face"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddFace); - if (!filterFace) { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (selectionMode != SelectionMode::Face) { + selectionMode = SelectionMode::Face; + setSelectionGate(); } } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; + selectionMode = SelectionMode::None; } } @@ -322,9 +329,9 @@ void PartGui::DlgProjectionOnSurface::onPushButtonAddEdgeClicked() if (ui->pushButtonAddEdge->isChecked()) { m_currentSelection = "add_edge"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddEdge); - if (!filterEdge) { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (selectionMode != SelectionMode::Edge) { + selectionMode = SelectionMode::Edge; + setSelectionGate(); } ui->radioButtonEdges->setChecked(true); onRadioButtonEdgesClicked(); @@ -333,7 +340,7 @@ void PartGui::DlgProjectionOnSurface::onPushButtonAddEdgeClicked() m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; + selectionMode = SelectionMode::None; } } @@ -1118,16 +1125,16 @@ void PartGui::DlgProjectionOnSurface::onPushButtonAddProjFaceClicked() if (ui->pushButtonAddProjFace->isChecked()) { m_currentSelection = "add_projection_surface"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); - if (!filterFace) { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (selectionMode != SelectionMode::Face) { + selectionMode = SelectionMode::Face; + setSelectionGate(); } } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; + selectionMode = SelectionMode::None; } } void PartGui::DlgProjectionOnSurface::onRadioButtonShowAllClicked() @@ -1160,9 +1167,9 @@ void PartGui::DlgProjectionOnSurface::onPushButtonAddWireClicked() if (ui->pushButtonAddWire->isChecked()) { m_currentSelection = "add_wire"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddWire); - if (!filterEdge) { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (selectionMode != SelectionMode::Edge) { + selectionMode = SelectionMode::Edge; + setSelectionGate(); } ui->radioButtonEdges->setChecked(true); onRadioButtonEdgesClicked(); @@ -1171,7 +1178,7 @@ void PartGui::DlgProjectionOnSurface::onPushButtonAddWireClicked() m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; + selectionMode = SelectionMode::None; } } @@ -1218,6 +1225,15 @@ bool TaskProjectionOnSurface::reject() widget->reject(); return true; } +void TaskProjectionOnSurface::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskProjectionOnSurface::deactivate() +{ + widget->detachSelection(); +} void TaskProjectionOnSurface::clicked(int id) { @@ -1235,8 +1251,6 @@ void TaskProjectionOnSurface::clicked(int id) DlgProjectOnSurface::DlgProjectOnSurface(Part::ProjectOnSurface* feature, QWidget* parent) : QWidget(parent) , ui(new Ui::DlgProjectionOnSurface) - , filterEdge(nullptr) - , filterFace(nullptr) , feature(feature) { ui->setupUi(this); @@ -1254,7 +1268,7 @@ DlgProjectOnSurface::DlgProjectOnSurface(Part::ProjectOnSurface* feature, QWidge DlgProjectOnSurface::~DlgProjectOnSurface() { - if (filterFace || filterEdge) { + if (selectionMode != SelectionMode::None) { Gui::Selection().rmvSelectionGate(); } } @@ -1331,42 +1345,37 @@ void DlgProjectOnSurface::reject() void DlgProjectOnSurface::onAddProjFaceClicked() { if (ui->pushButtonAddProjFace->isChecked()) { - selectionMode = SelectionMode::SupportFace; - if (!filterFace) { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (selectionMode != SelectionMode::SupportFace) { + selectionMode = SelectionMode::SupportFace; + setSelectionGate(); } } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; } } void DlgProjectOnSurface::onAddFaceClicked() { if (ui->pushButtonAddFace->isChecked()) { - selectionMode = SelectionMode::AddFace; - if (!filterFace) { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (selectionMode != SelectionMode::AddFace) { + selectionMode = SelectionMode::AddFace; + setSelectionGate(); } } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; } } void DlgProjectOnSurface::onAddWireClicked() { if (ui->pushButtonAddWire->isChecked()) { - selectionMode = SelectionMode::AddWire; - if (!filterEdge) { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (selectionMode != SelectionMode::AddWire) { + selectionMode = SelectionMode::AddWire; + setSelectionGate(); } ui->radioButtonEdges->setChecked(true); onEdgesClicked(); @@ -1374,25 +1383,32 @@ void DlgProjectOnSurface::onAddWireClicked() else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; } } void DlgProjectOnSurface::onAddEdgeClicked() { if (ui->pushButtonAddEdge->isChecked()) { - selectionMode = SelectionMode::AddEdge; - if (!filterEdge) { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (selectionMode != SelectionMode::AddEdge) { + selectionMode = SelectionMode::AddEdge; + setSelectionGate(); } + ui->radioButtonEdges->setChecked(true); onEdgesClicked(); } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; + } +} +void DlgProjectOnSurface::setSelectionGate() +{ + if (selectionMode == SelectionMode::SupportFace || selectionMode == SelectionMode::AddFace) { + Gui::Selection().addSelectionGate(new FaceSelection()); + } + else if (selectionMode == SelectionMode::AddEdge || selectionMode == SelectionMode::AddWire) { + Gui::Selection().addSelectionGate(new EdgeSelection()); } } @@ -1645,5 +1661,14 @@ bool TaskProjectOnSurface::reject() resetEdit(); return true; } +void TaskProjectOnSurface::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskProjectOnSurface::deactivate() +{ + widget->detachSelection(); +} #include "moc_DlgProjectionOnSurface.cpp" diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.h b/src/Mod/Part/Gui/DlgProjectionOnSurface.h index 0c87c6f69f..78a2c354d7 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.h +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.h @@ -57,6 +57,7 @@ public: void apply(); void reject(); + void setSelectionGate(); private: void setupConnections(); @@ -94,6 +95,12 @@ private: long transparency = 0; double extrudeValue = 0.0; }; + enum class SelectionMode + { + None, + Face, + Edge + }; // from Gui::SelectionObserver void onSelectionChanged(const Gui::SelectionChanges& msg) override; @@ -151,8 +158,7 @@ private: App::Document* m_partDocument = nullptr; double m_lastDepthVal; - Gui::SelectionFilterGate* filterEdge; - Gui::SelectionFilterGate* filterFace; + SelectionMode selectionMode; }; class TaskProjectionOnSurface: public Gui::TaskView::TaskDialog @@ -166,6 +172,8 @@ public: bool accept() override; bool reject() override; void clicked(int id) override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { @@ -189,6 +197,7 @@ public: void accept(); void reject(); + void setSelectionGate(); // from Gui::SelectionObserver void onSelectionChanged(const Gui::SelectionChanges& msg) override; @@ -226,8 +235,6 @@ private: private: std::unique_ptr ui; - Gui::SelectionFilterGate* filterEdge; - Gui::SelectionFilterGate* filterFace; App::WeakPtrT feature; SelectionMode selectionMode = SelectionMode::None; }; @@ -243,6 +250,8 @@ public: public: bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgRevolution.cpp b/src/Mod/Part/Gui/DlgRevolution.cpp index 3e22e7e99a..722716fa4f 100644 --- a/src/Mod/Part/Gui/DlgRevolution.cpp +++ b/src/Mod/Part/Gui/DlgRevolution.cpp @@ -107,6 +107,7 @@ DlgRevolution::DlgRevolution(QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl) , ui(new Ui_DlgRevolution) , filter(nullptr) + , filterSelection(false) { ui->setupUi(this); setupConnections(); @@ -527,17 +528,25 @@ void DlgRevolution::accept() QDialog::accept(); } +void DlgRevolution::setSelectionGate() +{ + if (filterSelection) { + filter = new EdgeSelection(); + Gui::Selection().addSelectionGate(filter); + } +} void DlgRevolution::onSelectLineClicked() { - if (!filter) { - filter = new EdgeSelection(); - Gui::Selection().addSelectionGate(filter); + if (!filterSelection) { + filterSelection = true; + setSelectionGate(); ui->selectLine->setText(tr("Selecting… (Line or Arc)")); } else { Gui::Selection().rmvSelectionGate(); filter = nullptr; + filterSelection = false; ui->selectLine->setText(tr("Select Reference")); } } @@ -668,5 +677,14 @@ bool TaskRevolution::accept() widget->accept(); return (widget->result() == QDialog::Accepted); } +void TaskRevolution::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskRevolution::deactivate() +{ + widget->detachSelection(); +} #include "moc_DlgRevolution.cpp" diff --git a/src/Mod/Part/Gui/DlgRevolution.h b/src/Mod/Part/Gui/DlgRevolution.h index 989adb10f4..236e9a2b5e 100644 --- a/src/Mod/Part/Gui/DlgRevolution.h +++ b/src/Mod/Part/Gui/DlgRevolution.h @@ -42,6 +42,7 @@ public: explicit DlgRevolution(QWidget* parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags()); ~DlgRevolution() override; void accept() override; + void setSelectionGate(); Base::Vector3d getDirection() const; Base::Vector3d getPosition() const; @@ -83,6 +84,7 @@ private: std::unique_ptr ui; class EdgeSelection; EdgeSelection* filter; + bool filterSelection; }; class TaskRevolution: public Gui::TaskView::TaskDialog @@ -94,6 +96,8 @@ public: public: bool accept() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/Mirroring.cpp b/src/Mod/Part/Gui/Mirroring.cpp index 9f87023fc4..7476465bbb 100644 --- a/src/Mod/Part/Gui/Mirroring.cpp +++ b/src/Mod/Part/Gui/Mirroring.cpp @@ -201,6 +201,7 @@ public: Mirroring::Mirroring(QWidget* parent) : QWidget(parent) , ui(new Ui_Mirroring) + , filterSelection(true) { ui->setupUi(this); constexpr double max = std::numeric_limits::max(); @@ -219,8 +220,7 @@ Mirroring::Mirroring(QWidget* parent) connect(ui->selectButton, &QPushButton::clicked, this, &Mirroring::onSelectButtonClicked); - MirrorPlaneSelection* gate = new MirrorPlaneSelection(); - Gui::Selection().addSelectionGate(gate); + setSelectionGate(); } /* @@ -232,11 +232,12 @@ void Mirroring::onSelectButtonClicked() { if (!ui->selectButton->isChecked()) { Gui::Selection().rmvSelectionGate(); + filterSelection = false; ui->selectButton->setText(tr("Select Reference")); } else { - MirrorPlaneSelection* gate = new MirrorPlaneSelection(); - Gui::Selection().addSelectionGate(gate); + filterSelection = true; + setSelectionGate(); ui->selectButton->setText(tr("Selecting")); } } @@ -302,6 +303,7 @@ void Mirroring::findShapes() bool Mirroring::reject() { Gui::Selection().rmvSelectionGate(); + filterSelection = false; return true; } @@ -391,8 +393,16 @@ bool Mirroring::accept() activeDoc->commitTransaction(); activeDoc->recompute(); Gui::Selection().rmvSelectionGate(); + filterSelection = false; return true; } +void Mirroring::setSelectionGate() +{ + if (filterSelection) { + MirrorPlaneSelection* gate = new MirrorPlaneSelection(); + Gui::Selection().addSelectionGate(gate); + } +} // --------------------------------------- @@ -411,5 +421,14 @@ bool TaskMirroring::reject() { return widget->reject(); } +void TaskMirroring::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskMirroring::deactivate() +{ + widget->detachSelection(); +} #include "moc_Mirroring.cpp" diff --git a/src/Mod/Part/Gui/Mirroring.h b/src/Mod/Part/Gui/Mirroring.h index e9a6cd438d..402eb43a51 100644 --- a/src/Mod/Part/Gui/Mirroring.h +++ b/src/Mod/Part/Gui/Mirroring.h @@ -47,6 +47,7 @@ public: ~Mirroring() override; bool accept(); bool reject(); + void setSelectionGate(); protected: void changeEvent(QEvent* e) override; @@ -59,6 +60,7 @@ private: void onSelectionChanged(const Gui::SelectionChanges& msg) override; QString document; std::unique_ptr ui; + bool filterSelection; }; class TaskMirroring: public Gui::TaskView::TaskDialog @@ -71,6 +73,8 @@ public: public: bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/ShapeFromMesh.cpp b/src/Mod/Part/Gui/ShapeFromMesh.cpp index 79df2e6aab..554e6a50a4 100644 --- a/src/Mod/Part/Gui/ShapeFromMesh.cpp +++ b/src/Mod/Part/Gui/ShapeFromMesh.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -71,7 +72,9 @@ void ShapeFromMesh::perform() meshes = Gui::Selection().getObjectsOfType(meshid); Gui::doCommandT(Gui::Command::Doc, "import Part"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Convert mesh")); + Gui::Application::Instance->activeDocument()->openCommand( + QT_TRANSLATE_NOOP("Command", "Convert mesh") + ); for (auto it : meshes) { App::Document* doc = it->getDocument(); @@ -95,7 +98,7 @@ void ShapeFromMesh::perform() Gui::doCommandT(Gui::Command::Doc, "del __shape__"); } - Gui::Command::commitCommand(); + Gui::Application::Instance->activeDocument()->commitCommand(); } void ShapeFromMesh::accept() diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index 3f538015ae..bf044c94f9 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -1376,7 +1376,9 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) return; } - auto editDoc = Gui::Application::Instance->editDocument(); + Gui::Document* editDoc = Gui::Application::Instance->isInEdit(ViewProvider->getDocument()) + ? ViewProvider->getDocument() + : nullptr; App::DocumentObject* editObj = ViewProvider->getObject(); std::string editSubName; auto sels = Gui::Selection().getSelection(nullptr, Gui::ResolveMode::NoResolve, true); @@ -1435,6 +1437,7 @@ TaskDlgAttacher::TaskDlgAttacher( , onAccept(onAccept) , onReject(onReject) , accepted(false) + , tid(0) { assert(ViewProvider); setDocumentName(ViewProvider->getDocument()->getDocument()->getName()); @@ -1458,7 +1461,7 @@ TaskDlgAttacher::~TaskDlgAttacher() void TaskDlgAttacher::open() { if (!Gui::Command::hasPendingCommand()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit attachment")); + tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit attachment")); } } @@ -1517,7 +1520,7 @@ bool TaskDlgAttacher::accept() ); Gui::cmdAppObject(obj, "recompute()"); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } catch (const Base::Exception& e) { QMessageBox::warning( @@ -1543,7 +1546,7 @@ bool TaskDlgAttacher::reject() Gui::Document* document = doc.getDocument(); if (document) { // roll back the done things - Gui::Command::abortCommand(); + Gui::Command::abortCommand(tid); Gui::Command::doCommand(Gui::Command::Doc, "%s.recompute()", doc.getAppDocumentPython().c_str()); } @@ -1551,5 +1554,17 @@ bool TaskDlgAttacher::reject() return true; } +void TaskDlgAttacher::activate() +{ + if (parameter) { + parameter->attachSelection(); + } +} +void TaskDlgAttacher::deactivate() +{ + if (parameter) { + parameter->detachSelection(); + } +} #include "moc_TaskAttacher.cpp" diff --git a/src/Mod/Part/Gui/TaskAttacher.h b/src/Mod/Part/Gui/TaskAttacher.h index af34b84101..09608ff218 100644 --- a/src/Mod/Part/Gui/TaskAttacher.h +++ b/src/Mod/Part/Gui/TaskAttacher.h @@ -214,7 +214,10 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - /// is called by the framework if the user presses the help button + + void activate() override; + void deactivate() override; + bool isAllowedAlterDocument() const override { return false; @@ -234,6 +237,7 @@ protected: std::function onAccept; std::function onReject; bool accepted; + int tid; }; } // namespace PartGui diff --git a/src/Mod/Part/Gui/TaskFaceAppearances.cpp b/src/Mod/Part/Gui/TaskFaceAppearances.cpp index 4f43ff8162..72e6859c1b 100644 --- a/src/Mod/Part/Gui/TaskFaceAppearances.cpp +++ b/src/Mod/Part/Gui/TaskFaceAppearances.cpp @@ -265,8 +265,7 @@ FaceAppearances::FaceAppearances(ViewProviderPartExt* vp, QWidget* parent) d->ui->groupBox->setTitle(QString::fromUtf8(vp->getObject()->Label.getValue())); d->ui->buttonCustomAppearance->setDisabled(true); - FaceSelection* gate = new FaceSelection(d->vp->getObject()); - Gui::Selection().addSelectionGate(gate); + setSelectionGate(); // NOLINTBEGIN d->connectDelDoc = Gui::Application::Instance->signalDeleteDocument.connect( @@ -317,21 +316,21 @@ void FaceAppearances::slotUndoDocument(const Gui::Document& Doc) { if (d->doc == &Doc) { d->doc->resetEdit(); - Gui::Control().closeDialog(); + Gui::Control().closeDialog(d->doc->getDocument()); } } void FaceAppearances::slotDeleteDocument(const Gui::Document& Doc) { if (d->doc == &Doc) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(d->doc->getDocument()); } } void FaceAppearances::slotDeleteObject(const Gui::ViewProvider& obj) { if (d->vp == &obj) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(d->doc->getDocument()); } } @@ -512,6 +511,11 @@ void FaceAppearances::changeEvent(QEvent* e) d->ui->retranslateUi(this); } } +void FaceAppearances::setSelectionGate() +{ + FaceSelection* gate = new FaceSelection(d->vp->getObject()); + Gui::Selection().addSelectionGate(gate); +} /* TRANSLATOR PartGui::TaskFaceAppearances */ @@ -541,5 +545,14 @@ bool TaskFaceAppearances::reject() { return widget->reject(); } +void TaskFaceAppearances::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskFaceAppearances::deactivate() +{ + widget->detachSelection(); +} #include "moc_TaskFaceAppearances.cpp" diff --git a/src/Mod/Part/Gui/TaskFaceAppearances.h b/src/Mod/Part/Gui/TaskFaceAppearances.h index 8c9ffc9434..7e9a1b1a34 100644 --- a/src/Mod/Part/Gui/TaskFaceAppearances.h +++ b/src/Mod/Part/Gui/TaskFaceAppearances.h @@ -54,6 +54,7 @@ public: void open(); bool accept(); bool reject(); + void setSelectionGate(); private: void setupConnections(); @@ -89,6 +90,8 @@ public: bool accept() override; bool reject() override; void clicked(int) override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/TaskOffset.cpp b/src/Mod/Part/Gui/TaskOffset.cpp index 39d4ba96ea..1fc39e87f8 100644 --- a/src/Mod/Part/Gui/TaskOffset.cpp +++ b/src/Mod/Part/Gui/TaskOffset.cpp @@ -217,10 +217,12 @@ bool OffsetWidget::accept() if (!d->offset->isValid()) { throw Base::CADKernelError(d->offset->getStatusString()); } + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); - Gui::Command::commitCommand(); + d->offset->getDocument()->commitTransaction(); // ViewProviderDocumentObject::startDefaultEditMode() } catch (const Base::Exception& e) { + d->offset->getDocument()->abortTransaction(); // ViewProviderDocumentObject::startDefaultEditMode() QMessageBox::warning( this, tr("Input error"), @@ -241,7 +243,7 @@ bool OffsetWidget::reject() } // roll back the done things - Gui::Command::abortCommand(); + d->offset->getDocument()->abortTransaction(); // ViewProviderDocumentObject::startDefaultEditMode() Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); diff --git a/src/Mod/Part/Gui/TaskShapeBuilder.cpp b/src/Mod/Part/Gui/TaskShapeBuilder.cpp index 3284c50bd8..bfccef4114 100644 --- a/src/Mod/Part/Gui/TaskShapeBuilder.cpp +++ b/src/Mod/Part/Gui/TaskShapeBuilder.cpp @@ -130,8 +130,7 @@ ShapeBuilderWidget::ShapeBuilderWidget(QWidget* parent) connect(d->ui.createButton, &QPushButton::clicked, this, &ShapeBuilderWidget::onCreateButtonClicked); connect(&d->bg, &QButtonGroup::idClicked, this, &ShapeBuilderWidget::switchMode); - d->gate = new ShapeSelection(); - Gui::Selection().addSelectionGate(d->gate); + setSelectionGate(); d->bg.button(0)->setChecked(true); switchMode(0); @@ -605,6 +604,11 @@ bool ShapeBuilderWidget::reject() { return true; } +void ShapeBuilderWidget::setSelectionGate() +{ + d->gate = new ShapeSelection(); + Gui::Selection().addSelectionGate(d->gate); +} void ShapeBuilderWidget::changeEvent(QEvent* e) { @@ -640,5 +644,14 @@ bool TaskShapeBuilder::reject() { return widget->reject(); } +void TaskShapeBuilder::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskShapeBuilder::deactivate() +{ + widget->detachSelection(); +} #include "moc_TaskShapeBuilder.cpp" diff --git a/src/Mod/Part/Gui/TaskShapeBuilder.h b/src/Mod/Part/Gui/TaskShapeBuilder.h index a4977611cd..8ceab390cf 100644 --- a/src/Mod/Part/Gui/TaskShapeBuilder.h +++ b/src/Mod/Part/Gui/TaskShapeBuilder.h @@ -42,6 +42,7 @@ public: bool accept(); bool reject(); + void setSelectionGate(); private: void onCreateButtonClicked(); @@ -78,6 +79,8 @@ public: bool accept() override; bool reject() override; void clicked(int) override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/TaskThickness.cpp b/src/Mod/Part/Gui/TaskThickness.cpp index c6df2bfdde..133fe9036e 100644 --- a/src/Mod/Part/Gui/TaskThickness.cpp +++ b/src/Mod/Part/Gui/TaskThickness.cpp @@ -287,9 +287,11 @@ bool ThicknessWidget::accept() throw Base::CADKernelError(d->thickness->getStatusString()); } Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); - Gui::Command::commitCommand(); + d->thickness->getDocument()->commitTransaction(); // Opened in + // ViewProviderDocumentObject::startDefaultEditMode() } catch (const Base::Exception& e) { + d->thickness->getDocument()->abortTransaction(); // ViewProviderDocumentObject::startDefaultEditMode() QMessageBox::warning( this, tr("Input error"), @@ -313,7 +315,7 @@ bool ThicknessWidget::reject() App::DocumentObject* source = d->thickness->Faces.getValue(); // roll back the done things - Gui::Command::abortCommand(); + d->thickness->getDocument()->abortTransaction(); // ViewProviderDocumentObject::startDefaultEditMode() Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); diff --git a/src/Mod/Part/Gui/ViewProvider.cpp b/src/Mod/Part/Gui/ViewProvider.cpp index b04745eba0..35665234b3 100644 --- a/src/Mod/Part/Gui/ViewProvider.cpp +++ b/src/Mod/Part/Gui/ViewProvider.cpp @@ -51,7 +51,7 @@ bool ViewProviderPart::doubleClicked() { try { QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); - Gui::Command::openCommand(text.toUtf8()); + Gui::Command::openActiveDocumentCommand(text.toStdString()); Gui::cmdSetEdit(pcObject, Gui::Application::Instance->getUserEditMode()); return true; } diff --git a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp index 18e3f5109e..4c9c56ed95 100644 --- a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp +++ b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "ViewProviderAttachExtension.h" @@ -110,12 +111,11 @@ void ViewProviderAttachExtension::showAttachmentEditor( std::function onReject ) { - if (Gui::Control().activeDialog()) { - Gui::Control().closeDialog(); + if (Gui::Control().activeDialog(getExtendedViewProvider()->getDocument()->getDocument())) { + Gui::Control().closeDialog(getExtendedViewProvider()->getDocument()->getDocument()); } - - TaskDlgAttacher* task = new TaskDlgAttacher(getExtendedViewProvider(), true, onAccept, onReject); - Gui::Control().showDialog(task); + auto* task = new TaskDlgAttacher(getExtendedViewProvider(), true, onAccept, onReject); + Gui::Control().showDialog(task, getExtendedViewProvider()->getDocument()->getDocument()); } namespace Gui diff --git a/src/Mod/Part/Gui/ViewProviderGridExtension.cpp b/src/Mod/Part/Gui/ViewProviderGridExtension.cpp index 0f2f874949..190b178d61 100644 --- a/src/Mod/Part/Gui/ViewProviderGridExtension.cpp +++ b/src/Mod/Part/Gui/ViewProviderGridExtension.cpp @@ -72,7 +72,7 @@ public: void drawGrid(bool cameraUpdate = false); - void setEnabled(bool enable); + void setEnabled(Gui::View3DInventor* view_); bool getEnabled(); SoSeparator* getGridRoot(); @@ -120,7 +120,7 @@ private: private: ViewProviderGridExtension* vp; - bool enabled = false; + Gui::View3DInventor* view {nullptr}; double computedGridValue = 10; bool isTooManySegmentsNotified = false; @@ -244,10 +244,6 @@ void GridExtensionP::computeGridSize(const Gui::View3DInventorViewer* viewer) void GridExtensionP::createGrid(bool cameraUpdate) { - auto view = dynamic_cast( - Gui::Application::Instance->editDocument()->getActiveView() - ); - if (!view) { return; } @@ -427,16 +423,16 @@ Base::Vector3d GridExtensionP::getCamCenterInSketchCoordinates() const return center; } -void GridExtensionP::setEnabled(bool enable) +void GridExtensionP::setEnabled(Gui::View3DInventor* view_) { - enabled = enable; + view = view_; drawGrid(); } bool GridExtensionP::getEnabled() { - return enabled; + return view != nullptr; } void GridExtensionP::createEditModeInventorNodes() @@ -454,7 +450,7 @@ SoSeparator* GridExtensionP::getGridRoot() void GridExtensionP::drawGrid(bool cameraUpdate) { - if (vp->ShowGrid.getValue() && enabled) { + if (vp->ShowGrid.getValue() && getEnabled()) { createGrid(cameraUpdate); } else { @@ -496,9 +492,9 @@ ViewProviderGridExtension::ViewProviderGridExtension() ViewProviderGridExtension::~ViewProviderGridExtension() = default; -void ViewProviderGridExtension::setGridEnabled(bool enable) +void ViewProviderGridExtension::setGridEnabled(Gui::View3DInventor* view) { - pImpl->setEnabled(enable); + pImpl->setEnabled(view); } void ViewProviderGridExtension::drawGrid(bool cameraUpdate) diff --git a/src/Mod/Part/Gui/ViewProviderGridExtension.h b/src/Mod/Part/Gui/ViewProviderGridExtension.h index 3cabd5cc47..cacd834eaa 100644 --- a/src/Mod/Part/Gui/ViewProviderGridExtension.h +++ b/src/Mod/Part/Gui/ViewProviderGridExtension.h @@ -52,7 +52,7 @@ public: ViewProviderGridExtension(); ~ViewProviderGridExtension() override; - void setGridEnabled(bool enable); + void setGridEnabled(Gui::View3DInventor* view); void drawGrid(bool cameraUpdate); diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index 65ef640143..47c638c67b 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -326,7 +326,7 @@ bool ViewProviderFillet::setEdit(int ModNum) void ViewProviderFillet::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { ViewProviderPart::unsetEdit(ModNum); @@ -447,7 +447,7 @@ bool ViewProviderChamfer::setEdit(int ModNum) void ViewProviderChamfer::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { ViewProviderPart::unsetEdit(ModNum); @@ -570,14 +570,14 @@ void ViewProviderOffset::setupContextMenu(QMenu* menu, QObject* receiver, const bool ViewProviderOffset::setEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(getDocument()->getDocument()); TaskOffset* offsetDlg = qobject_cast(dlg); if (offsetDlg && offsetDlg->getObject() != this->getObject()) { offsetDlg = nullptr; // another pad left open its task panel } if (dlg && !offsetDlg) { if (dlg->canClose()) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { return false; @@ -589,10 +589,13 @@ bool ViewProviderOffset::setEdit(int ModNum) // start the edit dialog if (offsetDlg) { - Gui::Control().showDialog(offsetDlg); + Gui::Control().showDialog(offsetDlg, getDocument()->getDocument()); } else { - Gui::Control().showDialog(new TaskOffset(getObject())); + Gui::Control().showDialog( + new TaskOffset(getObject()), + getDocument()->getDocument() + ); } return true; @@ -606,7 +609,7 @@ void ViewProviderOffset::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog - Gui::Control().closeDialog(); + Gui::Control().closeDialog(nullptr); } else { PartGui::ViewProviderPart::unsetEdit(ModNum); @@ -657,14 +660,14 @@ void ViewProviderThickness::setupContextMenu(QMenu* menu, QObject* receiver, con bool ViewProviderThickness::setEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(getDocument()->getDocument()); TaskThickness* thicknessDlg = qobject_cast(dlg); if (thicknessDlg && thicknessDlg->getObject() != this->getObject()) { thicknessDlg = nullptr; // another pad left open its task panel } if (dlg && !thicknessDlg) { if (dlg->canClose()) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { return false; @@ -676,24 +679,28 @@ bool ViewProviderThickness::setEdit(int ModNum) // start the edit dialog if (thicknessDlg) { - Gui::Control().showDialog(thicknessDlg); + Gui::Control().showDialog(thicknessDlg, getDocument()->getDocument()); } else { - Gui::Control().showDialog(new TaskThickness(getObject())); + Gui::Control().showDialog( + new TaskThickness(getObject()), + getDocument()->getDocument() + ); } return true; } - else { - return ViewProviderPart::setEdit(ModNum); - } + + return ViewProviderPart::setEdit(ModNum); } void ViewProviderThickness::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog - QTimer::singleShot(0, &Gui::Control(), &Gui::ControlSingleton::closeDialog); + QTimer::singleShot(100, [this]() { + Gui::Control().closeDialog(getDocument()->getDocument()); + }); } else { PartGui::ViewProviderPart::unsetEdit(ModNum); diff --git a/src/Mod/Part/Gui/ViewProviderPrimitive.cpp b/src/Mod/Part/Gui/ViewProviderPrimitive.cpp index e2e7ec26a2..c6b1a2b3fb 100644 --- a/src/Mod/Part/Gui/ViewProviderPrimitive.cpp +++ b/src/Mod/Part/Gui/ViewProviderPrimitive.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "ViewProviderPrimitive.h" @@ -80,7 +81,7 @@ bool ViewProviderPrimitive::setEdit(int ModNum) void ViewProviderPrimitive::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { ViewProviderPart::unsetEdit(ModNum); diff --git a/src/Mod/Part/Gui/ViewProviderProjectOnSurface.cpp b/src/Mod/Part/Gui/ViewProviderProjectOnSurface.cpp index 1da0eccb0a..ef7173733b 100644 --- a/src/Mod/Part/Gui/ViewProviderProjectOnSurface.cpp +++ b/src/Mod/Part/Gui/ViewProviderProjectOnSurface.cpp @@ -29,6 +29,7 @@ #include "ViewProviderProjectOnSurface.h" #include "DlgProjectionOnSurface.h" #include +#include using namespace PartGui; @@ -76,7 +77,7 @@ bool ViewProviderProjectOnSurface::setEdit(int ModNum) void ViewProviderProjectOnSurface::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { - Gui::Control().closeDialog(); + Gui::Control().closeDialog(getDocument()->getDocument()); } else { ViewProviderPart::unsetEdit(ModNum); diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index ed69402c6d..8c377d6694 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -105,8 +105,9 @@ void UnifiedDatumCommand(Gui::Command& cmd, Base::Type type, std::string name) PartDesign::Body* pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */ true); if (bEditSelected) { - std::string tmp = std::string("Edit ") + name; - cmd.openCommand(tmp.c_str()); + pcActiveBody->getDocument()->openTransaction( + std::string(std::string("Edit ") + name).c_str() + ); // Will be closed in the edit dialog accept/reject PartDesignGui::setEdit(support.getValue(), pcActiveBody); } else if (pcActiveBody) { @@ -114,9 +115,9 @@ void UnifiedDatumCommand(Gui::Command& cmd, Base::Type type, std::string name) // TODO Check how this will work outside of a body (2015-10-20, Fat-Zer) std::string FeatName = cmd.getUniqueObjectName(name.c_str(), pcActiveBody); - std::string tmp = std::string("Create ") + name; - - cmd.openCommand(tmp.c_str()); + pcActiveBody->getDocument()->openTransaction( + std::string(std::string("Create ") + name).c_str() + ); // Will be closed in the edit dialog accept/reject FCMD_OBJ_CMD(pcActiveBody, "newObject('" << fullTypeName << "','" << FeatName << "')"); // remove the body from links in case it's selected as @@ -502,6 +503,8 @@ void CmdPartDesignClone::activated(int iMsg) std::vector objs = getSelection().getObjectsOfType( Part::Feature::getClassTypeId() ); + + App::DocumentObject* obj = nullptr; if (objs.size() == 1) { // As suggested in https://forum.freecad.org/viewtopic.php?f=3&t=25265&p=198547#p207336 // put the clone into its own new body. @@ -554,6 +557,45 @@ void CmdPartDesignClone::activated(int iMsg) copyVisual(cloneObj, "DisplayMode", obj); commitCommand(); } + + // As suggested in https://forum.freecad.org/viewtopic.php?f=3&t=25265&p=198547#p207336 + // put the clone into its own new body. + // This also fixes bug #3447 because the clone is a PD feature and thus + // requires a body where it is part of. + + openCommand(QT_TRANSLATE_NOOP("Command", "Create Clone")); + + auto objCmd = getObjectCmd(obj); + std::string cloneName = getUniqueObjectName("Clone", obj); + std::string bodyName = getUniqueObjectName("Body", obj); + + // Create body and clone + Gui::cmdAppDocument(obj, std::stringstream() << "addObject('PartDesign::Body','" << bodyName << "')"); + Gui::cmdAppDocument( + obj, + std::stringstream() << "addObject('PartDesign::FeatureBase','" << cloneName << "')" + ); + + auto bodyObj = obj->getDocument()->getObject(bodyName.c_str()); + auto cloneObj = obj->getDocument()->getObject(cloneName.c_str()); + + // In the first step set the group link and tip of the body + Gui::cmdAppObject(bodyObj, std::stringstream() << "Group = [" << getObjectCmd(cloneObj) << "]"); + Gui::cmdAppObject(bodyObj, std::stringstream() << "Tip = " << getObjectCmd(cloneObj)); + + // In the second step set the link of the base feature + Gui::cmdAppObject(cloneObj, std::stringstream() << "BaseFeature = " << objCmd); + Gui::cmdAppObject(cloneObj, std::stringstream() << "Placement = " << objCmd << ".Placement"); + Gui::cmdAppObject(cloneObj, std::stringstream() << "setEditorMode('Placement', 0)"); + + updateActive(); + copyVisual(cloneObj, "ShapeAppearance", obj); + copyVisual(cloneObj, "LineColor", obj); + copyVisual(cloneObj, "PointColor", obj); + copyVisual(cloneObj, "Transparency", obj); + copyVisual(cloneObj, "DisplayMode", obj); + + commitCommand(); } bool CmdPartDesignClone::isActive() @@ -872,7 +914,7 @@ void prepareProfileBased( std::string FeatName = cmd->getUniqueObjectName(which.c_str(), pcActiveBody); - Gui::Command::openCommand((std::string("Make ") + which).c_str()); + cmd->openCommand(std::string("Make ") + which); FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::" << which << "','" << FeatName << "')"); auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str()); @@ -1109,7 +1151,7 @@ void prepareProfileBased( } if (!dlg.radioXRef->isChecked()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make Copy")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Make Copy")); auto copy = PartDesignGui::TaskFeaturePick::makeCopy( sketches[0], "", @@ -1160,7 +1202,7 @@ void prepareProfileBased( // if (!bNoSketchWasSelected && extReference) // pickDlg->showExternal(true); - Gui::Control().showDialog(pickDlg); + Gui::Control().showDialog(pickDlg, cmd->getDocument()); } else { std::vector theSketch; @@ -1884,7 +1926,7 @@ bool dressupGetSelected( } void finishDressupFeature( - const Gui::Command* cmd, + Gui::Command* cmd, const std::string& which, Part::Feature* base, const std::vector& SubNames, @@ -1904,14 +1946,14 @@ void finishDressupFeature( if (!body) { return; } - cmd->openCommand((std::string("Make ") + which).c_str()); + cmd->openCommand(std::string("Make ") + which); FCMD_OBJ_CMD(body, "newObject('PartDesign::" << which << "','" << FeatName << "')"); auto Feat = body->getDocument()->getObject(FeatName.c_str()); FCMD_OBJ_CMD(Feat, "Base = " << str.str()); if (useAllEdges && (which.compare("Fillet") == 0 || which.compare("Chamfer") == 0)) { FCMD_OBJ_CMD(Feat, "UseAllEdges = True"); } - cmd->doCommand(cmd->Gui, "Gui.Selection.clearSelection()"); + Gui::Command::doCommand(cmd->Gui, "Gui.Selection.clearSelection()"); finishFeature(cmd, Feat, base); App::DocumentObject* baseFeature = static_cast(Feat)->Base.getValue(); @@ -2150,7 +2192,7 @@ void prepareTransformed( auto worker = [=](std::vector features) { std::string msg("Make "); msg += which; - Gui::Command::openCommand(msg.c_str()); + cmd->openCommand(msg.c_str()); FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::" << which << "','" << FeatName << "')"); // FIXME: There seems to be kind of a race condition here, leading to sporadic errors like // Exception (Thu Sep 6 11:52:01 2012): 'App.Document' object has no attribute 'Mirrored' diff --git a/src/Mod/PartDesign/Gui/CommandBody.cpp b/src/Mod/PartDesign/Gui/CommandBody.cpp index eefcb5aff1..13b0e66d7a 100644 --- a/src/Mod/PartDesign/Gui/CommandBody.cpp +++ b/src/Mod/PartDesign/Gui/CommandBody.cpp @@ -222,6 +222,7 @@ void CmdPartDesignBody::activated(int iMsg) } openCommand(QT_TRANSLATE_NOOP("Command", "Add a Body")); + bool openedModal = false; std::string bodyName = getUniqueObjectName("Body"); const char* bodyString = bodyName.c_str(); @@ -367,6 +368,10 @@ void CmdPartDesignBody::activated(int iMsg) } updateActive(); + + if (!openedModal) { + commitCommand(); + } } bool CmdPartDesignBody::isActive() @@ -521,9 +526,7 @@ void CmdPartDesignMigrate::activated(int iMsg) } // do the actual migration - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Migrate legacy Part Design features to bodies") - ); + openCommand(QT_TRANSLATE_NOOP("Command", "Migrate legacy Part Design features to bodies")); for (auto chainIt = featureChains.begin(); !featureChains.empty(); featureChains.erase(chainIt), chainIt = featureChains.begin()) { @@ -803,6 +806,8 @@ void CmdPartDesignDuplicateSelection::activated(int iMsg) } updateActive(); + + commitCommand(); } bool CmdPartDesignDuplicateSelection::isActive() @@ -1002,6 +1007,8 @@ void CmdPartDesignMoveFeature::activated(int iMsg) }*/ updateActive(); + + commitCommand(); } bool CmdPartDesignMoveFeature::isActive() @@ -1199,6 +1206,8 @@ void CmdPartDesignMoveFeatureInTree::activated(int iMsg) } updateActive(); + + commitCommand(); } bool CmdPartDesignMoveFeatureInTree::isActive() diff --git a/src/Mod/PartDesign/Gui/CommandPrimitive.cpp b/src/Mod/PartDesign/Gui/CommandPrimitive.cpp index 249bf206aa..10b12a80fe 100644 --- a/src/Mod/PartDesign/Gui/CommandPrimitive.cpp +++ b/src/Mod/PartDesign/Gui/CommandPrimitive.cpp @@ -109,9 +109,9 @@ void CmdPrimtiveCompAdditive::activated(int iMsg) Gui::ActionGroup* pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); - auto shapeType(primitiveIntToName(iMsg)); + std::string shapeType(primitiveIntToName(iMsg)); - Gui::Command::openCommand((std::string("Make additive ") + shapeType).c_str()); + openCommand("Make additive " + shapeType); if (shouldMakeBody) { pcActiveBody = PartDesignGui::makeBody(doc); } @@ -120,7 +120,7 @@ void CmdPrimtiveCompAdditive::activated(int iMsg) return; } - auto FeatName(getUniqueObjectName(shapeType, pcActiveBody)); + auto FeatName(getUniqueObjectName(shapeType.c_str(), pcActiveBody)); FCMD_OBJ_DOC_CMD( pcActiveBody, @@ -315,7 +315,7 @@ void CmdPrimtiveCompSubtractive::activated(int iMsg) auto shapeType(primitiveIntToName(iMsg)); auto FeatName(getUniqueObjectName(shapeType, pcActiveBody)); - Gui::Command::openCommand((std::string("Make subtractive ") + shapeType).c_str()); + openCommand(std::string("Make subtractive ") + shapeType); FCMD_OBJ_CMD( pcActiveBody, "newObject('PartDesign::Subtractive" << shapeType << "','" << FeatName << "')" diff --git a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp index 636d9433ba..119ae82383 100644 --- a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp +++ b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp @@ -100,9 +100,9 @@ void DlgActiveBody::accept() } else { // A transaction must be created as otherwise the undo/redo is broken - App::GetApplication().setActiveTransaction(QT_TRANSLATE_NOOP("Command", "Add a Body"), true); + _doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Add a Body")); activeBody = makeBody(_doc); - App::GetApplication().closeActiveTransaction(); + _doc->commitTransaction(); } QDialog::accept(); diff --git a/src/Mod/PartDesign/Gui/SketchWorkflow.cpp b/src/Mod/PartDesign/Gui/SketchWorkflow.cpp index a2cc2ef289..7f0d5daa0d 100644 --- a/src/Mod/PartDesign/Gui/SketchWorkflow.cpp +++ b/src/Mod/PartDesign/Gui/SketchWorkflow.cpp @@ -722,7 +722,8 @@ private: void checkForShownDialog() { - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + App::Document* appdocument = guidocument->getDocument(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(appdocument); PartDesignGui::TaskDlgFeaturePick* pickDlg = qobject_cast(dlg); if (dlg && !pickDlg) { diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp index a813c2a674..d2bbd86654 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp @@ -394,8 +394,14 @@ bool TaskDlgBooleanParameters::accept() } str << "])"; Gui::Command::runCommand(Gui::Command::Doc, str.str().c_str()); + FCMD_OBJ_CMD(obj, "Type = " << parameter->getType()); + + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); + obj->getDocument()->commitTransaction(); } catch (const Base::Exception& e) { + obj->getDocument()->abortTransaction(); QMessageBox::warning( parameter, tr("Boolean: Accept: Input error"), @@ -403,12 +409,6 @@ bool TaskDlgBooleanParameters::accept() ); return false; } - - FCMD_OBJ_CMD(obj, "Type = " << parameter->getType()); - Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); - Gui::Command::commitCommand(); - return true; } @@ -425,15 +425,21 @@ bool TaskDlgBooleanParameters::reject() doc->setShow(body->getNameInDocument()); } } + // roll back the done things + doc->abortCommand(); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); } - // roll back the done things - Gui::Command::abortCommand(); - Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); - - return true; } +void TaskDlgBooleanParameters::activate() +{ + parameter->attachSelection(); +} +void TaskDlgBooleanParameters::deactivate() +{ + parameter->detachSelection(); +} #include "moc_TaskBooleanParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h index 12d66c4a51..f3c65083fc 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h @@ -112,7 +112,9 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - /// is called by the framework if the user presses the help button + void activate() override; + void deactivate() override; + bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp index 7b9b99c7e7..e4f3178677 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ TaskDressUpParameters::TaskDressUpParameters( , DressUpView(DressUpView) { // remember initial transaction ID - App::GetApplication().getActiveTransaction(&transactionID); + transactionID = DressUpView->getObject()->getDocument()->getBookedTransactionID(); selectionMode = none; } @@ -85,16 +86,16 @@ void TaskDressUpParameters::setupTransaction() return; } - int tid = 0; - App::GetApplication().getActiveTransaction(&tid); - if (tid && tid == transactionID) { + int tid = DressUpView->getObject()->getDocument()->getBookedTransactionID(); + if (tid != App::NullTransaction && tid == transactionID) { return; } // open a transaction if none is active + // where is this transaction commited - theo-vt? std::string n("Edit "); n += DressUpView->getObject()->Label.getValue(); - transactionID = App::GetApplication().setActiveTransaction(n.c_str()); + transactionID = DressUpView->getObject()->getDocument()->openTransaction(n.c_str()); } void TaskDressUpParameters::referenceSelected(const Gui::SelectionChanges& msg, QListWidget* widget) @@ -426,8 +427,6 @@ void TaskDressUpParameters::setSelectionMode(selectionModes mode) setButtons(mode); if (mode == none) { - Gui::Selection().rmvSelectionGate(); - // remove any highlights and selections DressUpView->highlightReferences(false); @@ -438,11 +437,6 @@ void TaskDressUpParameters::setSelectionMode(selectionModes mode) } } else { - AllowSelectionFlags allow; - allow.setFlag(AllowSelection::EDGE, allowEdges); - allow.setFlag(AllowSelection::FACE, allowFaces); - Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), allow)); - DressUpView->highlightReferences(true); // selection must come from the previous feature, we also need to remember the currently @@ -450,9 +444,21 @@ void TaskDressUpParameters::setSelectionMode(selectionModes mode) previouslyShownViewProvider = DressUpView->getBodyViewProvider()->getShownViewProvider(); DressUpView->showPreviousFeature(true); } - + setSelectionGate(); Gui::Selection().clearSelection(); } +void TaskDressUpParameters::setSelectionGate() +{ + if (selectionMode == none) { + Gui::Selection().rmvSelectionGate(); + } + else { + AllowSelectionFlags allow; + allow.setFlag(AllowSelection::EDGE, allowEdges); + allow.setFlag(AllowSelection::FACE, allowFaces); + Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), allow)); + } +} //************************************************************************** //************************************************************************** @@ -507,5 +513,14 @@ bool TaskDlgDressUpParameters::reject() getViewObject()->highlightReferences(false); return TaskDlgFeatureParameters::reject(); } +void TaskDlgDressUpParameters::activate() +{ + parameter->attachSelection(); + parameter->setSelectionGate(); +} +void TaskDlgDressUpParameters::deactivate() +{ + parameter->detachSelection(); +} #include "moc_TaskDressUpParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h index 39342a4c49..f20ec3d785 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h @@ -68,6 +68,8 @@ public: return transactionID; } + void setSelectionGate(); + bool event(QEvent* event) override; protected Q_SLOTS: @@ -137,6 +139,8 @@ public: /// is called by the framework if the dialog is accepted (Ok) bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; protected: TaskDressUpParameters* parameter; diff --git a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp index 99f4220bbd..1f1da97189 100644 --- a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp @@ -214,10 +214,10 @@ bool TaskDlgFeatureParameters::accept() } Gui::cmdGuiDocument(feature, "resetEdit()"); - Gui::Command::commitCommand(); + feature->getDocument()->commitTransaction(); } catch (const Base::Exception& e) { - + feature->getDocument()->abortTransaction(); QString errorText = QString::fromUtf8(e.what()); QString statusText = QString::fromUtf8(getObject()->getStatusString()); @@ -263,7 +263,7 @@ bool TaskDlgFeatureParameters::reject() } // roll back the done things which may delete the feature - Gui::Command::abortCommand(); + document->abortTransaction(); // if abort command deleted the object make the previous feature visible again if (weakptr.expired()) { @@ -285,5 +285,27 @@ bool TaskDlgFeatureParameters::reject() return true; } +void TaskDlgFeatureParameters::activate() +{ + std::vector subwidgets = getDialogContent(); + for (auto it : subwidgets) { + TaskSketchBasedParameters* param = qobject_cast(it); + if (param) { + param->attachSelection(); + } + } +} +void TaskDlgFeatureParameters::deactivate() +{ + // detach the task panel from the selection to avoid to invoke + // eventually onAddSelection when the selection changes + std::vector subwidgets = getDialogContent(); + for (auto it : subwidgets) { + TaskSketchBasedParameters* param = qobject_cast(it); + if (param) { + param->detachSelection(); + } + } +} #include "moc_TaskFeatureParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskFeatureParameters.h b/src/Mod/PartDesign/Gui/TaskFeatureParameters.h index 3746aec053..74d7f24892 100644 --- a/src/Mod/PartDesign/Gui/TaskFeatureParameters.h +++ b/src/Mod/PartDesign/Gui/TaskFeatureParameters.h @@ -156,6 +156,8 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; + void activate() override; + void deactivate() override; template T* getViewObject() const diff --git a/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp b/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp index 6faf894bab..5d91cd6801 100644 --- a/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp +++ b/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp @@ -514,10 +514,16 @@ void TaskFeaturePick::onSelectionChanged(const Gui::SelectionChanges& msg) item->setSelected(true); if (msg.Type == Gui::SelectionChanges::AddSelection) { + std::string docNameCopy = documentName; if (isSingleSelectionEnabled()) { QMetaObject::invokeMethod( qobject_cast(&Gui::Control()), - "accept", + [docNameCopy] { + Gui::Control().accept( + Gui::Application::Instance->getDocument(docNameCopy.c_str()) + ->getDocument() + ); + }, Qt::QueuedConnection ); } @@ -557,9 +563,14 @@ void TaskFeaturePick::onDoubleClick(QListWidgetItem* item) Gui::Selection().addSelection(documentName.c_str(), t.toLatin1()); doSelection = false; + std::string docNameCopy = documentName; QMetaObject::invokeMethod( qobject_cast(&Gui::Control()), - "accept", + [docNameCopy] { + Gui::Control().accept( + Gui::Application::Instance->getDocument(docNameCopy.c_str())->getDocument() + ); + }, Qt::QueuedConnection ); } @@ -571,17 +582,18 @@ void TaskFeaturePick::slotDeletedObject(const Gui::ViewProviderDocumentObject& O } } -void TaskFeaturePick::slotUndoDocument(const Gui::Document&) +void TaskFeaturePick::slotUndoDocument(const Gui::Document& doc) { if (origins.empty()) { - QTimer::singleShot(100, &Gui::Control(), &Gui::ControlSingleton::closeDialog); + QTimer::singleShot(100, [&doc]() { Gui::Control().closeDialog(doc.getDocument()); }); } } -void TaskFeaturePick::slotDeleteDocument(const Gui::Document&) +void TaskFeaturePick::slotDeleteDocument(const Gui::Document& doc) { origins.clear(); - QTimer::singleShot(100, &Gui::Control(), &Gui::ControlSingleton::closeDialog); + App::Document* docPtr = doc.getDocument(); + QTimer::singleShot(100, [docPtr]() { Gui::Control().closeDialog(docPtr); }); } void TaskFeaturePick::showExternal(bool val) @@ -665,6 +677,14 @@ bool TaskDlgFeaturePick::reject() accepted = false; return true; } +void TaskDlgFeaturePick::activate() +{ + pick->attachSelection(); +} +void TaskDlgFeaturePick::deactivate() +{ + pick->detachSelection(); +} void TaskDlgFeaturePick::showExternal(bool val) { diff --git a/src/Mod/PartDesign/Gui/TaskFeaturePick.h b/src/Mod/PartDesign/Gui/TaskFeaturePick.h index 6f6f564c63..96e8f792bd 100644 --- a/src/Mod/PartDesign/Gui/TaskFeaturePick.h +++ b/src/Mod/PartDesign/Gui/TaskFeaturePick.h @@ -129,7 +129,10 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - /// is called by the framework if the user presses the help button + + void activate() override; + void deactivate() override; + bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp b/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp index 9bc1890ae4..2aa4253a83 100644 --- a/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp @@ -257,7 +257,7 @@ void TaskMultiTransformParameters::onTransformAddMirrored() } if (isEnabledTransaction()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Mirror")); + pcBody->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Mirror")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::Mirrored','" << newFeatName << "')"); @@ -297,7 +297,7 @@ void TaskMultiTransformParameters::onTransformAddLinearPattern() } if (isEnabledTransaction()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Linear Pattern")); + pcBody->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Linear Pattern")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::LinearPattern','" << newFeatName << "')"); @@ -346,7 +346,7 @@ void TaskMultiTransformParameters::onTransformAddPolarPattern() } if (isEnabledTransaction()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Polar Pattern")); + pcBody->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Polar Pattern")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::PolarPattern','" << newFeatName << "')"); @@ -385,7 +385,7 @@ void TaskMultiTransformParameters::onTransformAddScaled() } if (isEnabledTransaction()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scale")); + pcBody->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Scale")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::Scaled','" << newFeatName << "')"); diff --git a/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp b/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp index b9439c1b1e..0e1011900e 100644 --- a/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp @@ -551,7 +551,7 @@ bool TaskPipeParameters::accept() throw Base::RuntimeError(getObject()->getStatusString()); } Gui::cmdGuiDocument(pipe, "resetEdit()"); - Gui::Command::commitCommand(); + pipe->getDocument()->commitTransaction(); // we need to add the copied features to the body after the command action, as otherwise // FreeCAD crashes unexplainably @@ -560,6 +560,7 @@ bool TaskPipeParameters::accept() } } catch (const Base::Exception& e) { + pipe->getDocument()->abortTransaction(); QMessageBox::warning(this, tr("Input error"), QApplication::translate("Exception", e.what())); return false; } diff --git a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp index def61aff1f..9533eae109 100644 --- a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp @@ -1108,7 +1108,8 @@ bool TaskDlgPrimitiveParameters::accept() bool TaskDlgPrimitiveParameters::reject() { // roll back the done things - Gui::Command::abortCommand(); + // Gui::Command::abortCommand(); + vp_prm->getDocument()->abortCommand(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); return true; diff --git a/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp b/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp index 60de257ed0..db1aa26b9f 100644 --- a/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp +++ b/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp @@ -433,10 +433,11 @@ bool TaskDlgShapeBinder::accept() throw Base::RuntimeError(vp->getObject()->getStatusString()); } Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); - Gui::Command::commitCommand(); + vp->getDocument()->commitCommand(); } } catch (const Base::Exception& e) { + vp->getDocument()->abortCommand(); QMessageBox::warning( parameter, tr("Input error"), @@ -451,13 +452,22 @@ bool TaskDlgShapeBinder::accept() bool TaskDlgShapeBinder::reject() { if (!vp.expired()) { - App::Document* doc = vp->getObject()->getDocument(); // roll back the done things (deletes 'vp') - Gui::Command::abortCommand(); + // Gui::Command::abortCommand(); + vp->getDocument()->abortCommand(); + App::Document* doc = vp->getObject()->getDocument(); Gui::cmdGuiDocument(doc, "resetEdit()"); Gui::cmdAppDocument(doc, "recompute()"); } return true; } +void TaskDlgShapeBinder::activate() +{ + parameter->attachSelection(); +} +void TaskDlgShapeBinder::deactivate() +{ + parameter->detachSelection(); +} #include "moc_TaskShapeBinder.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskShapeBinder.h b/src/Mod/PartDesign/Gui/TaskShapeBinder.h index 190537eba2..61f54f9130 100644 --- a/src/Mod/PartDesign/Gui/TaskShapeBinder.h +++ b/src/Mod/PartDesign/Gui/TaskShapeBinder.h @@ -49,7 +49,7 @@ namespace PartDesignGui { -class TaskShapeBinder: public Gui::TaskView::TaskBox, Gui::SelectionObserver +class TaskShapeBinder: public Gui::TaskView::TaskBox, public Gui::SelectionObserver { Q_OBJECT @@ -112,6 +112,8 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; + void activate() override; + void deactivate() override; protected: TaskShapeBinder* parameter; diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index 9eb1e0f85b..a4c10bd78b 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -70,9 +71,6 @@ TaskTransformedParameters::TaskTransformedParameters( { Gui::Document* doc = TransformedView->getDocument(); this->attachDocument(doc); - - // remember initial transaction ID - App::GetApplication().getActiveTransaction(&transactionID); } TaskTransformedParameters::TaskTransformedParameters(TaskMultiTransformParameters* parentTask) @@ -276,16 +274,16 @@ void TaskTransformedParameters::setupTransaction() return; } - int tid = 0; - App::GetApplication().getActiveTransaction(&tid); - if (tid != 0 && tid == transactionID) { + int tid = obj->getDocument()->getBookedTransactionID(); + if (tid != App::NullTransaction) { return; } // open a transaction if none is active + // where is this transaction commited - theo-vt? std::string name("Edit "); name += obj->Label.getValue(); - transactionID = App::GetApplication().setActiveTransaction(name.c_str()); + transactionID = obj->getDocument()->openTransaction(name.c_str()); } void TaskTransformedParameters::setEnabledTransaction(bool on) @@ -636,6 +634,14 @@ bool TaskDlgTransformedParameters::reject() parameter->exitSelectionMode(); return TaskDlgFeatureParameters::reject(); } +void TaskDlgTransformedParameters::activate() +{ + parameter->attachSelection(); +} +void TaskDlgTransformedParameters::deactivate() +{ + parameter->detachSelection(); +} #include "moc_TaskTransformedParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h index 8f322224db..9be92754f6 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h @@ -236,6 +236,9 @@ public: /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; + void activate() override; + void deactivate() override; + protected: TaskTransformedParameters* parameter = nullptr; TaskTransformedMessages* message = nullptr; diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index 86d02d63a4..ec6648a5ec 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -92,11 +92,11 @@ bool ViewProvider::doubleClicked() { try { QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); - Gui::Command::openCommand(text.toUtf8()); + getDocument()->openCommand(text.toUtf8()); Gui::cmdSetEdit(pcObject, Gui::Application::Instance->getUserEditMode()); } catch (const Base::Exception&) { - Gui::Command::abortCommand(); + getDocument()->abortCommand(); } return true; } @@ -172,7 +172,10 @@ bool ViewProvider::setEdit(int ModNum) } } - Gui::Control().showDialog(featureDlg); + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == this; + }); + Gui::Control().showDialog(featureDlg, editDoc->getDocument()); return true; } else { diff --git a/src/Mod/PartDesign/Gui/ViewProviderBase.cpp b/src/Mod/PartDesign/Gui/ViewProviderBase.cpp index 2046ee5e75..7e27adfb21 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBase.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderBase.cpp @@ -53,11 +53,11 @@ bool ViewProviderBase::doubleClicked() try { std::string Msg("Edit "); Msg += base->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + getDocument()->openCommand(Msg.c_str()); Gui::cmdSetEdit(base, Gui::Application::Instance->getUserEditMode()); } catch (const Base::Exception&) { - Gui::Command::abortCommand(); + getDocument()->commitCommand(); } return true; } diff --git a/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp b/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp index b87228b407..2b5c3167db 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp index b7e6e86db3..175342ab1d 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp @@ -302,7 +302,7 @@ bool ViewProviderDatum::doubleClicked() std::string Msg("Edit "); Msg += this->pcObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + getDocument()->openCommand(Msg.c_str()); Part::Datum* pcDatum = getObject(); PartDesign::Body* activeBody = activeView->getActiveObject(PDBODYKEY); diff --git a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp index 990e41d677..5e59fabe51 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp @@ -27,6 +27,7 @@ #include +#include #include #include #include diff --git a/src/Mod/PartDesign/Gui/ViewProviderShapeBinder.cpp b/src/Mod/PartDesign/Gui/ViewProviderShapeBinder.cpp index 31191609fa..f9969e8387 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderShapeBinder.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderShapeBinder.cpp @@ -225,7 +225,7 @@ void ViewProviderShapeBinder::setupContextMenu(QMenu* menu, QObject* receiver, c Gui::ActionFunction* func = new Gui::ActionFunction(menu); func->trigger(act, [this]() { QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); - Gui::Command::openCommand(text.toUtf8()); + getDocument()->openCommand(text.toUtf8()); Gui::Document* document = this->getDocument(); if (document) { @@ -436,13 +436,14 @@ void ViewProviderSubShapeBinder::updatePlacement(bool transaction) return; } - App::GetApplication().setActiveTransaction("Sync binder"); + + getDocument()->openCommand("Sync binder"); try { if (relative) { self->Context.setValue(parent, parentSub.c_str()); } self->update(PartDesign::SubShapeBinder::UpdateForced); - App::GetApplication().closeActiveTransaction(); + getDocument()->commitCommand(); return; } catch (Base::Exception& e) { @@ -460,7 +461,7 @@ void ViewProviderSubShapeBinder::updatePlacement(bool transaction) } FC_ERR(str.str()); } - App::GetApplication().closeActiveTransaction(true); + getDocument()->abortCommand(); } std::vector ViewProviderSubShapeBinder::claimChildren() const diff --git a/src/Mod/ReverseEngineering/Gui/FitBSplineCurve.cpp b/src/Mod/ReverseEngineering/Gui/FitBSplineCurve.cpp index 96a0c5ba29..0e0a45bc40 100644 --- a/src/Mod/ReverseEngineering/Gui/FitBSplineCurve.cpp +++ b/src/Mod/ReverseEngineering/Gui/FitBSplineCurve.cpp @@ -85,7 +85,7 @@ bool FitBSplineCurveWidget::accept() tryAccept(); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + d->obj.getDocument()->abortTransaction(); QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); return false; } @@ -135,9 +135,9 @@ void FitBSplineCurveWidget::exeCommand(const QString& cmd) { Gui::WaitCursor wc; Gui::Command::addModule(Gui::Command::App, "ReverseEngineering"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Fit B-spline")); + d->obj.getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Fit B-spline")); Gui::Command::runCommand(Gui::Command::Doc, cmd.toLatin1()); - Gui::Command::commitCommand(); + d->obj.getDocument()->commitTransaction(); Gui::Command::updateActive(); } @@ -147,7 +147,7 @@ void FitBSplineCurveWidget::tryCommand(const QString& cmd) exeCommand(cmd); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + d->obj.getDocument()->abortTransaction(); e.reportException(); } } diff --git a/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp b/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp index 8e06862861..37b96fd1fa 100644 --- a/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp +++ b/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp @@ -157,17 +157,17 @@ void FitBSplineSurfaceWidget::onMakePlacementClicked() ) .arg(document, argument); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); + d->obj.getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Placement")); Gui::Command::runCommand(Gui::Command::Doc, "from FreeCAD import Base"); Gui::Command::runCommand(Gui::Command::Doc, command.toLatin1()); - Gui::Command::commitCommand(); + d->obj.getDocument()->commitTransaction(); Gui::Command::updateActive(); } } } } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + d->obj.getDocument()->abortTransaction(); QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); } } @@ -239,13 +239,13 @@ bool FitBSplineSurfaceWidget::accept() Gui::WaitCursor wc; Gui::Command::addModule(Gui::Command::App, "ReverseEngineering"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Fit B-spline")); + d->obj.getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Fit B-spline")); Gui::Command::runCommand(Gui::Command::Doc, command.toLatin1()); - Gui::Command::commitCommand(); + d->obj.getDocument()->commitTransaction(); Gui::Command::updateActive(); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + d->obj.getDocument()->abortTransaction(); QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); return false; } diff --git a/src/Mod/ReverseEngineering/Gui/Poisson.cpp b/src/Mod/ReverseEngineering/Gui/Poisson.cpp index 304dbae3aa..86d7bbcf0e 100644 --- a/src/Mod/ReverseEngineering/Gui/Poisson.cpp +++ b/src/Mod/ReverseEngineering/Gui/Poisson.cpp @@ -84,13 +84,13 @@ bool PoissonWidget::accept() Gui::WaitCursor wc; Gui::Command::addModule(Gui::Command::App, "ReverseEngineering"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Poisson reconstruction")); + d->obj.getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Poisson reconstruction")); Gui::Command::runCommand(Gui::Command::Doc, command.toLatin1()); - Gui::Command::commitCommand(); + d->obj.getDocument()->commitTransaction(); Gui::Command::updateActive(); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + d->obj.getDocument()->abortTransaction(); QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); return false; } diff --git a/src/Mod/Show/SceneDetails/ClipPlane.py b/src/Mod/Show/SceneDetails/ClipPlane.py index 4784fa1188..22277f792f 100644 --- a/src/Mod/Show/SceneDetails/ClipPlane.py +++ b/src/Mod/Show/SceneDetails/ClipPlane.py @@ -60,7 +60,9 @@ class ClipPlane(SceneDetail): def _viewer(self): if self.doc is None: - gdoc = Gui.editDocument() + # Originaly used edit document but now multiple documents can + # be in edit so use the active document as fall back + gdoc = Gui.ActiveDocument else: gdoc = Gui.getDocument(self.doc.Name) v = gdoc.activeView() diff --git a/src/Mod/Show/mTempoVis.py b/src/Mod/Show/mTempoVis.py index c6a262d864..fce54261aa 100644 --- a/src/Mod/Show/mTempoVis.py +++ b/src/Mod/Show/mTempoVis.py @@ -476,13 +476,12 @@ class TempoVis(object): result.append(obj) return result - def sketchClipPlane(self, sketch, enable=None, reverted=False): + def sketchClipPlane(self, sketch, editDoc, enable=None, reverted=False): """sketchClipPlane(sketch, enable = None): Clips all objects by plane of sketch. If enable argument is omitted, calling the routine repeatedly will toggle clipping plane.""" from .SceneDetails.ClipPlane import ClipPlane - editDoc = Gui.editDocument() if editDoc is None: doc = sketch.Document pla = sketch.getGlobalPlacement() diff --git a/src/Mod/Sketcher/Gui/Command.cpp b/src/Mod/Sketcher/Gui/Command.cpp index 23401ef7b2..1224266423 100644 --- a/src/Mod/Sketcher/Gui/Command.cpp +++ b/src/Mod/Sketcher/Gui/Command.cpp @@ -271,6 +271,7 @@ void CmdSketcherNewSketch::activated(int iMsg) FeatName.c_str()); } } + commitCommand(); } else { // ask user for orientation @@ -313,6 +314,7 @@ void CmdSketcherNewSketch::activated(int iMsg) FeatName.c_str(), AttachEngine::getModeName(Attacher::mmDeactivated).c_str()); doCommand(Gui, "Gui.activeDocument().setEdit('%s')", FeatName.c_str()); + commitCommand(); } } @@ -559,6 +561,7 @@ void CmdSketcherReorientSketch::activated(int iMsg) r[2], r[3]); doCommand(Gui, "Gui.ActiveDocument.setEdit('%s')", sketch->getNameInDocument()); + commitCommand(); } bool CmdSketcherReorientSketch::isActive() @@ -759,6 +762,7 @@ void CmdSketcherMapSketch::activated(int iMsg) Gui::cmdAppObjectArgs( sketch, "MapMode = \"%s\"", AttachEngine::getModeName(suggMapMode).c_str()); Gui::cmdAppObjectArgs(sketch, "AttachmentSupport = %s", supportString.c_str()); + // commitCommand(); commitCommand(); doCommand(Gui, "App.activeDocument().recompute()"); } @@ -767,6 +771,7 @@ void CmdSketcherMapSketch::activated(int iMsg) Gui::cmdAppObjectArgs( sketch, "MapMode = \"%s\"", AttachEngine::getModeName(suggMapMode).c_str()); Gui::cmdAppObjectArgs(sketch, "AttachmentSupport = None"); + // commitCommand(); commitCommand(); doCommand(Gui, "App.activeDocument().recompute()"); } @@ -807,13 +812,11 @@ CmdSketcherViewSketch::CmdSketcherViewSketch() void CmdSketcherViewSketch::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document* doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = - dynamic_cast(doc->getInEdit()); - if (vp) { + + if (Gui::Application::Instance->isInEdit(getActiveGuiDocument())) { runCommand(Gui, "Gui.ActiveDocument.ActiveView.setCameraOrientation(" - "App.Placement(Gui.editDocument().EditingTransform).Rotation.Q)"); + "App.Placement(Gui.ActiveDocument.EditingTransform).Rotation.Q)"); } } @@ -981,6 +984,7 @@ void CmdSketcherMirrorSketch::activated(int iMsg) delete tempsketch; } + commitCommand(); doCommand(Gui, "App.activeDocument().recompute()"); } @@ -1078,6 +1082,8 @@ void CmdSketcherMergeSketches::activated(int iMsg) doCommand(Doc, "App.activeDocument().ActiveObject.Placement = App.activeDocument().%s.Placement", selection.front().getFeatName()); + + commitCommand(); doCommand(Doc, "App.activeDocument().recompute()"); } @@ -1109,8 +1115,9 @@ void CmdSketcherViewSection::activated(int iMsg) { Q_UNUSED(iMsg); QString cmdStr = - QLatin1String("ActiveSketch.ViewObject.TempoVis.sketchClipPlane(ActiveSketch, None, %1)\n"); + QLatin1String("ActiveSketch.ViewObject.TempoVis.sketchClipPlane(ActiveSketch, Gui.ActiveDocument, None, %1)\n"); Gui::Document* doc = getActiveGuiDocument(); + bool revert = false; if (doc) { SketcherGui::ViewProviderSketch* vp = diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index ceb3117693..4f0cd2c6dc 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -154,7 +154,8 @@ void finishDatumConstraint(Gui::Command* cmd, // Ask for the value of the distance immediately if (show && isDriving) { - EditDatumDialog editDatumDialog(sketch, ConStr.size() - 1); + EditDatumDialog editDatumDialog(cmd->transactionID(), sketch, ConStr.size() - 1); + cmd->resetTransactionID(); editDatumDialog.exec(); } else { @@ -260,7 +261,7 @@ void SketcherGui::makeAngleBetweenTwoLines(Sketcher::SketchObject* Obj, return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint")); Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f))", geoId1, @@ -365,10 +366,10 @@ bool SketcherGui::calculateAngle(Sketcher::SketchObject* Obj, int& GeoId1, int& /// geom2 => any of an ellipse, an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the ellipse /// geoId2 => geoid of geom2 -/// NOTE: A command must be opened before calling this function, which this function -/// commits or aborts as appropriate. The reason is for compatibility reasons with -/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj, +/// Returns true on success and false on failure +/// the call site is responsible to create, commit and abort commands +/// and recompute if it needs to +bool SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj, const Part::GeomEllipse* ellipse, const Part::Geometry* geom2, int geoId1, @@ -433,14 +434,11 @@ void SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj, Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); - tryAutoRecompute(Obj); - return; + return false; } - Gui::Command::commitCommand(); - tryAutoRecompute(Obj); + return true; } /// Makes a simple tangency constraint using extra point + tangent via point @@ -448,10 +446,10 @@ void SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj, /// geom2 => any of an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the arc of ellipse /// geoId2 => geoid of geom2 -/// NOTE: A command must be opened before calling this function, which this function -/// commits or aborts as appropriate. The reason is for compatibility reasons with -/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* Obj, +/// Returns true on success and false on failure +/// the call site is responsible to create, commit and abort commands +/// and recompute if it needs to +bool SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* Obj, const Part::GeomArcOfEllipse* aoe, const Part::Geometry* geom2, int geoId1, @@ -513,14 +511,9 @@ void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* O Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); - - tryAutoRecompute(Obj); - return; + return false; } - - Gui::Command::commitCommand(); - tryAutoRecompute(Obj); + return true; } /// Makes a simple tangency constraint using extra point + tangent via point @@ -528,10 +521,10 @@ void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* O /// geom2 => any of an arc of hyperbola, an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the arc of hyperbola /// geoId2 => geoid of geom2 -/// NOTE: A command must be opened before calling this function, which this function -/// commits or aborts as appropriate. The reason is for compatibility reasons with -/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void SketcherGui::makeTangentToArcOfHyperbolaviaNewPoint(Sketcher::SketchObject* Obj, +/// Returns true on success and false on failure +/// the call site is responsible to create, commit and abort commands +/// and recompute if it needs to +bool SketcherGui::makeTangentToArcOfHyperbolaviaNewPoint(Sketcher::SketchObject* Obj, const Part::GeomArcOfHyperbola* aoh, const Part::Geometry* geom2, int geoId1, @@ -610,25 +603,19 @@ void SketcherGui::makeTangentToArcOfHyperbolaviaNewPoint(Sketcher::SketchObject* Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); - - tryAutoRecompute(Obj); - return; + return false; } - - Gui::Command::commitCommand(); - - tryAutoRecompute(Obj); + return true; } /// Makes a simple tangency constraint using extra point + tangent via point /// aop => an arc of parabola /// geom2 => any of an arc of parabola, an arc of hyperbola an arc of ellipse, a circle, or an arc -/// (of circle) geoId1 => geoid of the arc of parabola geoId2 => geoid of geom2 NOTE: A command must -/// be opened before calling this function, which this function commits or aborts as appropriate. -/// The reason is for compatibility reasons with other code e.g. "Autoconstraints" in -/// DrawSketchHandler.cpp -void SketcherGui::makeTangentToArcOfParabolaviaNewPoint(Sketcher::SketchObject* Obj, +/// (of circle) geoId1 => geoid of the arc of parabola geoId2 => geoid of geom2 +/// Returns true on success and false on failure +/// the call site is responsible to create, commit and abort commands +/// and recompute if it needs to +bool SketcherGui::makeTangentToArcOfParabolaviaNewPoint(Sketcher::SketchObject* Obj, const Part::GeomArcOfParabola* aop, const Part::Geometry* geom2, int geoId1, @@ -701,14 +688,10 @@ void SketcherGui::makeTangentToArcOfParabolaviaNewPoint(Sketcher::SketchObject* QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); - - tryAutoRecompute(Obj); - return; + return false; } - Gui::Command::commitCommand(); - tryAutoRecompute(Obj); + return true; } void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj, @@ -775,8 +758,6 @@ bool addConstraintSafely(SketchObject* obj, std::function constraintaddi QT_TRANSLATE_NOOP("Notifications", "Invalid constraint"), e.what()); - Gui::Command::abortCommand(); - tryAutoRecompute(obj); return false; } @@ -786,8 +767,6 @@ bool addConstraintSafely(SketchObject* obj, std::function constraintaddi QObject::tr("Error"), QObject::tr("Unexpected error. More information may be available in the report view.")); - Gui::Command::abortCommand(); - tryAutoRecompute(obj); return false; } @@ -1775,7 +1754,7 @@ public: void activated() override { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); Obj = sketchgui->getSketchObject(); @@ -1808,7 +1787,7 @@ public: void deactivated() override { - Gui::Command::abortCommand(); + abortCommand(); Obj->solve(); sketchgui->draw(false, false); // Redraw } @@ -2119,7 +2098,7 @@ protected: for (int index : cstrIndexes | boost::adaptors::reversed) { if (show && ConStr[index]->isDimensional() && ConStr[index]->isDriving) { commandHandledInEditDatum = true; - EditDatumDialog editDatumDialog(sketchgui, index); + EditDatumDialog editDatumDialog(currentTransactionID, sketchgui, index); editDatumDialog.exec(); if (!editDatumDialog.isSuccess()) { break; @@ -2127,8 +2106,9 @@ protected: } } - if (!commandHandledInEditDatum) - Gui::Command::commitCommand(); + if (!commandHandledInEditDatum) { + commitCommand(); + } // This code enables the continuous creation mode. bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); @@ -3125,7 +3105,7 @@ protected: } //make sure we are not taking into account the constraint created in previous mode. - Gui::Command::abortCommand(); + abortCommand(); Obj->solve(); auto solvext = Obj->getSolvedSketch().getSolverExtension(geoId); @@ -3172,19 +3152,19 @@ protected: void restartCommand(const char* cstrName) { specialConstraint = SpecialConstraint::None; - Gui::Command::abortCommand(); + abortCommand(); Obj->solve(); sketchgui->draw(false, false); // Redraw - Gui::Command::openCommand(cstrName); + openCommand(cstrName); cstrIndexes.clear(); } void resetTool() { - Gui::Command::abortCommand(); + abortCommand(); Gui::Selection().clearSelection(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); cstrIndexes.clear(); specialConstraint = SpecialConstraint::None; previousOnSketchPos = Base::Vector2d(0.f, 0.f); @@ -3211,7 +3191,6 @@ CmdSketcherDimension::CmdSketcherDimension() void CmdSketcherDimension::activated(int iMsg) { Q_UNUSED(iMsg); - App::AutoTransaction::setEnable(false); // get the selection std::vector selection = getSelection().getSelectionEx(); @@ -3392,7 +3371,7 @@ void horVerActivated(CmdSketcherConstraint* cmd, std::string type) if (!edgegeoids.empty()) { // undo command open const char* cmdName = type == "Horizontal" ? "Add horizontal constraint" : type == "Vertical" ? "Add vertical constraint" : "Add horizontal/vertical constraint"; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); for (auto& geoId : edgegeoids) { std::string typeToApply = type; if (type == "HorVer") { @@ -3411,7 +3390,7 @@ void horVerActivated(CmdSketcherConstraint* cmd, std::string type) else if (fixedpoints <= 1) {// pointgeoids // undo command open const char* cmdName = type == "Horizontal" ? "Add horizontal alignment" : type == "Vertical" ? "Add vertical alignment" : "Add horizontal/vertical alignment"; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); std::vector::iterator it; std::vector::iterator itp; for (it = pointgeoids.begin(), itp = pointpos.begin(); @@ -3443,7 +3422,7 @@ void horVerActivated(CmdSketcherConstraint* cmd, std::string type) return; } // finish the transaction and update - Gui::Command::commitCommand(); + cmd->commitCommand(); tryAutoRecompute(Obj); @@ -3490,7 +3469,7 @@ void horVerApplyConstraint(CmdSketcherConstraint* cmd, std::string type, std::ve } const char* cmdName = typeToApply == "Horizontal" ? "Add horizontal constraint" : "Add vertical constraint"; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); // issue the actual commands to create the constraint Gui::cmdAppObjectArgs(sketchgui->getObject(), @@ -3498,7 +3477,7 @@ void horVerApplyConstraint(CmdSketcherConstraint* cmd, std::string type, std::ve typeToApply, CrvId); // finish the transaction and update - Gui::Command::commitCommand(); + cmd->commitCommand(); tryAutoRecompute(Obj); } @@ -3531,7 +3510,7 @@ void horVerApplyConstraint(CmdSketcherConstraint* cmd, std::string type, std::ve // undo command open const char* cmdName = type == "Horizontal" ? "Add horizontal alignment" : "Add vertical alignment"; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", cmdName)); // issue the actual commands to create the constraint Gui::cmdAppObjectArgs(sketchgui->getObject(), @@ -3542,7 +3521,7 @@ void horVerApplyConstraint(CmdSketcherConstraint* cmd, std::string type, std::ve GeoId2, static_cast(PosId2)); // finish the transaction and update - Gui::Command::commitCommand(); + cmd->commitCommand(); tryAutoRecompute(Obj); @@ -3885,6 +3864,7 @@ void CmdSketcherConstrainLock::activated(int iMsg) // finish the transaction and update commitCommand(); + tryAutoRecompute(Obj); // clear the selection (convenience) @@ -3914,7 +3894,7 @@ void CmdSketcherConstrainLock::applyConstraint(std::vector& selSeq, i Base::Vector3d pnt = Obj->getPoint(selSeq.front().GeoId, selSeq.front().PosId); // undo command open - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add fixed constraint")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add fixed constraint")); Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('DistanceX', %d, %d, %f))", selSeq.front().GeoId, @@ -3942,7 +3922,7 @@ void CmdSketcherConstrainLock::applyConstraint(std::vector& selSeq, i } // finish the transaction and update - Gui::Command::commitCommand(); + commitCommand(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher"); @@ -4092,12 +4072,11 @@ void CmdSketcherConstrainBlock::activated(int iMsg) }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + commitCommand(); + tryAutoRecompute(Obj); } // clear the selection (convenience) @@ -4143,12 +4122,12 @@ void CmdSketcherConstrainBlock::applyConstraint(std::vector& selSeq, }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + + commitCommand(); + tryAutoRecompute(Obj); } break; default: break; @@ -4414,8 +4393,7 @@ void CmdSketcherConstrainCoincidentUnified::activatedPointOnObject(SketchObject* QObject::tr("Wrong selection"), QObject::tr("Select an edge that is not a B-spline weight.")); abortCommand(); - - continue; + return; } if (substituteConstraintCombinationsPointOnObject(obj, @@ -4445,7 +4423,6 @@ void CmdSketcherConstrainCoincidentUnified::activatedPointOnObject(SketchObject* QObject::tr("Wrong selection"), QObject::tr("None of the selected points were constrained onto the respective curves, because they are part of the same element, they are both external geometry, or the edge is not eligible.")); } - return; } void CmdSketcherConstrainCoincidentUnified::activatedCoincident(SketchObject* obj, std::vector points, std::vector curves) @@ -4618,7 +4595,6 @@ void CmdSketcherConstrainCoincidentUnified::applyConstraintPointOnObject(std::ve "either because they are parts of the same element, " "or because they are both external geometry.")); } - return; } void CmdSketcherConstrainCoincidentUnified::applyConstraintCoincident(std::vector& selSeq, int seqIndex) @@ -4659,7 +4635,8 @@ void CmdSketcherConstrainCoincidentUnified::applyConstraintCoincident(std::vecto return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint")); + // undo command open + openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint")); if (substituteConstraintCombinationsCoincident(Obj, GeoId1, PosId1, GeoId2, PosId2)) {} else if (isCoincidentSelectionValid(Obj, GeoId1, PosId1, GeoId2, PosId2)) { @@ -4671,10 +4648,10 @@ void CmdSketcherConstrainCoincidentUnified::applyConstraintCoincident(std::vecto static_cast(PosId2)); } else { - Gui::Command::abortCommand(); + abortCommand(); return; } - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecompute(Obj); } @@ -5946,7 +5923,6 @@ void CmdSketcherConstrainDistanceY::activated(int iMsg) Obj, QObject::tr("Wrong selection"), QObject::tr("Select exactly one line or up to two points from the sketch.")); - return; } void CmdSketcherConstrainDistanceY::applyConstraint(std::vector& selSeq, int seqIndex) @@ -6500,6 +6476,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) GeoId1, static_cast(PosId1), GeoId2); + commitCommand(); tryAutoRecompute(Obj); @@ -6655,7 +6632,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecompute(Obj); return; @@ -6680,8 +6657,6 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) return; } } - - return; } void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& selSeq, @@ -6859,7 +6834,7 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecompute(Obj); @@ -6977,12 +6952,11 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + commitCommand(); + tryAutoRecompute(Obj); getSelection().clearSelection(); @@ -7007,7 +6981,7 @@ protected: void activated(int iMsg) override; void applyConstraint(std::vector& selSeq, int seqIndex) override; // returns true if a substitution took place - static bool substituteConstraintCombinations(SketchObject* Obj, int GeoId1, int GeoId2); + bool substituteConstraintCombinations(SketchObject* Obj, int GeoId1, int GeoId2); }; CmdSketcherConstrainTangent::CmdSketcherConstrainTangent() @@ -7060,7 +7034,7 @@ bool CmdSketcherConstrainTangent::substituteConstraintCombinations(SketchObject* int first = (*it)->First; int firstpos = static_cast((*it)->FirstPos); - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Swap coincident+tangency with ptp tangency")); doEndpointTangency(Obj, (*it)->First, (*it)->Second, (*it)->FirstPos, (*it)->SecondPos); @@ -7083,7 +7057,7 @@ bool CmdSketcherConstrainTangent::substituteConstraintCombinations(SketchObject* || ((*it)->Second == GeoId1 && (*it)->First == GeoId2)) && ((*it)->FirstPos == Sketcher::PointPos::start || (*it)->FirstPos == Sketcher::PointPos::end)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Swap point on object and tangency with point to curve tangency")); @@ -7244,12 +7218,11 @@ void CmdSketcherConstrainTangent::activated(int iMsg) }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + commitCommand(); + tryAutoRecompute(Obj); getSelection().clearSelection(); @@ -7387,37 +7360,45 @@ void CmdSketcherConstrainTangent::activated(int iMsg) geom2 = Obj->getGeometry(GeoId2); if (isEllipse(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToEllipseviaNewPoint(Obj, + closeAndRecompute(currentTransactionID, + !makeTangentToEllipseviaNewPoint(Obj, static_cast(geom1), geom2, GeoId1, - GeoId2); + GeoId2), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfHyperbola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfHyperbolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfHyperbolaviaNewPoint(Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfParabola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } @@ -7434,27 +7415,32 @@ void CmdSketcherConstrainTangent::activated(int iMsg) if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfEllipseviaNewPoint( - Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfEllipseviaNewPoint( + Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); getSelection().clearSelection(); return; } else if (isArcOfParabola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } @@ -7471,26 +7457,32 @@ void CmdSketcherConstrainTangent::activated(int iMsg) if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfHyperbolaviaNewPoint( - Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfHyperbolaviaNewPoint( + Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfParabola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } @@ -7508,14 +7500,17 @@ void CmdSketcherConstrainTangent::activated(int iMsg) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); + getSelection().clearSelection(); return; } @@ -7601,37 +7596,46 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq if (isEllipse(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToEllipseviaNewPoint(Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToEllipseviaNewPoint(Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfHyperbola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfHyperbolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfHyperbolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfParabola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } @@ -7648,26 +7652,32 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfHyperbolaviaNewPoint( - Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfHyperbolaviaNewPoint( + Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); + getSelection().clearSelection(); return; } else if (isArcOfParabola(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom2), - geom1, - GeoId2, - GeoId1); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom2), + geom1, + GeoId2, + GeoId1), + Obj); + getSelection().clearSelection(); return; } @@ -7684,14 +7694,17 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq if (isArcOfParabola(*geom2) || isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) { - Gui::Command::openCommand( + openCommand( QT_TRANSLATE_NOOP("Command", "Add tangent constraint point")); - makeTangentToArcOfParabolaviaNewPoint( - Obj, - static_cast(geom1), - geom2, - GeoId1, - GeoId2); + closeAndRecompute(currentTransactionID, + !makeTangentToArcOfParabolaviaNewPoint( + Obj, + static_cast(geom1), + geom2, + GeoId1, + GeoId2), + Obj); + getSelection().clearSelection(); return; } @@ -7853,12 +7866,11 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + commitCommand(); + tryAutoRecompute(Obj); getSelection().clearSelection(); @@ -10437,12 +10449,11 @@ void CmdSketcherConstrainSnellsLaw::activated(int iMsg) }); if (!safe) { + abortCommand(); return; } - else { - commitCommand(); - tryAutoRecompute(Obj); - } + commitCommand(); + tryAutoRecompute(Obj); // clear the selection (convenience) getSelection().clearSelection(); @@ -10731,8 +10742,8 @@ void CmdSketcherChangeDimensionConstraint::activated(int iMsg) try { auto value = getDimConstraint(); - EditDatumDialog editDatumDialog(std::get<0>(value), std::get<1>(value)); - editDatumDialog.exec(false); + int tid = openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Modify sketch constraints")); + EditDatumDialog(tid, std::get<0>(value), std::get<1>(value)).exec(false); } catch (const Base::RuntimeError&) { Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(), diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.h b/src/Mod/Sketcher/Gui/CommandConstraints.h index 0739a7cc99..8f0e413ba7 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.h +++ b/src/Mod/Sketcher/Gui/CommandConstraints.h @@ -56,10 +56,11 @@ bool calculateAngle( /// geom2 => any of an ellipse, an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the ellipse /// geoId2 => geoid of geom2 +/// returns false if there was an exception /// NOTE: A command must be opened before calling this function, which this function /// commits or aborts as appropriate. The reason is for compatibility reasons with /// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void makeTangentToEllipseviaNewPoint( +bool makeTangentToEllipseviaNewPoint( Sketcher::SketchObject* Obj, const Part::GeomEllipse* ellipse, const Part::Geometry* geom2, @@ -71,10 +72,11 @@ void makeTangentToEllipseviaNewPoint( /// geom2 => any of an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the arc of ellipse /// geoId2 => geoid of geom2 +/// returns false if there was an exception /// NOTE: A command must be opened before calling this function, which this function /// commits or aborts as appropriate. The reason is for compatibility reasons with /// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void makeTangentToArcOfEllipseviaNewPoint( +bool makeTangentToArcOfEllipseviaNewPoint( Sketcher::SketchObject* Obj, const Part::GeomArcOfEllipse* aoe, const Part::Geometry* geom2, @@ -87,10 +89,11 @@ void makeTangentToArcOfEllipseviaNewPoint( /// geom2 => any of an arc of hyperbola, an arc of ellipse, a circle, or an arc (of circle) /// geoId1 => geoid of the arc of hyperbola /// geoId2 => geoid of geom2 +/// returns false if there was an exception /// NOTE: A command must be opened before calling this function, which this function /// commits or aborts as appropriate. The reason is for compatibility reasons with /// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp -void makeTangentToArcOfHyperbolaviaNewPoint( +bool makeTangentToArcOfHyperbolaviaNewPoint( Sketcher::SketchObject* Obj, const Part::GeomArcOfHyperbola* aoh, const Part::Geometry* geom2, @@ -104,11 +107,12 @@ void makeTangentToArcOfHyperbolaviaNewPoint( /// (of circle) /// geoId1 => geoid of the arc of parabola /// geoId2 => geoid of geom2 +/// returns false if there was an exception /// NOTE: A command must /// be opened before calling this function, which this function commits or aborts as appropriate. /// The reason is for compatibility reasons with other code e.g. "Autoconstraints" in /// DrawSketchHandler.cpp -void makeTangentToArcOfParabolaviaNewPoint( +bool makeTangentToArcOfParabolaviaNewPoint( Sketcher::SketchObject* Obj, const Part::GeomArcOfParabola* aop, const Part::Geometry* geom2, diff --git a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp index b76a96e12e..faec07ea0c 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp @@ -850,7 +850,7 @@ public: { Q_UNUSED(onSketchPos); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert knot")); + openCommand(QT_TRANSLATE_NOOP("Command", "Insert knot")); bool applied = false; boost::uuids::uuid bsplinetag = Obj->getGeometry(GeoId)->getTag(); @@ -915,10 +915,10 @@ public: } if (applied) { - Gui::Command::commitCommand(); + commitCommand(); } else { - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecomputeIfNotSolve(Obj); @@ -1162,7 +1162,7 @@ void CmdSketcherJoinCurves::activated(int iMsg) } } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Join Curves")); + openCommand(QT_TRANSLATE_NOOP("Command", "Join Curves")); bool applied = false; try { @@ -1190,10 +1190,10 @@ void CmdSketcherJoinCurves::activated(int iMsg) } if (applied) { - Gui::Command::commitCommand(); + commitCommand(); } else { - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecomputeIfNotSolve(Obj); diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index dc7df77809..08acf179f2 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -273,9 +273,9 @@ void CmdSketcherCut::activated(int iMsg) ReleaseHandler(doc); auto* vp = static_cast(doc->getInEdit()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cut in Sketcher")); + openCommand(QT_TRANSLATE_NOOP("Command", "Cut in Sketcher")); vp->deleteSelected(); - Gui::Command::commitCommand(); + commitCommand(); } } @@ -318,14 +318,14 @@ void CmdSketcherPaste::activated(int iMsg) } data = "objectStr = " + Gui::Command::getObjectCmd(obj) +"\n" + data; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Paste in Sketcher")); + openCommand(QT_TRANSLATE_NOOP("Command", "Paste in Sketcher")); Gui::Command::doCommand(Gui::Command::Doc, data.c_str()); obj->solve(true); vp->draw(false, false); - Gui::Command::commitCommand(); + commitCommand(); } bool CmdSketcherPaste::isActive() @@ -1065,7 +1065,7 @@ void CmdSketcherRestoreInternalAlignmentGeometry::activated(int iMsg) int currentgeoid = Obj->getHighestCurveIndex(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Exposing Internal Geometry")); + openCommand(QT_TRANSLATE_NOOP("Command", "Exposing Internal Geometry")); Gui::cmdAppObjectArgs(Obj, "exposeInternalGeometry(%d)", GeoId); int aftergeoid = Obj->getHighestCurveIndex(); @@ -1077,14 +1077,14 @@ void CmdSketcherRestoreInternalAlignmentGeometry::activated(int iMsg) catch (const Base::Exception& e) { Gui::NotifyUserError( Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(static_cast(Obj)); return; } - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecomputeIfNotSolve(static_cast(Obj)); } } @@ -1186,7 +1186,7 @@ static const char* cursor_createcopy[] = {"32 32 3 1", class DrawSketchHandlerCopy: public DrawSketchHandler { public: - DrawSketchHandlerCopy(string geoidlist, int origingeoid, Sketcher::PointPos originpos, + DrawSketchHandlerCopy(App::Document* doc, string geoidlist, int origingeoid, Sketcher::PointPos originpos, int nelements, SketcherCopy::Op op) : Mode(STATUS_SEEK_First) , snapMode(SnapMode::Free) @@ -1197,6 +1197,7 @@ public: , nElements(nelements) , Op(op) , EditCurve(2) + , doc(doc) {} ~DrawSketchHandlerCopy() override @@ -1267,7 +1268,7 @@ public: unsetCursor(); resetPositionText(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Copy/clone/move geometry")); + openCommand(QT_TRANSLATE_NOOP("Command", "Copy/clone/move geometry")); try { if (Op != SketcherCopy::Move) { @@ -1285,12 +1286,12 @@ public: vector.x, vector.y); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( sketchgui->getObject(), QT_TRANSLATE_NOOP("Notifications", "Error"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecomputeIfNotSolve( @@ -1324,6 +1325,7 @@ protected: SketcherCopy::Op Op; std::vector EditCurve; std::vector sugConstr1; + App::Document* doc; }; /*---- SketcherCopy definition ----*/ @@ -1453,9 +1455,9 @@ void SketcherCopy::activate(SketcherCopy::Op op) return; } */ - - ActivateHandler(getActiveGuiDocument(), - std::make_unique(geoIdList, LastGeoId, LastPointPos, geoids, op)); + Gui::Document* guidoc = getActiveGuiDocument(); + ActivateHandler(guidoc, + std::make_unique(guidoc->getDocument(), geoIdList, LastGeoId, LastPointPos, geoids, op)); } @@ -1851,7 +1853,7 @@ public: unsetCursor(); resetPositionText(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create copy of geometry")); + openCommand(QT_TRANSLATE_NOOP("Command", "Create copy of geometry")); try { Gui::cmdAppObjectArgs( @@ -1865,12 +1867,12 @@ public: Rows, (ConstraintSeparation ? "True" : "False"), (EqualVerticalHorizontalSpacing ? 1.0 : 0.5)); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( sketchgui, QT_TRANSLATE_NOOP("Notifications", "Error"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } // add auto constraints for the destination copy @@ -2101,14 +2103,14 @@ void CmdSketcherDeleteAllGeometry::activated(int iMsg) Sketcher::SketchObject* Obj = getSketchObject(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Delete all geometry")); + openCommand(QT_TRANSLATE_NOOP("Command", "Delete all geometry")); Gui::cmdAppObjectArgs(Obj, "deleteAllGeometry()"); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( Obj, QT_TRANSLATE_NOOP("Notifications", "Failed to delete all geometry"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( @@ -2165,16 +2167,16 @@ void CmdSketcherDeleteAllConstraints::activated(int iMsg) Sketcher::SketchObject* Obj = getSketchObject(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Delete all constraints")); + openCommand(QT_TRANSLATE_NOOP("Command", "Delete all constraints")); Gui::cmdAppObjectArgs(Obj, "deleteAllConstraints()"); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( Obj, QT_TRANSLATE_NOOP("Notifications", "Failed to delete all constraints"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( @@ -2299,15 +2301,15 @@ void CmdSketcherRemoveAxesAlignment::activated(int iMsg) geoIdList.insert(0, 1, '['); geoIdList.append(1, ']'); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Axes Alignment")); + openCommand(QT_TRANSLATE_NOOP("Command", "Remove Axes Alignment")); try { Gui::cmdAppObjectArgs(Obj, "removeAxesAlignment(%s)", geoIdList.c_str()); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError(Obj, QT_TRANSLATE_NOOP("Notifications", "Error"), e.what()); - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecomputeIfNotSolve(static_cast(Obj)); diff --git a/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp b/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp index 576f8914e9..c9766a4087 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp @@ -171,14 +171,10 @@ void CmdSketcherSwitchVirtualSpace::activated(int iMsg) int successful = SubNames.size(); // go through the selected subelements - for (std::vector::const_iterator it = SubNames.begin(); it != SubNames.end(); - ++it) { + for (const std::string& subName : SubNames) { // only handle constraints - if (it->size() > 10 && it->substr(0, 10) == "Constraint") { - int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it); - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Update constraint's virtual space") - ); + if (subName.size() > 10 && subName.substr(0, 10) == "Constraint") { + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(subName); try { Gui::cmdAppObjectArgs(Obj, "toggleVirtualSpace(%d)", ConstrId); } @@ -186,6 +182,9 @@ void CmdSketcherSwitchVirtualSpace::activated(int iMsg) successful--; } } + else { + successful--; // Not a failure, but not applicable + } } if (successful > 0) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchController.h b/src/Mod/Sketcher/Gui/DrawSketchController.h index ff43dc3daf..5d632f6bc1 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchController.h +++ b/src/Mod/Sketcher/Gui/DrawSketchController.h @@ -669,7 +669,11 @@ protected: { Gui::View3DInventorViewer* viewer = handler->getViewer(); - auto doc = Gui::Application::Instance->editDocument(); + auto doc = viewer->getDocument(); + if (!doc->getInEdit()) { + return; + } + auto placement = Base::Placement(doc->getEditingTransform()); onViewParameters.clear(); diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h index e3690428e7..114e64c127 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h @@ -915,18 +915,18 @@ protected: try { // add auto-constraints if (owncommand) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Auto-Constraints")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add Auto-Constraints")); } tryAddAutoConstraints(); if (owncommand) { - Gui::Command::commitCommand(); + commitCommand(); } } catch (const Base::PyException&) { if (owncommand) { - Gui::Command::abortCommand(); + abortCommand(); } } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h index f5674dadde..0809555e61 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h +++ b/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h @@ -233,7 +233,8 @@ public: { if constexpr (PFirstComboboxIsConstructionMethod == true) { - if (comboboxindex == WCombobox::FirstCombo && handler->ConstructionMethodsCount() > 1) { + if (handler && comboboxindex == WCombobox::FirstCombo + && handler->ConstructionMethodsCount() > 1) { handler->setConstructionMethod(static_cast(value)); } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index 382f163585..c2e9bd0f3e 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -740,6 +740,19 @@ void DrawSketchHandler::seekTangentAutoConstraint( } } +void DrawSketchHandler::openCommand(const std::string& name) +{ + currentTransactionID = sketchgui->getDocument()->openCommand(name.c_str()); +} +void DrawSketchHandler::commitCommand() +{ + Gui::Command::commitCommand(currentTransactionID); +} +void DrawSketchHandler::abortCommand() +{ + Gui::Command::abortCommand(currentTransactionID); +} + int DrawSketchHandler::seekAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Pos, @@ -777,152 +790,170 @@ void DrawSketchHandler::createAutoConstraints( return; // If Autoconstraints property is not set quit } - if (!autoConstrs.empty()) { + if (autoConstrs.empty()) { + return; + } + + if (createowncommand) { + // Open the Command + openCommand(QT_TRANSLATE_NOOP("Command", "Add Auto-Constraints")); + } + + // Iterate through constraints + for (auto& cstr : autoConstrs) { + int geoId2 = cstr.GeoId; + + switch (cstr.Type) { + case Sketcher::Coincident: { + if (posId1 == Sketcher::PointPos::none) { + continue; + } + // If the auto constraint has a point create a coincident otherwise it is an + // edge on a point + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ", + geoId1, + static_cast(posId1), + cstr.GeoId, + static_cast(cstr.PosId) + ); + } break; + case Sketcher::PointOnObject: { + Sketcher::PointPos posId2 = cstr.PosId; + if (posId1 == Sketcher::PointPos::none) { + // Auto constraining an edge so swap parameters + std::swap(geoId1, geoId2); + std::swap(posId1, posId2); + } + + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ", + geoId1, + static_cast(posId1), + geoId2 + ); + } break; + case Sketcher::Symmetric: { + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Symmetric',%d,1,%d,2,%d,%d)) ", + geoId2, + geoId2, + geoId1, + static_cast(posId1) + ); + } break; + // In special case of Horizontal/Vertical constraint, geoId2 is normally unused + // and should be 'Constraint::GeoUndef' However it can be used as a way to + // require the function to apply these constraints on another geometry In this + // case the caller as to set geoId2, then it will be used as target instead of + // geoId2 + case Sketcher::Horizontal: { + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", + geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 + ); + } break; + case Sketcher::Vertical: { + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Vertical',%d)) ", + geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 + ); + } break; + case Sketcher::Tangent: { + Sketcher::SketchObject* Obj = sketchgui->getSketchObject(); + + const Part::Geometry* geom1 = Obj->getGeometry(geoId1); + const Part::Geometry* geom2 = Obj->getGeometry(cstr.GeoId); + + // ellipse tangency support using construction elements (lines) + if (geom1 && geom2 + && (geom1->is() || geom2->is())) { + + if (!geom1->is()) { + std::swap(geoId1, geoId2); + } + + // geoId1 is the ellipse + geom1 = Obj->getGeometry(geoId1); + geom2 = Obj->getGeometry(geoId2); + + if (geom2->is() || geom2->is() + || geom2->is() || geom2->is()) { + // in all these cases an intermediate element is needed + bool success = makeTangentToEllipseviaNewPoint( + Obj, + static_cast(geom1), + geom2, + geoId1, + geoId2 + ); + + if (createowncommand) { + closeAndRecompute(currentTransactionID, !success, Obj); + } + else { + tryAutoRecompute(Obj); + } + + return; + } + } + + // arc of ellipse tangency support using external elements + if (geom1 && geom2 + && (geom1->is() || geom2->is())) { + + if (!geom1->is()) { + std::swap(geoId1, geoId2); + } + + // geoId1 is the arc of ellipse + geom1 = Obj->getGeometry(geoId1); + geom2 = Obj->getGeometry(geoId2); + + if (geom2->is() || geom2->is() + || geom2->is()) { + // in all these cases an intermediate element is needed + bool success = makeTangentToArcOfEllipseviaNewPoint( + Obj, + static_cast(geom1), + geom2, + geoId1, + geoId2 + ); + + if (createowncommand) { + closeAndRecompute(currentTransactionID, !success, Obj); + } + else { + tryAutoRecompute(Obj); + } + + return; + } + } + + Gui::cmdAppObjectArgs( + sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Tangent',%d, %d)) ", + geoId1, + cstr.GeoId + ); + } break; + default: + break; + } if (createowncommand) { - // Open the Command - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Auto-Constraints")); - } - - // Iterate through constraints - for (auto& cstr : autoConstrs) { - int geoId2 = cstr.GeoId; - - switch (cstr.Type) { - case Sketcher::Coincident: { - if (posId1 == Sketcher::PointPos::none) { - continue; - } - // If the auto constraint has a point create a coincident otherwise it is an - // edge on a point - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ", - geoId1, - static_cast(posId1), - cstr.GeoId, - static_cast(cstr.PosId) - ); - } break; - case Sketcher::PointOnObject: { - Sketcher::PointPos posId2 = cstr.PosId; - if (posId1 == Sketcher::PointPos::none) { - // Auto constraining an edge so swap parameters - std::swap(geoId1, geoId2); - std::swap(posId1, posId2); - } - - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ", - geoId1, - static_cast(posId1), - geoId2 - ); - } break; - case Sketcher::Symmetric: { - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('Symmetric',%d,1,%d,2,%d,%d)) ", - geoId2, - geoId2, - geoId1, - static_cast(posId1) - ); - } break; - // In special case of Horizontal/Vertical constraint, geoId2 is normally unused - // and should be 'Constraint::GeoUndef' However it can be used as a way to - // require the function to apply these constraints on another geometry In this - // case the caller as to set geoId2, then it will be used as target instead of - // geoId2 - case Sketcher::Horizontal: { - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", - geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 - ); - } break; - case Sketcher::Vertical: { - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('Vertical',%d)) ", - geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 - ); - } break; - case Sketcher::Tangent: { - Sketcher::SketchObject* Obj = sketchgui->getSketchObject(); - - const Part::Geometry* geom1 = Obj->getGeometry(geoId1); - const Part::Geometry* geom2 = Obj->getGeometry(cstr.GeoId); - - // ellipse tangency support using construction elements (lines) - if (geom1 && geom2 - && (geom1->is() || geom2->is())) { - - if (!geom1->is()) { - std::swap(geoId1, geoId2); - } - - // geoId1 is the ellipse - geom1 = Obj->getGeometry(geoId1); - geom2 = Obj->getGeometry(geoId2); - - if (geom2->is() || geom2->is() - || geom2->is() || geom2->is()) { - // in all these cases an intermediate element is needed - makeTangentToEllipseviaNewPoint( - Obj, - static_cast(geom1), - geom2, - geoId1, - geoId2 - ); - return; - } - } - - // arc of ellipse tangency support using external elements - if (geom1 && geom2 - && (geom1->is() - || geom2->is())) { - - if (!geom1->is()) { - std::swap(geoId1, geoId2); - } - - // geoId1 is the arc of ellipse - geom1 = Obj->getGeometry(geoId1); - geom2 = Obj->getGeometry(geoId2); - - if (geom2->is() || geom2->is() - || geom2->is()) { - // in all these cases an intermediate element is needed - makeTangentToArcOfEllipseviaNewPoint( - Obj, - static_cast(geom1), - geom2, - geoId1, - geoId2 - ); - return; - } - } - - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "addConstraint(Sketcher.Constraint('Tangent',%d, %d)) ", - geoId1, - cstr.GeoId - ); - } break; - default: - break; - } - - if (createowncommand) { - Gui::Command::commitCommand(); - } + commitCommand(); } + // Gui::Command::updateActive(); // There is already an recompute in each command + // creation, this is redundant. } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index fc122dce00..90ea6e23c7 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -334,6 +334,10 @@ protected: const Base::Vector2d& Dir ); + void openCommand(const std::string& name); + void commitCommand(); + void abortCommand(); + protected: /** * Returns constraints icons scaled to width. @@ -343,6 +347,7 @@ protected: ViewProviderSketch* sketchgui; QWidget* toolwidget; + int currentTransactionID {0}; }; diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h index c6b662a704..65a95b495d 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h @@ -282,18 +282,18 @@ private: try { createShape(false); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc")); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { /*Gui::NotifyError(sketchgui, QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Failed to add arc"));*/ - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h index b8f9b78f81..97564661da 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h @@ -323,7 +323,7 @@ public: int currentgeoid = getHighestCurveIndex(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of ellipse")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of ellipse")); Gui::cmdAppObjectArgs( sketchgui->getObject(), @@ -351,14 +351,14 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Failed to add arc of ellipse") ); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); return false; } - Gui::Command::commitCommand(); + commitCommand(); // add auto constraints for the center point if (!sugConstr1.empty()) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h index 21021139ae..393cc46fe9 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h @@ -318,7 +318,7 @@ public: try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of hyperbola")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of hyperbola")); // Add arc of hyperbola, point and constrain point as focus2. We add focus2 for it // to balance the intrinsic focus1, in order to balance out the intrinsic invisible @@ -349,14 +349,14 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Cannot create arc of hyperbola") ); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); return false; } - Gui::Command::commitCommand(); + commitCommand(); // add auto constraints for the center point if (!sugConstr1.empty()) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h index ec73b7da1c..733c877ceb 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h @@ -237,7 +237,7 @@ public: int currentgeoid = getHighestCurveIndex(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of Parabola")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of Parabola")); // Add arc of parabola Gui::cmdAppObjectArgs( @@ -264,14 +264,14 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Cannot create arc of parabola") ); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); return false; } - Gui::Command::commitCommand(); + commitCommand(); // add auto constraints for the focus point if (!sugConstr1.empty()) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h index 94c391edba..8909b8b678 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h @@ -203,11 +203,11 @@ private: try { createShape(false); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc slot")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc slot")); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -216,7 +216,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add arc slot") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h index 6aa2b9bcce..ac5e246e23 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h @@ -96,7 +96,7 @@ public: void activated() override { DrawSketchHandlerBSplineBase::activated(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); } private: @@ -130,7 +130,7 @@ private: { if (geoIds.size() == 1) { // if we just have one point and we can not close anything - Gui::Command::abortCommand(); + abortCommand(); return; } @@ -381,7 +381,7 @@ private: Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -389,7 +389,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Error creating B-spline") ); - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getSketchObject()); @@ -617,9 +617,9 @@ private: void onReset() override { - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getSketchObject()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); SplineDegree = 3; geoIds.clear(); @@ -676,7 +676,7 @@ private: ); // some commands might have already deleted some constraints/geometries but not // others - Gui::Command::abortCommand(); + abortCommand(); sketchgui->getSketchObject()->solve(); @@ -752,7 +752,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Error adding B-spline pole/knot") ); - Gui::Command::abortCommand(); + abortCommand(); sketchgui->getSketchObject()->solve(); @@ -764,9 +764,9 @@ private: void changeConstructionMethode() { // Restart the command - Gui::Command::abortCommand(); + abortCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getSketchObject()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add Sketch B-Spline")); // Restore keyboard focus after command restart if (Gui::Document* doc = Gui::Application::Instance->activeDocument()) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h index a3e20936c1..d5179608e1 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h @@ -179,7 +179,7 @@ public: if (obj->is() || sketchArchType == obj->getTypeId().getName()) { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create a carbon copy")); + openCommand(QT_TRANSLATE_NOOP("Command", "Create a carbon copy")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "carbonCopy(\"%s\",%s)", @@ -187,7 +187,7 @@ public: constructionModeAsBooleanText() ); - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); @@ -203,7 +203,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Failed to add carbon copy") ); - Gui::Command::abortCommand(); + abortCommand(); } return true; } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h index 6e0d47934b..3ff6231bb0 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h @@ -211,11 +211,11 @@ private: try { createShape(false); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle")); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -224,7 +224,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add circle") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h index 936d81c4dd..af319a8e9b 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h @@ -210,7 +210,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch ellipse")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch ellipse")); ellipseGeoId = getHighestCurveIndex() + 1; @@ -224,7 +224,7 @@ private: Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", ellipseGeoId); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -233,7 +233,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add ellipse") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h index c819138cd9..e305851fb3 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h @@ -290,7 +290,7 @@ public: } else if (Mode == STATUS_SEEK_Second) { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend edge")); + openCommand(QT_TRANSLATE_NOOP("Command", "Extend edge")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "extend(%d, %f, %d)\n", // GeoId, increment, PointPos @@ -299,7 +299,7 @@ public: ExtendFromStart ? static_cast(Sketcher::PointPos::start) : static_cast(Sketcher::PointPos::end) ); - Gui::Command::commitCommand(); + commitCommand(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher" @@ -344,7 +344,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Failed to extend edge") ); - Gui::Command::abortCommand(); + abortCommand(); } } else { // exit extension tool if user clicked on empty space diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h index 391dc86d52..d98bdbb304 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h @@ -181,7 +181,7 @@ public: || (subName.size() > 6 && subName.substr(0, 6) == "Vertex") || (subName.size() > 4 && subName.substr(0, 4) == "Face")) { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add external geometry")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add external geometry")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "addExternal(\"%s\",\"%s\", %s, %s)", @@ -191,7 +191,7 @@ public: intersection ? "True" : "False" ); - Gui::Command::commitCommand(); + commitCommand(); // adding external geometry does not require a solve() per se (the DoF is the // same), however a solve is required to update the amount of solver geometry, @@ -213,7 +213,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Failed to add external geometry") ); Gui::Selection().clearSelection(); - Gui::Command::abortCommand(); + abortCommand(); } return true; } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h index 433d187baf..4afcd93cf6 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h @@ -209,7 +209,7 @@ private: int filletGeoId = getHighestCurveIndex() + (isChamfer ? 2 : 1); // create fillet at point try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); + openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); Gui::cmdAppObjectArgs( obj, "fillet(%d,%d,%f,%s,%s,%s)", @@ -225,7 +225,7 @@ private: Gui::cmdAppObjectArgs(obj, "toggleConstruction(%d) ", filletGeoId); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( @@ -233,7 +233,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to create fillet"), e.what() ); - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecomputeIfNotSolve(obj); @@ -267,7 +267,7 @@ private: // create fillet between lines try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); + openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); Gui::cmdAppObjectArgs( obj, "fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s,%s)", @@ -282,7 +282,7 @@ private: preserveCorner ? "True" : "False", isChamfer ? "True" : "False" ); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::CADKernelError& e) { if (e.getTranslatable()) { @@ -293,12 +293,12 @@ private: ); } Gui::Selection().clearSelection(); - Gui::Command::abortCommand(); + abortCommand(); } catch (const Base::ValueError& e) { Gui::TranslatedUserError(sketchgui, tr("Value Error"), tr(e.getMessage().c_str())); Gui::Selection().clearSelection(); - Gui::Command::abortCommand(); + abortCommand(); } tryAutoRecompute(obj); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h index 1fe60e1504..8f8d58201f 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h @@ -131,11 +131,11 @@ private: try { createShape(false); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch line")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch line")); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -144,7 +144,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add line") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h index 1f941508da..a0bb89c781 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h @@ -457,9 +457,7 @@ public: // issue the geometry try { // open the transaction - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Add line to sketch polyline") - ); + openCommand(QT_TRANSLATE_NOOP("Command", "Add line to sketch polyline")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)", @@ -477,7 +475,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Error"), QT_TRANSLATE_NOOP("Notifications", "Failed to add line") ); - Gui::Command::abortCommand(); + abortCommand(); } firstsegment = false; @@ -489,9 +487,7 @@ public: } try { - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Add arc to sketch polyline") - ); + openCommand(QT_TRANSLATE_NOOP("Command", "Add arc to sketch polyline")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "addGeometry(Part.ArcOfCircle" @@ -512,7 +508,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Failed to add arc") ); - Gui::Command::abortCommand(); + abortCommand(); } firstsegment = false; @@ -582,7 +578,7 @@ public: ); firstsegment = true; } - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); } @@ -665,7 +661,7 @@ public: } } else { - Gui::Command::commitCommand(); + commitCommand(); // Add auto constraints if (!sugConstr1.empty()) { // this is relevant only to the very first point diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h index 201dfd87e2..a8b47c01ee 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h @@ -427,7 +427,7 @@ private: return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Offset")); + openCommand(QT_TRANSLATE_NOOP("Command", "Offset")); // Create geos Obj->addGeometry(std::move(geometriesToAdd)); @@ -442,7 +442,7 @@ private: makeOffsetConstraint(listOfOffsetGeoIds); } - Gui::Command::commitCommand(); + commitCommand(); } void jointOffsetCurves(std::vector& listOfOffsetGeoIds) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h index bf3f1d64af..5fcb5a2084 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h @@ -93,7 +93,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch point")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch point")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "addGeometry(Part.Point(App.Vector(%f,%f,0)), %s)", @@ -102,7 +102,7 @@ private: isConstructionMode() ? "True" : "False" ); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -111,7 +111,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add point") ); - Gui::Command::abortCommand(); + abortCommand(); } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h index 06fe5a03c1..56b3878ff2 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h @@ -108,7 +108,7 @@ private: { unsetCursor(); resetPositionText(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add polygon")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add polygon")); try { Gui::Command::doCommand( @@ -125,7 +125,7 @@ private: constructionModeAsBooleanText() ); - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecomputeIfNotSolve(sketchgui->getObject()); } @@ -136,7 +136,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add polygon") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h index 92f4a32988..6dbe9fe869 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h @@ -503,11 +503,11 @@ private: createShape(false); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch box")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch box")); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -516,7 +516,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add box") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h index e346acebf0..1632bb48f8 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h @@ -146,7 +146,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Rotate geometries")); + openCommand(QT_TRANSLATE_NOOP("Command", "Rotate geometries")); expressionHelper.storeOriginalExpressions(sketchgui->getSketchObject(), listOfGeoIds); @@ -166,7 +166,7 @@ private: deleteOriginalGeos(); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { e.reportException(); @@ -176,7 +176,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to rotate") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h index 4f0550be9a..6d5a5795cf 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h @@ -128,7 +128,7 @@ public: } try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scale geometries")); + openCommand(QT_TRANSLATE_NOOP("Command", "Scale geometries")); createShape(false); @@ -144,7 +144,7 @@ public: } scaleLabels(initialConstraintCount); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { e.reportException(); @@ -155,7 +155,7 @@ public: ); if (abortOnFail) { - Gui::Command::abortCommand(); + abortCommand(); } THROWM( Base::RuntimeError, diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h index 6c2f7bcc0d..f2b540a38b 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h @@ -176,13 +176,13 @@ private: firstCurve = getHighestCurveIndex() + 1; try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add slot")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add slot")); createShape(false); commandAddShapeGeometryAndConstraints(); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception&) { Gui::NotifyError( @@ -191,7 +191,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add slot") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h index 394c665cee..1487a7e915 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h @@ -157,7 +157,7 @@ public: if (GeoId >= 0) { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Split edge")); + openCommand(QT_TRANSLATE_NOOP("Command", "Split edge")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "split(%d,App.Vector(%f,%f,0))", @@ -165,7 +165,7 @@ public: onSketchPos.x, onSketchPos.y ); - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecompute(sketchgui->getObject()); } catch (const Base::Exception&) { @@ -175,7 +175,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Failed to add edge") ); - Gui::Command::abortCommand(); + abortCommand(); } } else { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h index 6066093647..439ddc583c 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h @@ -135,7 +135,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Symmetry geometries")); + openCommand(QT_TRANSLATE_NOOP("Command", "Symmetry geometries")); SketchObject* Obj = sketchgui->getSketchObject(); createSymConstraints = !deleteOriginal && createSymConstraints; @@ -146,7 +146,7 @@ private: } tryAutoRecomputeIfNotSolve(Obj); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { e.reportException(); @@ -156,7 +156,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to create symmetry") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerText.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerText.h index 1ca1ab89d9..7e162139e6 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerText.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerText.h @@ -128,7 +128,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch Text")); + openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch Text")); // Add the Handle Line Gui::cmdAppObjectArgs( @@ -174,7 +174,7 @@ private: constrBoolStr ); - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyError( @@ -183,7 +183,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to add text") ); - Gui::Command::abortCommand(); + abortCommand(); } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h index 8b58e4b24d..761c084a4c 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h @@ -119,7 +119,7 @@ private: void executeCommands() override { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Translate geometries")); + openCommand(QT_TRANSLATE_NOOP("Command", "Translate geometries")); expressionHelper.storeOriginalExpressions(sketchgui->getSketchObject(), listOfGeoIds); @@ -139,7 +139,7 @@ private: deleteOriginalGeos(); } - Gui::Command::commitCommand(); + commitCommand(); } catch (const Base::Exception& e) { e.reportException(); @@ -149,7 +149,7 @@ private: QT_TRANSLATE_NOOP("Notifications", "Failed to translate") ); - Gui::Command::abortCommand(); + abortCommand(); THROWM( Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h index 3452c358bd..52776345de 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h @@ -179,7 +179,7 @@ public: if (geo->isDerivedFrom() || geo->is() || geo->is() || geo->is()) { try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Trim edge")); + openCommand(QT_TRANSLATE_NOOP("Command", "Trim edge")); Gui::cmdAppObjectArgs( sketchgui->getObject(), "trim(%d,App.Vector(%f,%f,0))", @@ -187,7 +187,7 @@ public: onSketchPos.x, onSketchPos.y ); - Gui::Command::commitCommand(); + commitCommand(); tryAutoRecompute(sketchgui->getObject()); } catch (const Base::Exception&) { @@ -197,7 +197,7 @@ public: QT_TRANSLATE_NOOP("Notifications", "Failed to trim edge") ); - Gui::Command::abortCommand(); + abortCommand(); } } } diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index 8c1db0273a..7eb27bb2e8 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -75,18 +75,20 @@ bool SketcherGui::checkConstraintName(const Sketcher::SketchObject* sketch, std: } -EditDatumDialog::EditDatumDialog(ViewProviderSketch* vp, int ConstrNbr) +EditDatumDialog::EditDatumDialog(int tid, ViewProviderSketch* vp, int ConstrNbr) : ConstrNbr(ConstrNbr) , success(false) + , transactionID(tid) { sketch = vp->getSketchObject(); const std::vector& Constraints = sketch->Constraints.getValues(); Constr = Constraints[ConstrNbr]; } -EditDatumDialog::EditDatumDialog(Sketcher::SketchObject* pcSketch, int ConstrNbr) +EditDatumDialog::EditDatumDialog(int tid, Sketcher::SketchObject* pcSketch, int ConstrNbr) : sketch(pcSketch) , ConstrNbr(ConstrNbr) + , transactionID(tid) { const std::vector& Constraints = sketch->Constraints.getValues(); Constr = Constraints[ConstrNbr]; @@ -312,7 +314,7 @@ void EditDatumDialog::accepted() ); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(transactionID); // THIS IS A WORK-AROUND NOT TO DELAY 0.19 RELEASE // @@ -337,7 +339,7 @@ void EditDatumDialog::accepted() catch (const Base::Exception& e) { Gui::NotifyUserError(sketch, QT_TRANSLATE_NOOP("Notifications", "Value Error"), e.what()); - Gui::Command::abortCommand(); + Gui::Command::abortCommand(transactionID); if (sketch->noRecomputes) { // if setdatum failed, it is highly likely that solver // information is invalid. @@ -349,7 +351,7 @@ void EditDatumDialog::accepted() void EditDatumDialog::rejected() { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(transactionID); sketch->recomputeFeature(); } diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.h b/src/Mod/Sketcher/Gui/EditDatumDialog.h index c527e851aa..a3867ffa27 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.h +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.h @@ -46,8 +46,8 @@ class EditDatumDialog: public QObject Q_OBJECT public: - EditDatumDialog(ViewProviderSketch* vp, int ConstrNbr); - EditDatumDialog(Sketcher::SketchObject* pcSketch, int ConstrNbr); + EditDatumDialog(int tid, ViewProviderSketch* vp, int ConstrNbr); + EditDatumDialog(int tid, Sketcher::SketchObject* pcSketch, int ConstrNbr); ~EditDatumDialog() override; int exec(bool atCursor = true); @@ -59,6 +59,7 @@ private: int ConstrNbr; bool success; std::unique_ptr ui_ins_datum; + int transactionID; private Q_SLOTS: void accepted(); diff --git a/src/Mod/Sketcher/Gui/EditTextDialog.cpp b/src/Mod/Sketcher/Gui/EditTextDialog.cpp index 4d7c1d9901..4c48b2b4de 100644 --- a/src/Mod/Sketcher/Gui/EditTextDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditTextDialog.cpp @@ -102,7 +102,9 @@ void EditTextDialog::on_buttonBox_accepted() } // Open a command to make the change undo-able - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify sketch text constraint")); + sketchView->getDocument()->openCommand( + QT_TRANSLATE_NOOP("Command", "Modify sketch text constraint") + ); try { // Find if it was construction geometry to preserve that state @@ -127,10 +129,10 @@ void EditTextDialog::on_buttonBox_accepted() isConstruction ? "True" : "False" ); - Gui::Command::commitCommand(); + sketchView->getDocument()->commitCommand(); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + sketchView->getDocument()->abortCommand(); Base::Console().error("Failed to modify text constraint: %s\n", e.what()); } } diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index ccf48a033d..0a700fdfa9 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -796,14 +796,16 @@ void ConstraintView::swapNamedOfSelectedItems() ss << "DummyConstraint" << rand(); std::string tmpname = ss.str(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap constraint names")); + + + item1->sketch->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Swap constraint names")); Gui::cmdAppObjectArgs( item1->sketch, "renameConstraint(%d, u'%s')", item1->ConstraintNbr, tmpname.c_str()); Gui::cmdAppObjectArgs( item2->sketch, "renameConstraint(%d, u'%s')", item2->ConstraintNbr, escapedstr1.c_str()); Gui::cmdAppObjectArgs( item1->sketch, "renameConstraint(%d, u'%s')", item1->ConstraintNbr, escapedstr2.c_str()); - Gui::Command::commitCommand(); + item1->sketch->getDocument()->commitTransaction(); } /* Filter constraints list widget ----------------------*/ @@ -1270,9 +1272,9 @@ void TaskSketcherConstraints::onListWidgetConstraintsItemActivated(QListWidgetIt // if its the right constraint if (it->isDimensional()) { - EditDatumDialog* editDatumDialog = new EditDatumDialog(this->sketchView, it->ConstraintNbr); - editDatumDialog->exec(false); - delete editDatumDialog; + int tid = this->sketchView->getDocument()->openCommand( + QT_TRANSLATE_NOOP("Command", "Modify sketch constraints")); + EditDatumDialog(tid, this->sketchView, it->ConstraintNbr).exec(false); } else if (it->constraintType() == Sketcher::Text) { auto* editDialog = new EditTextDialog(this->sketchView, it->ConstraintNbr); @@ -1311,14 +1313,14 @@ void TaskSketcherConstraints::onListWidgetConstraintsItemChanged(QListWidgetItem newName = currConstraintName; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Rename sketch constraint")); + sketchView->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Rename sketch constraint")); try { Gui::cmdAppObjectArgs( sketch, "renameConstraint(%d, u'%s')", it->ConstraintNbr, newName.c_str()); - Gui::Command::commitCommand(); + sketchView->getDocument()->commitCommand(); } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + sketchView->getDocument()->abortCommand(); Gui::NotifyUserError( sketch, QT_TRANSLATE_NOOP("Notifications", "Value Error"), e.what()); @@ -1400,6 +1402,7 @@ void TaskSketcherConstraints::updateList() void TaskSketcherConstraints::onSelectionChanged(const Gui::SelectionChanges& msg) { assert(sketchView); + assert(sketchView->getSketchObject()); std::string temp; if (msg.Type == Gui::SelectionChanges::ClrSelection) { @@ -1656,17 +1659,16 @@ bool TaskSketcherConstraints::doSetVirtualSpace(const std::vector& constrId std::string constrIdList = stream.str(); - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Update constraint's virtual space")); + sketchView->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Update constraint's virtual space")); try { Gui::cmdAppObjectArgs(sketch, "setVirtualSpace(%s, %s)", constrIdList, isvirtualspace ? "True" : "False"); - Gui::Command::commitCommand(); - } + sketchView->getDocument()->commitCommand(); + } catch (const Base::Exception& e) { - Gui::Command::abortCommand(); + sketchView->getDocument()->abortCommand(); Gui::TranslatedUserError( sketch, tr("Error"), tr("Impossible to update visibility tracking:") + QLatin1String(" ") + QLatin1String(e.what())); diff --git a/src/Mod/Sketcher/Gui/Utils.cpp b/src/Mod/Sketcher/Gui/Utils.cpp index 66f852e1e7..b44dd413bd 100644 --- a/src/Mod/Sketcher/Gui/Utils.cpp +++ b/src/Mod/Sketcher/Gui/Utils.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -167,6 +168,21 @@ void SketcherGui::tryAutoRecomputeIfNotSolve(Sketcher::SketchObject* obj) } } +void SketcherGui::closeAndRecompute(int& tid, bool abort, Sketcher::SketchObject* Obj) +{ + if (tid == App::NullTransaction) { + tryAutoRecompute(Obj); + } + if (abort) { + Gui::Command::abortCommand(tid); + tryAutoRecompute(Obj); + } + else { + tryAutoRecompute(Obj); + Gui::Command::commitCommand(tid); + } +} + std::string SketcherGui::getStrippedPythonExceptionString(const Base::Exception& e) { std::string msg = e.what(); diff --git a/src/Mod/Sketcher/Gui/Utils.h b/src/Mod/Sketcher/Gui/Utils.h index 93538d4e21..eb3f28f857 100644 --- a/src/Mod/Sketcher/Gui/Utils.h +++ b/src/Mod/Sketcher/Gui/Utils.h @@ -98,6 +98,9 @@ bool tryAutoRecompute(Sketcher::SketchObject* obj, bool& autoremoveredundants); /// is not enabled, then it solves the SketchObject. void tryAutoRecomputeIfNotSolve(Sketcher::SketchObject* obj); +// Recomputes and closes a transaction, then resets the transaction id +void closeAndRecompute(int& tid, bool abort, Sketcher::SketchObject* Obj); + /// Release any currently-active handler for the document. /// Returns true if a handler was released, and false if not bool ReleaseHandler(Gui::Document* doc); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 4978ffc988..a5c80f39f7 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -741,7 +741,13 @@ void ViewProviderSketch::purgeHandler() Gui::Selection().clearSelection(); // ensure that we are in sketch only selection mode - auto* view = dynamic_cast(Gui::Application::Instance->editDocument()->getActiveView()); + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == this; + }); + Gui::View3DInventor* view = nullptr; + if (!editDoc) { + view = dynamic_cast(editDoc->getActiveView()); + } if(view) { Gui::View3DInventorViewer* viewer; @@ -925,12 +931,15 @@ void ViewProviderSketch::getProjectingLine(const SbVec2s& pnt, Base::Placement ViewProviderSketch::getEditingPlacement() const { - auto doc = Gui::Application::Instance->editDocument(); - if (!doc || doc->getInEdit() != this) + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getInEdit() == this; + }); + if (!editDoc) { return getSketchObject()->globalPlacement(); + } // TODO: won't work if there is scale. Hmm... what to do... - return Base::Placement(doc->getEditingTransform()); + return Base::Placement(editDoc->getEditingTransform()); } void ViewProviderSketch::getCoordsOnSketchPlane(const SbVec3f& point, const SbVec3f& normal, @@ -1352,7 +1361,6 @@ void ViewProviderSketch::editDoubleClicked() } if (textConstrId != -1) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify Text constraint")); EditTextDialog editTextDialog(this, textConstrId); editTextDialog.exec(); setSketchMode(STATUS_NONE); @@ -1379,14 +1387,12 @@ void ViewProviderSketch::editDoubleClicked() // if its the right constraint if (Constr->isDimensional()) { - Gui::Command::openCommand( + int tid = getDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Modify sketch constraints")); - EditDatumDialog editDatumDialog(this, id); + EditDatumDialog editDatumDialog(tid, this, id); editDatumDialog.exec(); } else if (Constr->Type == Sketcher::Text) { - Gui::Command::openCommand( - QT_TRANSLATE_NOOP("Command", "Modify Text constraint")); EditTextDialog editTextDialog(this, id); editTextDialog.exec(); } @@ -2294,11 +2300,11 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) bool handled = false; if (Mode == STATUS_SKETCH_UseHandler) { - App::AutoTransaction committer; handled = sketchHandler->onSelectionChanged(msg); } - if (handled) + if (handled) { return; + } std::string temp; if (msg.Type == Gui::SelectionChanges::ClrSelection) { @@ -3340,8 +3346,9 @@ void ViewProviderSketch::updateData(const App::Property* prop) { void ViewProviderSketch::slotSolverUpdate() { - if (!isInEditMode() ) + if (!isInEditMode()) { return; + } // At this point, we do not need to solve the Sketch // If we are adding geometry an update can be triggered before the sketch is actually @@ -3358,9 +3365,19 @@ void ViewProviderSketch::slotSolverUpdate() if (getSketchObject()->getExternalGeometryCount() + getSketchObject()->getHighestCurveIndex() + 1 == getSolvedSketch().getGeometrySize()) { - Gui::MDIView* mdi = Gui::Application::Instance->editDocument()->getActiveView(); - if (mdi->isDerivedFrom()) + + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == this; + }); + + Gui::MDIView* mdi = nullptr; + + if (editDoc) { + mdi = editDoc->getActiveView(); + } + if (mdi && mdi->isDerivedFrom()) { draw(false, true); + } signalConstraintsChanged(); } @@ -3548,8 +3565,9 @@ bool ViewProviderSketch::setEdit(int ModNum) // the task panel Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); TaskDlgEditSketch* sketchDlg = qobject_cast(dlg); - if (sketchDlg && sketchDlg->getSketchView() != this) + if (sketchDlg && sketchDlg->getSketchView() != this) { sketchDlg = nullptr;// another sketch left open its task panel + } if (dlg && !sketchDlg) { QMessageBox msgBox(Gui::getMainWindow()); msgBox.setText(tr("A dialog is already open in the task panel")); @@ -3557,10 +3575,12 @@ bool ViewProviderSketch::setEdit(int ModNum) msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) + if (ret == QMessageBox::Yes) { Gui::Control().closeDialog(); - else + } + else { return false; + } } Sketcher::SketchObject* sketch = getSketchObject(); @@ -3591,13 +3611,10 @@ bool ViewProviderSketch::setEdit(int ModNum) Gui::Selection().clearSelection(); Gui::Selection().rmvPreselect(); - this->attachSelection(); - auto gridnode = getGridNode(); Base::Placement plm = getEditingPlacement(); setGridOrientation(plm.getPosition(), plm.getRotation()); addNodeToRoot(gridnode); - setGridEnabled(true); // create the container for the additional edit data assert(!isInEditMode()); @@ -3606,14 +3623,21 @@ bool ViewProviderSketch::setEdit(int ModNum) editCoinManager = std::make_unique(*this); snapManager = std::make_unique(*this); - auto editDoc = Gui::Application::Instance->editDocument(); + App::DocumentObject* editObj = getSketchObject(); std::string editSubName; ViewProviderDocumentObject* editVp = nullptr; + + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == this; + }); + if (editDoc) { editDoc->getInEdit(&editVp, &editSubName); - if (editVp) + if (editVp) { editObj = editVp->getObject(); + } + setGridEnabled(dynamic_cast(editDoc->getActiveView())); } // visibility automation @@ -3633,7 +3657,7 @@ bool ViewProviderSketch::setEdit(int ModNum) " tv.show([ref[0] for ref in ActiveSketch.AttachmentSupport if not (ref[0].isDerivedFrom(\"App::Plane\") or ref[0].isDerivedFrom(\"App::LocalCoordinateSystem\"))])\n" "if ActiveSketch.ViewObject.ShowLinks:\n" " tv.show([ref[0] for ref in ActiveSketch.ExternalGeometry])\n" - "tv.sketchClipPlane(ActiveSketch, ActiveSketch.ViewObject.SectionView)\n" + "tv.sketchClipPlane(ActiveSketch, Gui.ActiveDocument, ActiveSketch.ViewObject.SectionView)\n" "tv.hide(ActiveSketch)\n" "del(tv)\n" "del(ActiveSketch)\n") @@ -3657,8 +3681,9 @@ bool ViewProviderSketch::setEdit(int ModNum) } // start the edit dialog - if (!sketchDlg) + if (!sketchDlg) { sketchDlg = new TaskDlgEditSketch(this); + } connectionToolWidget = sketchDlg->registerToolWidgetChanged(std::bind(&SketcherGui::ViewProviderSketch::slotToolWidgetChanged, this, sp::_1)); @@ -3707,14 +3732,46 @@ bool ViewProviderSketch::setEdit(int ModNum) listener = std::make_unique(this); Gui::getMainWindow()->installEventFilter(listener.get()); + if (editDoc && editDoc->isActive()) { + setupActiveAndInEdit(); + } + + return true; +} +void ViewProviderSketch::setupActiveAndInEdit() +{ + if (!listener) { + // intercept del key press from main app + listener = std::make_unique(this); + + Gui::getMainWindow()->installEventFilter(listener.get()); + } + attachSelection(); Workbench::enterEditMode(); // Give focus to the MDI so that keyboard events are caught after starting edit. // Else pressing ESC right after starting edit will not be caught to exit edit mode. ensureFocus(); +} +void ViewProviderSketch::unsetupActiveAndInEdit() +{ + if (listener) { + Gui::getMainWindow()->removeEventFilter(listener.get()); + listener.reset(); + } + detachSelection(); - return true; + Workbench::leaveEditMode(); +} +void ViewProviderSketch::setActive(bool active) +{ + bool inEdit = isInEditMode(); + if (active && inEdit) { + setupActiveAndInEdit(); + } else { + unsetupActiveAndInEdit(); + } } QString ViewProviderSketch::appendConflictMsg(const std::vector& conflicting) @@ -3854,7 +3911,7 @@ void ViewProviderSketch::unsetEdit(int ModNum) return PartGui::ViewProvider2DObject::unsetEdit(ModNum); } - setGridEnabled(false); + setGridEnabled(nullptr); auto gridnode = getGridNode(); pcRoot->removeChild(gridnode); @@ -3866,16 +3923,16 @@ void ViewProviderSketch::unsetEdit(int ModNum) } if (isInEditMode()) { - if (sketchHandler) + if (sketchHandler) { deactivateHandler(); + } editCoinManager = nullptr; snapManager = nullptr; preselection.reset(); selection.reset(); - this->detachSelection(); - App::AutoTransaction trans("Sketch recompute"); + App::AutoTransaction trans(getDocument()->getDocument(), "Sketch recompute"); try { // and update the sketch // getSketchObject()->getDocument()->recompute(); @@ -3896,6 +3953,7 @@ void ViewProviderSketch::unsetEdit(int ModNum) // when pressing ESC make sure to close the dialog Gui::Control().closeDialog(); + unsetupActiveAndInEdit(); // visibility automation try { @@ -3951,7 +4009,9 @@ void ViewProviderSketch::setEditViewer(Gui::View3DInventorViewer* viewer, int Mo } } - auto editDoc = Gui::Application::Instance->editDocument(); + auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { + return editdoc->getEditViewProvider() == this; + }); editDocName.clear(); if (editDoc) { ViewProviderDocumentObject* parent = nullptr; @@ -4096,7 +4156,7 @@ void ViewProviderSketch::onCameraChanged(SoCamera* cam) draw(); QString cmdStr = QStringLiteral("ActiveSketch.ViewObject.TempoVis.sketchClipPlane(" - "ActiveSketch, ActiveSketch.ViewObject.SectionView, %1)\n") + "ActiveSketch, Gui.ActiveDocument, ActiveSketch.ViewObject.SectionView, %1)\n") .arg(tmpFactor < 0 ? QLatin1String("True") : QLatin1String("False")); Base::Interpreter().runStringObject(cmdStr.toLatin1()); } diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index d8484e667d..09a112885c 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -663,6 +663,8 @@ public: void attach(App::DocumentObject*) override; void updateData(const App::Property*) override; + void setActive(bool active) override; + void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; /// is called when the Provider is in edit and a deletion request occurs bool onDelete(const std::vector&) override; @@ -861,6 +863,10 @@ private: OffsetMode offset = NoOffset ); void moveAngleConstraint(Sketcher::Constraint*, int constNum, const Base::Vector2d& toPos); + //@} + + void setupActiveAndInEdit(); + void unsetupActiveAndInEdit(); /** @name signals*/ //@{ diff --git a/src/Mod/Spreadsheet/Gui/DlgBindSheet.cpp b/src/Mod/Spreadsheet/Gui/DlgBindSheet.cpp index d895014c1d..f045a7a973 100644 --- a/src/Mod/Spreadsheet/Gui/DlgBindSheet.cpp +++ b/src/Mod/Spreadsheet/Gui/DlgBindSheet.cpp @@ -240,8 +240,7 @@ void DlgBindSheet::accept() } } } - - Gui::Command::openCommand("Bind cells"); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Bind cells")); commandActive = true; if (ui->checkBoxHREF->isChecked()) { @@ -274,7 +273,7 @@ void DlgBindSheet::accept() ); } Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); QDialog::accept(); } catch (Base::Exception& e) { @@ -285,7 +284,7 @@ void DlgBindSheet::accept() tr("Error:\n") + QString::fromUtf8(e.what()) ); if (commandActive) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } } @@ -295,7 +294,7 @@ void DlgBindSheet::onDiscard() try { std::string fromStart(ui->lineEditFromStart->text().trimmed().toLatin1().constData()); std::string fromEnd(ui->lineEditFromEnd->text().trimmed().toLatin1().constData()); - Gui::Command::openCommand("Unbind cells"); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Unbind cells")); Gui::cmdAppObjectArgs(sheet, "setExpression('.cells.Bind.%s.%s', None)", fromStart, fromEnd); Gui::cmdAppObjectArgs( sheet, @@ -304,13 +303,13 @@ void DlgBindSheet::onDiscard() fromEnd ); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); reject(); } catch (Base::Exception& e) { e.reportException(); QMessageBox::critical(this, tr("Unbind Cells"), QString::fromUtf8(e.what())); - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } diff --git a/src/Mod/Spreadsheet/Gui/DlgSheetConf.cpp b/src/Mod/Spreadsheet/Gui/DlgSheetConf.cpp index 70dca2c62b..5c84b1f41f 100644 --- a/src/Mod/Spreadsheet/Gui/DlgSheetConf.cpp +++ b/src/Mod/Spreadsheet/Gui/DlgSheetConf.cpp @@ -25,7 +25,6 @@ #include -#include #include #include #include @@ -202,7 +201,7 @@ void DlgSheetConf::accept() FC_THROWM(Base::RuntimeError, "Invalid property expression: " << expr->toString()); } - AutoTransaction guard("Setup conf table"); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Setup conf table")); commandActive = true; // unbind any previous binding @@ -284,14 +283,14 @@ void DlgSheetConf::accept() ); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); QDialog::accept(); } catch (Base::Exception& e) { e.reportException(); QMessageBox::critical(this, tr("Setup Configuration Table"), QString::fromUtf8(e.what())); if (commandActive) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } } @@ -307,7 +306,7 @@ void DlgSheetConf::onDiscard() Range range(from, to); - AutoTransaction guard("Unsetup conf table"); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Unsetup conf table")); commandActive = true; // unbind any previous binding @@ -341,14 +340,14 @@ void DlgSheetConf::onDiscard() } Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); QDialog::accept(); } catch (Base::Exception& e) { e.reportException(); QMessageBox::critical(this, tr("Unsetup Configuration Table"), QString::fromUtf8(e.what())); if (commandActive) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } } diff --git a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp index 9e47862958..9b69aa203e 100644 --- a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp +++ b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp @@ -266,7 +266,7 @@ PropertiesDialog::~PropertiesDialog() void PropertiesDialog::apply() { if (!ranges.empty()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Set cell properties")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Set cell properties")); std::vector::const_iterator i = ranges.begin(); bool changes = false; @@ -332,11 +332,11 @@ void PropertiesDialog::apply() } } if (changes) { - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } else { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } } diff --git a/src/Mod/Spreadsheet/Gui/SheetModel.cpp b/src/Mod/Spreadsheet/Gui/SheetModel.cpp index 0127ad7a51..3ef942764c 100644 --- a/src/Mod/Spreadsheet/Gui/SheetModel.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetModel.cpp @@ -626,18 +626,18 @@ void SheetModel::setCellData(QModelIndex index, QString str) { try { CellAddress address(index.row(), index.column()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit cell")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Edit cell")); // Because of possible complication of recursively escaped // characters, let's take a shortcut and bypass the command // interface for now. sheet->setContent(address, str.toUtf8().constData()); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } catch (const Base::Exception& e) { e.reportException(); - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); } } diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index cb853ce318..da0930aa19 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -220,7 +220,7 @@ SheetTableView::SheetTableView(QWidget* parent) void SheetTableView::onRecompute() { - Gui::Command::openCommand("Recompute Cells"); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Recompute Cells")); for (auto& range : selectedRanges()) { Gui::cmdAppObjectArgs( sheet, @@ -229,7 +229,7 @@ void SheetTableView::onRecompute() range.toCellString() ); } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); } void SheetTableView::onBind() @@ -320,55 +320,55 @@ QModelIndexList SheetTableView::selectedIndexesRaw() const void SheetTableView::insertRows(bool after) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Rows")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Insert Rows")); for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedRows(), Qt::Vertical)) { if (!model()->insertRows(after ? end + 1 : begin, end - begin + 1)) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); return; } } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } void SheetTableView::insertColumns(bool after) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Columns")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Insert Columns")); for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedColumns(), Qt::Horizontal)) { if (!model()->insertColumns(after ? end + 1 : begin, end - begin + 1)) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); return; } } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } void SheetTableView::removeRows() { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Rows")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Remove Rows")); for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedRows(), Qt::Vertical)) { if (!model()->removeRows(begin, end - begin + 1)) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); return; } } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } void SheetTableView::removeColumns() { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Columns")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Remove Columns")); for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedColumns(), Qt::Horizontal)) { if (!model()->removeColumns(begin, end - begin + 1)) { - Gui::Command::abortCommand(); + sheet->getDocument()->abortTransaction(); return; } } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } @@ -568,7 +568,7 @@ void SheetTableView::deleteSelection() QModelIndexList selection = selectionModel()->selectedIndexes(); if (!selection.empty()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Clear Cells")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Clear Cells")); std::vector ranges = selectedRanges(); std::vector::const_iterator i = ranges.begin(); @@ -581,7 +581,7 @@ void SheetTableView::deleteSelection() ); } Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); } } @@ -641,7 +641,7 @@ void SheetTableView::cutSelection() void SheetTableView::pasteClipboard() { - App::AutoTransaction committer("Paste Cell"); + App::AutoTransaction committer(sheet->getDocument()->openTransaction("Paste Cell")); try { bool copy = true; auto ranges = sheet->getCopyOrCutRange(copy); @@ -701,6 +701,7 @@ void SheetTableView::pasteClipboard() GetApplication().getActiveDocument()->recompute(); } catch (Base::Exception& e) { + committer.close(App::TransactionCloseMode::Abort); e.reportException(); QMessageBox::critical( Gui::getMainWindow(), diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index f835cda2b6..bcc3e09896 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -190,12 +190,12 @@ bool SheetView::onMsg(const char* pMsg) else if (strcmp("Std_Delete", pMsg) == 0) { std::vector ranges = selectedRanges(); if (sheet->hasCell(ranges)) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Clear Cells")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Clear Cells")); std::vector::const_iterator i = ranges.begin(); for (; i != ranges.end(); ++i) { FCMD_OBJ_CMD(sheet, "clear('" << i->rangeString() << "')"); } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } return true; diff --git a/src/Mod/Spreadsheet/Gui/Workbench.cpp b/src/Mod/Spreadsheet/Gui/Workbench.cpp index 8395679abd..b8f76b765e 100644 --- a/src/Mod/Spreadsheet/Gui/Workbench.cpp +++ b/src/Mod/Spreadsheet/Gui/Workbench.cpp @@ -30,6 +30,7 @@ #include "Mod/Spreadsheet/App/Sheet.h" #include "Mod/Spreadsheet/Gui/SpreadsheetView.h" +#include #include #include #include @@ -144,7 +145,7 @@ void WorkbenchHelper::setForegroundColor(const QColor& color) if (!ranges.empty()) { std::vector::const_iterator i = ranges.begin(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Set text color")); + sheet->getDocument()->openTransaction(QT_TRANSLATE_NOOP("Command", "Set text color")); for (; i != ranges.end(); ++i) { Gui::Command::doCommand( Gui::Command::Doc, @@ -156,7 +157,7 @@ void WorkbenchHelper::setForegroundColor(const QColor& color) color.blueF() ); } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } } @@ -179,7 +180,9 @@ void WorkbenchHelper::setBackgroundColor(const QColor& color) if (!ranges.empty()) { std::vector::const_iterator i = ranges.begin(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Set background color")); + sheet->getDocument()->openTransaction( + QT_TRANSLATE_NOOP("Command", "Set background color") + ); for (; i != ranges.end(); ++i) { Gui::Command::doCommand( Gui::Command::Doc, @@ -191,7 +194,7 @@ void WorkbenchHelper::setBackgroundColor(const QColor& color) color.blueF() ); } - Gui::Command::commitCommand(); + sheet->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } } diff --git a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp index 6de85d25be..fddc8fb16a 100644 --- a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp +++ b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -208,8 +209,8 @@ void BlendCurvePanel::bindProperties() void BlendCurvePanel::onFirstEdgeButton(bool checked) { if (checked) { - onStartSelection(); selectionMode = StartEdge; + setSelectionGate(); onUncheckSecondEdgeButton(); } else { @@ -220,8 +221,8 @@ void BlendCurvePanel::onFirstEdgeButton(bool checked) void BlendCurvePanel::onSecondEdgeButton(bool checked) { if (checked) { - onStartSelection(); selectionMode = EndEdge; + setSelectionGate(); onUncheckFirstEdgeButton(); } else { @@ -307,9 +308,9 @@ void BlendCurvePanel::onSecondEdgeSizeChanged(double value) fea->recomputeFeature(); } -void BlendCurvePanel::onStartSelection() +void BlendCurvePanel::setSelectionGate() { - if (vp.expired()) { + if (vp.expired() || selectionMode == None) { return; } @@ -405,22 +406,22 @@ void BlendCurvePanel::clearSelection() void BlendCurvePanel::checkOpenCommand() { - if (!Gui::Command::hasPendingCommand()) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit blending curve")); + if (!vp->getDocument()->hasPendingCommand()) { + vp->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Edit blending curve")); } } bool BlendCurvePanel::accept() { Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); - Gui::Command::commitCommand(); + vp->getDocument()->commitCommand(); Gui::Command::updateActive(); return true; } bool BlendCurvePanel::reject() { - Gui::Command::abortCommand(); + vp->getDocument()->abortCommand(); Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); Gui::Command::updateActive(); return true; @@ -448,3 +449,12 @@ bool TaskBlendCurve::reject() { return widget->reject(); } +void TaskBlendCurve::activate() +{ + widget->setSelectionGate(); + widget->attachSelection(); +} +void TaskBlendCurve::deactivate() +{ + widget->detachSelection(); +} diff --git a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h index 594af867d2..f47bc588f3 100644 --- a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h +++ b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h @@ -61,6 +61,8 @@ public: bool accept(); bool reject(); + void setSelectionGate(); + protected: void changeEvent(QEvent* e) override; void onSelectionChanged(const Gui::SelectionChanges& msg) override; @@ -87,7 +89,6 @@ private: void onFirstEdgeSizeChanged(double value); void onSecondEdgeSizeChanged(double value); - void onStartSelection(); void clearSelection(); void exitSelectionMode(); void setStartEdge(App::DocumentObject* obj, const std::string& subname); @@ -115,6 +116,8 @@ public: void open() override; bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Surface/Gui/TaskFilling.cpp b/src/Mod/Surface/Gui/TaskFilling.cpp index 9ddc115619..5023770b83 100644 --- a/src/Mod/Surface/Gui/TaskFilling.cpp +++ b/src/Mod/Surface/Gui/TaskFilling.cpp @@ -192,15 +192,13 @@ void ViewProviderFilling::highlightReferences(ShapeType type, const References& class FillingPanel::ShapeSelection: public Gui::SelectionFilterGate { public: - ShapeSelection(FillingPanel::SelectionMode& mode, Surface::Filling* editedObject) + ShapeSelection(FillingPanel::SelectionMode mode, Surface::Filling* editedObject) : Gui::SelectionFilterGate(nullPointer()) , mode(mode) , editedObject(editedObject) {} ~ShapeSelection() override - { - mode = FillingPanel::None; - } + {} /** * Allow the user to pick only edges. */ @@ -261,7 +259,7 @@ private: } private: - FillingPanel::SelectionMode& mode; + FillingPanel::SelectionMode mode; Surface::Filling* editedObject; }; @@ -329,6 +327,12 @@ void FillingPanel::appendButtons(Gui::ButtonGroup* buttonGroup) buttonGroup->addButton(ui->buttonEdgeAdd, int(SelectionMode::AppendEdge)); buttonGroup->addButton(ui->buttonEdgeRemove, int(SelectionMode::RemoveEdge)); } +void FillingPanel::setSelectionGate() +{ + if (selectionMode != None) { + Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject.get())); + } +} // stores object pointer, its old fill type and adjusts radio buttons according to it. void FillingPanel::setEditedObject(Surface::Filling* fea) @@ -441,10 +445,10 @@ void FillingPanel::clearSelection() void FillingPanel::checkOpenCommand() { - if (checkCommand && !Gui::Command::hasPendingCommand()) { + if (checkCommand && !editedObject->getDocument()->hasPendingTransaction()) { std::string Msg("Edit "); Msg += editedObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + editedObject->getDocument()->openTransaction(Msg.c_str()); checkCommand = false; } } @@ -555,17 +559,15 @@ void FillingPanel::onLineInitFaceNameTextChanged(const QString& text) void FillingPanel::onButtonInitFaceClicked() { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject.get())); selectionMode = InitFace; + setSelectionGate(); } void FillingPanel::onButtonEdgeAddToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject.get())); selectionMode = AppendEdge; + setSelectionGate(); } else if (selectionMode == AppendEdge) { exitSelectionMode(); @@ -575,9 +577,8 @@ void FillingPanel::onButtonEdgeAddToggled(bool checked) void FillingPanel::onButtonEdgeRemoveToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject.get())); selectionMode = RemoveEdge; + setSelectionGate(); } else if (selectionMode == RemoveEdge) { exitSelectionMode(); @@ -586,9 +587,9 @@ void FillingPanel::onButtonEdgeRemoveToggled(bool checked) void FillingPanel::onListBoundaryItemDoubleClicked(QListWidgetItem* item) { + selectionMode = None; Gui::Selection().clearSelection(); Gui::Selection().rmvSelectionGate(); - selectionMode = None; ui->comboBoxFaces->clear(); ui->comboBoxCont->clear(); @@ -674,8 +675,8 @@ void FillingPanel::onSelectionChanged(const Gui::SelectionChanges& msg) links.emplace_back(sel.getObject(), subList); this->vp->highlightReferences(ViewProviderFilling::Face, links, true); - Gui::Selection().rmvSelectionGate(); selectionMode = None; + Gui::Selection().rmvSelectionGate(); } else if (selectionMode == AppendEdge) { QListWidgetItem* item = new QListWidgetItem(ui->listBoundary); @@ -953,6 +954,7 @@ void FillingPanel::exitSelectionMode() // 'selectionMode' is passed by reference to the filter and changed when the filter is deleted Gui::Selection().clearSelection(); Gui::Selection().rmvSelectionGate(); + selectionMode = None; } // ---------------------------------------------------------------------------- @@ -963,6 +965,8 @@ TaskFilling::TaskFilling(ViewProviderFilling* vp, Surface::Filling* obj) buttonGroup = new Gui::ButtonGroup(this); buttonGroup->setExclusive(true); + editedObj = obj; + // first task box widget1 = new FillingPanel(vp, obj); widget1->appendButtons(buttonGroup); @@ -981,6 +985,10 @@ TaskFilling::TaskFilling(ViewProviderFilling* vp, Surface::Filling* obj) void TaskFilling::setEditedObject(Surface::Filling* obj) { + if (editedObj != nullptr && obj != editedObj) { + editedObj->getDocument()->commitTransaction(); + } + editedObj = obj; widget1->setEditedObject(obj); } @@ -1002,7 +1010,7 @@ bool TaskFilling::accept() if (ok) { widget2->reject(); widget3->reject(); - Gui::Command::commitCommand(); + editedObj->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); } @@ -1016,13 +1024,30 @@ bool TaskFilling::reject() if (ok) { widget2->reject(); widget3->reject(); - Gui::Command::abortCommand(); + editedObj->getDocument()->abortTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); } return ok; } +void TaskFilling::activate() +{ + widget1->attachSelection(); + widget1->setSelectionGate(); + + widget2->attachSelection(); + widget2->setSelectionGate(); + + widget3->attachSelection(); + widget3->setSelectionGate(); +} +void TaskFilling::deactivate() +{ + widget1->detachSelection(); + widget2->detachSelection(); + widget3->detachSelection(); +} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskFilling.h b/src/Mod/Surface/Gui/TaskFilling.h index fc63b5f384..144095fdfc 100644 --- a/src/Mod/Surface/Gui/TaskFilling.h +++ b/src/Mod/Surface/Gui/TaskFilling.h @@ -98,6 +98,8 @@ public: void setEditedObject(Surface::Filling* obj); void appendButtons(Gui::ButtonGroup*); + void setSelectionGate(); + protected: void changeEvent(QEvent* e) override; void onSelectionChanged(const Gui::SelectionChanges& msg) override; @@ -139,6 +141,8 @@ public: void closed() override; bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { @@ -150,6 +154,7 @@ private: FillingPanel* widget1; FillingEdgePanel* widget2; FillingVertexPanel* widget3; + Surface::Filling* editedObj; }; } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskFillingEdge.cpp b/src/Mod/Surface/Gui/TaskFillingEdge.cpp index 37264eac53..cb3efb98f5 100644 --- a/src/Mod/Surface/Gui/TaskFillingEdge.cpp +++ b/src/Mod/Surface/Gui/TaskFillingEdge.cpp @@ -56,15 +56,13 @@ namespace SurfaceGui class FillingEdgePanel::ShapeSelection: public Gui::SelectionFilterGate { public: - ShapeSelection(FillingEdgePanel::SelectionMode& mode, Surface::Filling* editedObject) + ShapeSelection(FillingEdgePanel::SelectionMode mode, Surface::Filling* editedObject) : Gui::SelectionFilterGate(nullPointer()) , mode(mode) , editedObject(editedObject) {} ~ShapeSelection() override - { - mode = FillingEdgePanel::None; - } + {} /** * Allow the user to pick only edges. */ @@ -115,7 +113,7 @@ private: } private: - FillingEdgePanel::SelectionMode& mode; + FillingEdgePanel::SelectionMode mode; Surface::Filling* editedObject; }; @@ -190,6 +188,12 @@ void FillingEdgePanel::appendButtons(Gui::ButtonGroup* buttonGroup) buttonGroup->addButton(ui->buttonUnboundEdgeAdd, int(SelectionMode::AppendEdge)); buttonGroup->addButton(ui->buttonUnboundEdgeRemove, int(SelectionMode::RemoveEdge)); } +void FillingEdgePanel::setSelectionGate() +{ + if (selectionMode != None) { + Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); + } +} // stores object pointer, its old fill type and adjusts radio buttons according to it. void FillingEdgePanel::setEditedObject(Surface::Filling* fea) @@ -281,10 +285,10 @@ void FillingEdgePanel::clearSelection() void FillingEdgePanel::checkOpenCommand() { - if (checkCommand && !Gui::Command::hasPendingCommand()) { + if (checkCommand && !editedObject->getDocument()->hasPendingTransaction()) { std::string Msg("Edit "); Msg += editedObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + editedObject->getDocument()->openTransaction(Msg.c_str()); checkCommand = false; } } @@ -354,9 +358,8 @@ bool FillingEdgePanel::reject() void FillingEdgePanel::onButtonUnboundEdgeAddToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); selectionMode = AppendEdge; + setSelectionGate(); } else if (selectionMode == AppendEdge) { exitSelectionMode(); @@ -366,9 +369,8 @@ void FillingEdgePanel::onButtonUnboundEdgeAddToggled(bool checked) void FillingEdgePanel::onButtonUnboundEdgeRemoveToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); selectionMode = RemoveEdge; + setSelectionGate(); } else if (selectionMode == RemoveEdge) { exitSelectionMode(); @@ -686,6 +688,7 @@ void FillingEdgePanel::exitSelectionMode() // 'selectionMode' is passed by reference to the filter and changed when the filter is deleted Gui::Selection().clearSelection(); Gui::Selection().rmvSelectionGate(); + selectionMode = None; } #include "moc_TaskFillingEdge.cpp" diff --git a/src/Mod/Surface/Gui/TaskFillingEdge.h b/src/Mod/Surface/Gui/TaskFillingEdge.h index ef42aeebf7..07d7b65881 100644 --- a/src/Mod/Surface/Gui/TaskFillingEdge.h +++ b/src/Mod/Surface/Gui/TaskFillingEdge.h @@ -76,6 +76,8 @@ public: void setEditedObject(Surface::Filling* obj); void appendButtons(Gui::ButtonGroup*); + void setSelectionGate(); + protected: void changeEvent(QEvent* e) override; void onSelectionChanged(const Gui::SelectionChanges& msg) override; diff --git a/src/Mod/Surface/Gui/TaskFillingVertex.cpp b/src/Mod/Surface/Gui/TaskFillingVertex.cpp index aa091484b1..b4fe21e6b4 100644 --- a/src/Mod/Surface/Gui/TaskFillingVertex.cpp +++ b/src/Mod/Surface/Gui/TaskFillingVertex.cpp @@ -49,15 +49,13 @@ namespace SurfaceGui class FillingVertexPanel::VertexSelection: public Gui::SelectionFilterGate { public: - VertexSelection(FillingVertexPanel::SelectionMode& mode, Surface::Filling* editedObject) + VertexSelection(FillingVertexPanel::SelectionMode mode, Surface::Filling* editedObject) : Gui::SelectionFilterGate(nullPointer()) , mode(mode) , editedObject(editedObject) {} ~VertexSelection() override - { - mode = FillingVertexPanel::None; - } + {} /** * Allow the user to pick only edges. */ @@ -108,7 +106,7 @@ private: } private: - FillingVertexPanel::SelectionMode& mode; + FillingVertexPanel::SelectionMode mode; Surface::Filling* editedObject; }; @@ -160,6 +158,12 @@ void FillingVertexPanel::appendButtons(Gui::ButtonGroup* buttonGroup) buttonGroup->addButton(ui->buttonVertexAdd, int(SelectionMode::AppendVertex)); buttonGroup->addButton(ui->buttonVertexRemove, int(SelectionMode::RemoveVertex)); } +void FillingVertexPanel::setSelectionGate() +{ + if (selectionMode != None) { + Gui::Selection().addSelectionGate(new VertexSelection(selectionMode, editedObject)); + } +} // stores object pointer, its old fill type and adjusts radio buttons according to it. void FillingVertexPanel::setEditedObject(Surface::Filling* obj) @@ -228,10 +232,10 @@ void FillingVertexPanel::clearSelection() void FillingVertexPanel::checkOpenCommand() { - if (checkCommand && !Gui::Command::hasPendingCommand()) { + if (checkCommand && !editedObject->getDocument()->hasPendingTransaction()) { std::string Msg("Edit "); Msg += editedObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + editedObject->getDocument()->openTransaction(Msg.c_str()); checkCommand = false; } } @@ -262,9 +266,8 @@ void FillingVertexPanel::slotDeletedObject(const Gui::ViewProviderDocumentObject void FillingVertexPanel::onButtonVertexAddToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new VertexSelection(selectionMode, editedObject)); selectionMode = AppendVertex; + setSelectionGate(); } else if (selectionMode == AppendVertex) { exitSelectionMode(); @@ -274,9 +277,8 @@ void FillingVertexPanel::onButtonVertexAddToggled(bool checked) void FillingVertexPanel::onButtonVertexRemoveToggled(bool checked) { if (checked) { - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new VertexSelection(selectionMode, editedObject)); selectionMode = RemoveVertex; + setSelectionGate(); } else if (selectionMode == RemoveVertex) { exitSelectionMode(); @@ -411,6 +413,7 @@ void FillingVertexPanel::exitSelectionMode() // 'selectionMode' is passed by reference to the filter and changed when the filter is deleted Gui::Selection().clearSelection(); Gui::Selection().rmvSelectionGate(); + selectionMode = None; } } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskFillingVertex.h b/src/Mod/Surface/Gui/TaskFillingVertex.h index c45992e95c..3c15a5ec01 100644 --- a/src/Mod/Surface/Gui/TaskFillingVertex.h +++ b/src/Mod/Surface/Gui/TaskFillingVertex.h @@ -73,6 +73,7 @@ public: void checkOpenCommand(); void setEditedObject(Surface::Filling* obj); void appendButtons(Gui::ButtonGroup*); + void setSelectionGate(); protected: void changeEvent(QEvent* e) override; diff --git a/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp b/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp index 8e261fed69..715d793532 100644 --- a/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp +++ b/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp @@ -96,7 +96,7 @@ void ViewProviderGeomFillSurface::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog - QTimer::singleShot(0, &Gui::Control(), &Gui::ControlSingleton::closeDialog); + QTimer::singleShot(0, [] { Gui::Control().closeDialog(); }); } else { PartGui::ViewProviderSpline::unsetEdit(ModNum); @@ -309,6 +309,12 @@ void GeomFillSurface::setEditedObject(Surface::GeomFillSurface* obj) attachDocument(Gui::Application::Instance->getDocument(doc)); } +void GeomFillSurface::setSelectionGate() +{ + if (selectionMode != None) { + Gui::Selection().addSelectionGate(new EdgeSelection(selectionMode == Append, editedObject)); + } +} void GeomFillSurface::changeEvent(QEvent* e) { @@ -339,10 +345,10 @@ void GeomFillSurface::clearSelection() void GeomFillSurface::checkOpenCommand() { - if (checkCommand && !Gui::Command::hasPendingCommand()) { + if (checkCommand && !editedObject->getDocument()->hasPendingTransaction()) { std::string Msg("Edit "); Msg += editedObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + editedObject->getDocument()->openTransaction(Msg.c_str()); checkCommand = false; } } @@ -403,8 +409,8 @@ bool GeomFillSurface::accept() this->vp->highlightReferences(false); - Gui::Command::commitCommand(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + editedObject->getDocument()->commitTransaction(); Gui::Command::updateActive(); return true; } @@ -415,7 +421,7 @@ bool GeomFillSurface::reject() selectionMode = None; Gui::Selection().rmvSelectionGate(); - Gui::Command::abortCommand(); + editedObject->getDocument()->abortTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); return true; @@ -455,7 +461,7 @@ void GeomFillSurface::onButtonEdgeAddToggled(bool checked) { if (checked) { selectionMode = Append; - Gui::Selection().addSelectionGate(new EdgeSelection(true, editedObject)); + setSelectionGate(); } else if (selectionMode == Append) { exitSelectionMode(); @@ -466,7 +472,7 @@ void GeomFillSurface::onButtonEdgeRemoveToggled(bool checked) { if (checked) { selectionMode = Remove; - Gui::Selection().addSelectionGate(new EdgeSelection(false, editedObject)); + setSelectionGate(); } else if (selectionMode == Remove) { exitSelectionMode(); @@ -681,6 +687,14 @@ bool TaskGeomFillSurface::reject() { return widget->reject(); } +void TaskGeomFillSurface::activate() +{ + widget->attachSelection(); +} +void TaskGeomFillSurface::deactivate() +{ + widget->detachSelection(); +} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskGeomFillSurface.h b/src/Mod/Surface/Gui/TaskGeomFillSurface.h index 676ff2423d..4c363b4c93 100644 --- a/src/Mod/Surface/Gui/TaskGeomFillSurface.h +++ b/src/Mod/Surface/Gui/TaskGeomFillSurface.h @@ -87,6 +87,7 @@ public: bool accept(); bool reject(); void setEditedObject(Surface::GeomFillSurface* obj); + void setSelectionGate(); protected: void changeEvent(QEvent* e) override; @@ -128,6 +129,8 @@ public: void open() override; bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Surface/Gui/TaskSections.cpp b/src/Mod/Surface/Gui/TaskSections.cpp index f7eef49b94..c27d0bfb2e 100644 --- a/src/Mod/Surface/Gui/TaskSections.cpp +++ b/src/Mod/Surface/Gui/TaskSections.cpp @@ -94,7 +94,7 @@ void ViewProviderSections::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog - QTimer::singleShot(0, &Gui::Control(), &Gui::ControlSingleton::closeDialog); + QTimer::singleShot(0, [] { Gui::Control().closeDialog(nullptr); }); } else { PartGui::ViewProviderSpline::unsetEdit(ModNum); @@ -194,15 +194,13 @@ void ViewProviderSections::highlightReferences(ShapeType type, const References& class SectionsPanel::ShapeSelection: public Gui::SelectionFilterGate { public: - ShapeSelection(SectionsPanel::SelectionMode& mode, Surface::Sections* editedObject) + ShapeSelection(SectionsPanel::SelectionMode mode, Surface::Sections* editedObject) : Gui::SelectionFilterGate(nullPointer()) , mode(mode) , editedObject(editedObject) {} ~ShapeSelection() override - { - mode = SectionsPanel::None; - } + {} /** * Allow the user to pick only edges. */ @@ -253,7 +251,7 @@ private: } private: - SectionsPanel::SelectionMode& mode; + SectionsPanel::SelectionMode mode; Surface::Sections* editedObject; }; @@ -338,6 +336,12 @@ void SectionsPanel::setEditedObject(Surface::Sections* fea) // attach this document observer attachDocument(Gui::Application::Instance->getDocument(doc)); } +void SectionsPanel::setSelectionGate() +{ + if (selectionMode != None) { + Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); + } +} void SectionsPanel::changeEvent(QEvent* e) { @@ -375,10 +379,10 @@ void SectionsPanel::clearSelection() void SectionsPanel::checkOpenCommand() { - if (checkCommand && !Gui::Command::hasPendingCommand()) { + if (checkCommand && !editedObject->getDocument()->hasPendingTransaction()) { std::string Msg("Edit "); Msg += editedObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + editedObject->getDocument()->openTransaction(Msg.c_str()); checkCommand = false; } } @@ -450,8 +454,7 @@ void SectionsPanel::onButtonEdgeAddToggled(bool checked) { if (checked) { selectionMode = AppendEdge; - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); + setSelectionGate(); } else if (selectionMode == AppendEdge) { exitSelectionMode(); @@ -462,8 +465,7 @@ void SectionsPanel::onButtonEdgeRemoveToggled(bool checked) { if (checked) { selectionMode = RemoveEdge; - // 'selectionMode' is passed by reference and changed when the filter is deleted - Gui::Selection().addSelectionGate(new ShapeSelection(selectionMode, editedObject)); + setSelectionGate(); } else if (selectionMode == RemoveEdge) { exitSelectionMode(); @@ -618,11 +620,13 @@ void SectionsPanel::exitSelectionMode() // 'selectionMode' is passed by reference to the filter and changed when the filter is deleted Gui::Selection().clearSelection(); Gui::Selection().rmvSelectionGate(); + selectionMode = None; } // ---------------------------------------------------------------------------- TaskSections::TaskSections(ViewProviderSections* vp, Surface::Sections* obj) + : editedObj(obj) { // first task box widget1 = new SectionsPanel(vp, obj); @@ -631,6 +635,10 @@ TaskSections::TaskSections(ViewProviderSections* vp, Surface::Sections* obj) void TaskSections::setEditedObject(Surface::Sections* obj) { + if (editedObj != nullptr && obj != editedObj) { + editedObj->getDocument()->commitTransaction(); + } + editedObj = obj; widget1->setEditedObject(obj); } @@ -643,7 +651,7 @@ bool TaskSections::accept() { bool ok = widget1->accept(); if (ok) { - Gui::Command::commitCommand(); + editedObj->getDocument()->commitTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); } @@ -655,13 +663,22 @@ bool TaskSections::reject() { bool ok = widget1->reject(); if (ok) { - Gui::Command::abortCommand(); + editedObj->getDocument()->abortTransaction(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); Gui::Command::updateActive(); } return ok; } +void TaskSections::activate() +{ + widget1->setSelectionGate(); + widget1->attachSelection(); +} +void TaskSections::deactivate() +{ + widget1->detachSelection(); +} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskSections.h b/src/Mod/Surface/Gui/TaskSections.h index 9053d886cd..6f22d97d96 100644 --- a/src/Mod/Surface/Gui/TaskSections.h +++ b/src/Mod/Surface/Gui/TaskSections.h @@ -94,6 +94,7 @@ public: bool accept(); bool reject(); void setEditedObject(Surface::Sections* obj); + void setSelectionGate(); protected: void changeEvent(QEvent* e) override; @@ -131,6 +132,8 @@ public: void open() override; bool accept() override; bool reject() override; + void activate() override; + void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { @@ -139,6 +142,7 @@ public: private: SectionsPanel* widget1; + Surface::Sections* editedObj; }; } // namespace SurfaceGui diff --git a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp index bad39a35e9..2cca25931f 100644 --- a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp +++ b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp @@ -330,7 +330,7 @@ void execMidpoints(Gui::Command* cmd) if (!dvp || selectedEdges.empty()) return; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add midpoint vertices")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Add midpoint vertices")); const TechDraw::BaseGeomPtrVector edges = dvp->getEdgeGeometry(); for (auto& s: selectedEdges) { @@ -343,7 +343,7 @@ void execMidpoints(Gui::Command* cmd) dvp->addCosmeticVertex(mid); } - Gui::Command::commitCommand(); + cmd->commitCommand(); dvp->recomputeFeature(); } @@ -357,7 +357,7 @@ void execQuadrants(Gui::Command* cmd) if (!dvp || selectedEdges.empty()) return; - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Quadrant vertices")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Add Quadrant vertices")); const TechDraw::BaseGeomPtrVector edges = dvp->getEdgeGeometry(); for (auto& s: selectedEdges) { @@ -372,7 +372,7 @@ void execQuadrants(Gui::Command* cmd) } } - Gui::Command::commitCommand(); + cmd->commitCommand(); dvp->recomputeFeature(); } diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index 01df60faa1..eb843057ff 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -209,6 +208,7 @@ public: , partFeat(pFeat) , dims({}) , blockRemoveSel(false) + , tid(0) { } ~TDHandlerDimension() @@ -241,7 +241,9 @@ public: mdi->setDimensionsSelectability(false); } Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert dimension")); + + tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Insert dimension")); + handleInitialSelection(); } @@ -252,7 +254,7 @@ public: mdi->setDimensionsSelectability(true); } Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection); - Gui::Command::abortCommand(); + Gui::Command::abortCommand(tid); } void keyPressEvent(QKeyEvent* event) override @@ -591,6 +593,8 @@ protected: bool blockRemoveSel; + int tid; + void handleInitialSelection() { if (initialSelection.size() == 0) { @@ -623,7 +627,7 @@ protected: // Ask for the value of datum dimensions ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw"); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); // Touch the parent feature so the dimension in tree view appears as a child partFeat->touch(true); @@ -1333,15 +1337,15 @@ protected: void restartCommand(const char* cstrName) { specialDimension = SpecialDimension::None; - Gui::Command::abortCommand(); - Gui::Command::openCommand(cstrName); + Gui::Command::abortCommand(tid); + tid = Gui::Command::openActiveDocumentCommand(cstrName); dims.clear(); } void clearAndRestartCommand() { - Gui::Command::abortCommand(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + Gui::Command::abortCommand(tid); + tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); specialDimension = SpecialDimension::None; mousePos = QPoint(0,0); clearRefVectors(); @@ -1380,7 +1384,6 @@ CmdTechDrawDimension::CmdTechDrawDimension() void CmdTechDrawDimension::activated(int iMsg) { Q_UNUSED(iMsg); - App::AutoTransaction::setEnable(false); ReferenceVector references2d; ReferenceVector references3d; @@ -1970,14 +1973,14 @@ void execExtent(Gui::Command* cmd, const std::string& dimType) } else if (dimType == "DistanceY") { commandString = QT_TRANSLATE_NOOP("Command", "Create Dimension DistanceY"); } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", commandString)); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", commandString)); bool result = _checkDrawViewPart(cmd); if (!result) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("No view of a part in selection.")); - Gui::Command::abortCommand(); + cmd->abortCommand(); return; } ReferenceVector references2d; @@ -1993,7 +1996,7 @@ void execExtent(Gui::Command* cmd, const std::string& dimType) QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Selection contains both 2D and 3D geometry")); - Gui::Command::abortCommand(); + cmd->abortCommand(); return; } } @@ -2019,7 +2022,7 @@ void execExtent(Gui::Command* cmd, const std::string& dimType) QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Cannot make 2D extent dimension from selection")); - Gui::Command::abortCommand(); + cmd->abortCommand(); return; } @@ -2035,7 +2038,7 @@ void execExtent(Gui::Command* cmd, const std::string& dimType) QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Cannot make 3D extent dimension from selection")); - Gui::Command::abortCommand(); + cmd->abortCommand(); return; } } @@ -2046,7 +2049,7 @@ void execExtent(Gui::Command* cmd, const std::string& dimType) else { DrawDimHelper::makeExtentDim3d(partFeat, dimType, references3d); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } //=========================================================================== @@ -2255,11 +2258,11 @@ void execDim(Gui::Command* cmd, std::string type, StringVector acceptableGeometr DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimType, ReferenceVector references2d, ReferenceVector references3d) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create dimension")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create dimension")); TechDraw::DrawViewDimension* dim = dimMaker(dvp, dimType, references2d, references3d); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); // Touch the parent feature so the dimension in tree view appears as a child dvp->touch(true); diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp index 0360914516..e7531ce714 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp @@ -125,7 +125,7 @@ void execInsertPrefixChar(Gui::Command* cmd, const std::string& prefixFormat) { } size_t prefixSize = prefixText.capacity(); - Gui::Command::openCommand(QObject::tr("Insert Prefix").toStdString().c_str()); + cmd->openCommand(QObject::tr("Insert Prefix").toStdString().c_str()); for (auto selected : selection) { auto object = selected.getObject(); if (object->isDerivedFrom()) { @@ -136,7 +136,7 @@ void execInsertPrefixChar(Gui::Command* cmd, const std::string& prefixFormat) { dim->FormatSpec.setValue(formatSpec); } } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionInsertDiameter) @@ -240,7 +240,7 @@ void execRemovePrefixChar(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Prefix")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Remove Prefix")); for (auto selected : selection) { auto object = selected.getObject(); @@ -255,7 +255,7 @@ void execRemovePrefixChar(Gui::Command* cmd) { } } } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionRemovePrefixChar) @@ -414,7 +414,7 @@ void execIncreaseDecreaseDecimal(Gui::Command* cmd, int delta) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Increase/Decrease Decimal")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Increase/Decrease Decimal")); std::string numStr; for (auto selected : selection) { auto object = selected.getObject(); @@ -433,7 +433,7 @@ void execIncreaseDecreaseDecimal(Gui::Command* cmd, int delta) { } } } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionIncreaseDecimal) @@ -599,7 +599,7 @@ void execPosHorizChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Position Horizontal Chain Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Position Horizontal Chain Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "DistanceX"); if (validDimension.empty()) { @@ -616,7 +616,7 @@ void execPosHorizChainDimension(Gui::Command* cmd) { Base::Vector3d p2 = pp.second(); dim->X.setValue((p1.x + p2.x) / 2.0); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionPosHorizChainDimension) @@ -660,7 +660,7 @@ void execPosVertChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Position Vert Chain Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Position Vert Chain Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "DistanceY"); if (validDimension.empty()) { @@ -678,7 +678,7 @@ void execPosVertChainDimension(Gui::Command* cmd) { Base::Vector3d p2 = pp.second(); dim->Y.setValue((p1.y + p2.y) / -2.0 + 0.5 * fontSize); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionPosVertChainDimension) @@ -722,7 +722,7 @@ void execPosObliqueChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Position Oblique Chain Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Position Oblique Chain Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "Distance"); if (validDimension.empty()) { @@ -745,7 +745,7 @@ void execPosObliqueChainDimension(Gui::Command* cmd) { dim->X.setValue(p3.x); dim->Y.setValue(p3.y); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionPosObliqueChainDimension) @@ -908,7 +908,7 @@ void execCascadeHorizDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Horizontal Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Horizontal Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "DistanceX"); if (validDimension.empty()) { @@ -929,7 +929,7 @@ void execCascadeHorizDimension(Gui::Command* cmd) { dim->X.setValue((p1.x + p2.x) / 2.0); yMaster = yMaster + dimDistance; } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCascadeHorizDimension) @@ -974,7 +974,7 @@ void execCascadeVertDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Vertical Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Vertical Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "DistanceY"); if (validDimension.empty()) { @@ -996,7 +996,7 @@ void execCascadeVertDimension(Gui::Command* cmd) { dim->Y.setValue((p1.y + p2.y) / -2.0 + 0.5 * fontSize); xMaster = xMaster + dimDistance; } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCascadeVertDimension) @@ -1041,7 +1041,7 @@ void execCascadeObliqueDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Oblique Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cascade Oblique Dimension")); std::vector validDimension; validDimension = _getDimensions(selection, "Distance"); if (validDimension.empty()) { @@ -1071,7 +1071,7 @@ void execCascadeObliqueDimension(Gui::Command* cmd) { dim->Y.setValue(p3.y); i = i + 1; } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCascadeObliqueDimension) @@ -1240,7 +1240,7 @@ void execCreateHorizChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Chain Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Chain Dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -1261,7 +1261,7 @@ void execCreateHorizChainDimension(Gui::Command* cmd) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateHorizChainDimension) @@ -1305,7 +1305,7 @@ void execCreateVertChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Vert Chain dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create Vert Chain dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -1327,7 +1327,7 @@ void execCreateVertChainDimension(Gui::Command* cmd) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateVertChainDimension) @@ -1370,7 +1370,7 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create oblique chain dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create oblique chain dimension")); std::vector refs; for (auto& subName : selection[0].getSubNames()) { @@ -1379,13 +1379,13 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { auto dims = makeObliqueChainDimension(refs); if(dims.empty()){ - Gui::Command::abortCommand(); + cmd->abortCommand(); } else { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } } @@ -1599,7 +1599,7 @@ void execCreateHorizCoordDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Coord Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Coord Dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -1626,7 +1626,7 @@ void execCreateHorizCoordDimension(Gui::Command* cmd) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateHorizCoordDimension) @@ -1667,7 +1667,7 @@ void execCreateVertCoordDimension(Gui::Command* cmd) { if (!_checkSelObjAndSubs(cmd, selection, objFeat, QT_TRANSLATE_NOOP("QObject","TechDraw Create Vertical Coord dimension"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create vert coord dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create vert coord dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -1695,7 +1695,7 @@ void execCreateVertCoordDimension(Gui::Command* cmd) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateVertCoordDimension) @@ -1737,7 +1737,7 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create oblique coord dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create oblique coord dimension")); std::vector refs; for (auto& subName : selection[0].getSubNames()) { @@ -1746,13 +1746,13 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { auto dims = makeObliqueCoordDimension(refs); if (dims.empty()) { - Gui::Command::abortCommand(); + cmd->abortCommand(); } else { objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } } @@ -1968,7 +1968,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Chamfer Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create Horizontal Chamfer Dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -1992,7 +1992,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { objFeat->requestPaint(); cmd->getSelection().clearSelection(); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateHorizChamferDimension) @@ -2034,7 +2034,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Vert Chamfer Dimension")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Create Vert Chamfer Dimension")); const std::vector subNames = selection[0].getSubNames(); std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); @@ -2058,7 +2058,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { objFeat->requestPaint(); cmd->getSelection().clearSelection(); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCreateVertChamferDimension) @@ -2209,7 +2209,7 @@ void CmdTechDrawExtensionCreateLengthArc::activated(int iMsg) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Arc Length Dimension")); + openCommand(QT_TRANSLATE_NOOP("Command", "Create Arc Length Dimension")); ReferenceEntry ref(objFeat, selection[0].getSubNames()[0]); TechDraw::DrawViewDimension* dim = makeArcLengthDimension(ref); @@ -2217,10 +2217,10 @@ void CmdTechDrawExtensionCreateLengthArc::activated(int iMsg) { if (dim) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + commitCommand(); } else { - Gui::Command::abortCommand(); + abortCommand(); } } diff --git a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp index 3f18e61ccc..89b60db6dc 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp @@ -123,7 +123,7 @@ void execHoleCircle(Gui::Command* cmd) QObject::tr("Fewer than three circles selected")); return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Bolt circle centerlines")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Bolt circle centerlines")); // make the bolt hole circle from 3 scaled and rotated points Base::Vector3d bigCenter = @@ -157,7 +157,7 @@ void execHoleCircle(Gui::Command* cmd) cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } }// namespace TechDrawGui @@ -201,7 +201,7 @@ void execCircleCenterLines(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw circle centerlines"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Circle Centerlines")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Circle Centerlines")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); @@ -236,7 +236,7 @@ void execCircleCenterLines(Gui::Command* cmd) Gui::Selection().clearCompleteSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCircleCenterLines) @@ -375,7 +375,7 @@ void execThreadHoleSide(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Hole Side"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Hole Side")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Hole Side")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { _createThreadLines(SubNames, objFeat, ThreadFactor); @@ -383,7 +383,7 @@ void execThreadHoleSide(Gui::Command* cmd) cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadHoleSide) @@ -426,7 +426,7 @@ void execThreadBoltSide(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Bolt Side"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Side")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Side")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { _createThreadLines(SubNames, objFeat, ThreadFactor); @@ -434,7 +434,7 @@ void execThreadBoltSide(Gui::Command* cmd) cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadBoltSide) @@ -478,7 +478,7 @@ void execThreadHoleBottom(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Hole Bottom"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic thread hole bottom")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic thread hole bottom")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { _createThreadCircle(Name, objFeat, ThreadFactor); @@ -486,7 +486,7 @@ void execThreadHoleBottom(Gui::Command* cmd) cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadHoleBottom) @@ -529,7 +529,7 @@ void execThreadBoltBottom(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Bolt Bottom"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Bottom")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Bottom")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { _createThreadCircle(Name, objFeat, ThreadFactor); @@ -537,7 +537,7 @@ void execThreadBoltBottom(Gui::Command* cmd) cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadBoltBottom) @@ -755,7 +755,7 @@ void CmdTechDrawExtensionChangeLineAttributes::activated(int iMsg) if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw change line attributes"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Change line attributes")); + openCommand(QT_TRANSLATE_NOOP("Command", "Change line attributes")); const std::vector subNames = selection[0].getSubNames(); for (const std::string& name : subNames) { int num = DrawUtil::getIndexFromName(name); @@ -776,7 +776,7 @@ void CmdTechDrawExtensionChangeLineAttributes::activated(int iMsg) getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + commitCommand(); } bool CmdTechDrawExtensionChangeLineAttributes::isActive() @@ -814,7 +814,7 @@ void CmdTechDrawExtensionVertexAtIntersection::activated(int iMsg) if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic intersection vertices"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic intersection vertices")); + openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic intersection vertices")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { std::string GeoType1 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]); @@ -836,7 +836,7 @@ void CmdTechDrawExtensionVertexAtIntersection::activated(int iMsg) getSelection().clearSelection(); objFeat->refreshCVGeoms(); objFeat->requestPaint(); - Gui::Command::commitCommand(); + commitCommand(); } bool CmdTechDrawExtensionVertexAtIntersection::isActive() @@ -1011,7 +1011,7 @@ void execDrawCosmArc(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic arc"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic arc")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic arc")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); @@ -1035,7 +1035,7 @@ void execDrawCosmArc(Gui::Command* cmd) objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } } @@ -1080,7 +1080,7 @@ void execDrawCosmCircle(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic circle"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); @@ -1095,7 +1095,7 @@ void execDrawCosmCircle(Gui::Command* cmd) objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } } @@ -1139,7 +1139,7 @@ void execDrawCosmCircle3Points(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Cosmetic Circle 3 Points"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle 3 Points")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle 3 Points")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); @@ -1157,7 +1157,7 @@ void execDrawCosmCircle3Points(Gui::Command* cmd) objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + cmd->commitCommand(); } } @@ -1328,7 +1328,7 @@ void execLineParallelPerpendicular(Gui::Command* cmd, bool isParallel) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Cosmetic Line Parallel/Perpendicular"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Line Parallel/Perpendicular")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Line Parallel/Perpendicular")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { std::string GeoType1 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]); @@ -1372,7 +1372,7 @@ void execLineParallelPerpendicular(Gui::Command* cmd, bool isParallel) objFeat->requestPaint(); cmd->getSelection().clearSelection(); } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionLineParallel) @@ -1560,7 +1560,7 @@ void CmdTechDrawExtensionLockUnlockView::activated(int iMsg) return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Lock/Unlock View")); + openCommand(QT_TRANSLATE_NOOP("Command", "Lock/Unlock View")); for (auto& sel : selection) { auto* obj = static_cast(sel.getObject()); @@ -1570,7 +1570,7 @@ void CmdTechDrawExtensionLockUnlockView::activated(int iMsg) obj->LockPosition.setValue(lockPosition); } } - Gui::Command::commitCommand(); + commitCommand(); } bool CmdTechDrawExtensionLockUnlockView::isActive() @@ -1592,7 +1592,7 @@ void execExtendShortenLine(Gui::Command* cmd, bool extend) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Extend/Shorten Line"))) { return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend/shorten line")); + cmd->openCommand(QT_TRANSLATE_NOOP("Command", "Extend/shorten line")); const std::vector subNames = selection[0].getSubNames(); if (!subNames.empty()) { std::string name = subNames[0]; @@ -1664,7 +1664,7 @@ void execExtendShortenLine(Gui::Command* cmd, bool extend) } } } - Gui::Command::commitCommand(); + cmd->commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionExtendLine) @@ -1888,7 +1888,7 @@ void CmdTechDrawExtensionAreaAnnotation::activated(int iMsg) } // if area calculation was successful, start the command - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Face Area")); + openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Face Area")); // at first we create the balloon std::string balloonName = _createBalloon(this, objFeat); TechDraw::DrawViewBalloon* balloon = nullptr; @@ -1940,7 +1940,7 @@ void CmdTechDrawExtensionAreaAnnotation::activated(int iMsg) viewProvider->LineVisible.setValue(false); viewProvider->Color.setValue(Base::Color(1.0, 0.0, 0.0)); } - Gui::Command::commitCommand(); + commitCommand(); objFeat->touch(true); Gui::Command::updateActive(); } @@ -2014,7 +2014,7 @@ void CmdTechDrawExtensionArcLengthAnnotation::activated(int iMsg) } // We have calculated the length, let's start the command - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Edge Length")); + openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Edge Length")); // First we need to create the balloon std::string balloonName = _createBalloon(this, objFeat); @@ -2078,7 +2078,7 @@ void CmdTechDrawExtensionArcLengthAnnotation::activated(int iMsg) } // Close the command and update the view - Gui::Command::commitCommand(); + commitCommand(); objFeat->touch(true); Gui::Command::updateActive(); } diff --git a/src/Mod/TechDraw/Gui/PagePrinter.cpp b/src/Mod/TechDraw/Gui/PagePrinter.cpp index da0909d69c..670caecc14 100644 --- a/src/Mod/TechDraw/Gui/PagePrinter.cpp +++ b/src/Mod/TechDraw/Gui/PagePrinter.cpp @@ -441,13 +441,13 @@ void PagePrinter::saveDXF(ViewProviderPage* vpPage, const std::string& inFileNam auto filespec = Base::Tools::escapeEncodeFilename(inFileName); filespec = DU::cleanFilespecBackslash(filespec); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Save page to DXF")); + vpPage->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Save page to DXF")); Gui::Command::doCommand(Gui::Command::Doc, "import TechDraw"); Gui::Command::doCommand(Gui::Command::Doc, "TechDraw.writeDXFPage(App.activeDocument().%s, u\"%s\")", PageName.c_str(), filespec.c_str()); - Gui::Command::commitCommand(); + vpPage->getDocument()->commitCommand(); } // this one is somewhat superfluous (just a redirect). diff --git a/src/Mod/TechDraw/Gui/QGIRichAnno.cpp b/src/Mod/TechDraw/Gui/QGIRichAnno.cpp index b8dd5c6ca7..c781d29e19 100644 --- a/src/Mod/TechDraw/Gui/QGIRichAnno.cpp +++ b/src/Mod/TechDraw/Gui/QGIRichAnno.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -395,7 +396,7 @@ void QGIRichAnno::mouseMoveEvent(QGraphicsSceneMouseEvent* event) if (!m_isDraggingMidResize) { // First actual move during this resize op if (!Gui::Control().activeDialog()) { - Gui::Command::openCommand( + m_tid = Gui::Command::openActiveDocumentCommand( QObject::tr("Resize Rich Annotation").toStdString().c_str()); } m_transactionOpen = true; @@ -495,7 +496,7 @@ void QGIRichAnno::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) // Only commit if actual dragging (and thus property changes) occurred. // m_isDraggingMidResize flag indicates if mouseMoveEvent was processed. if (!Gui::Control().activeDialog()) { - Gui::Command::commitCommand(); + Gui::Command::commitCommand(m_tid); } m_transactionOpen = false; widthChanged(); @@ -531,7 +532,7 @@ void QGIRichAnno::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { // To avoid partial changes, might need to revert or just abort. // For simplicity, we commit if open. A better way would be to store original values and revert. if (!Gui::Control().activeDialog()) { - Gui::Command::commitCommand(); + Gui::Command::commitCommand(m_tid); } m_transactionOpen = false; } diff --git a/src/Mod/TechDraw/Gui/QGIRichAnno.h b/src/Mod/TechDraw/Gui/QGIRichAnno.h index 29bde5e9fa..93641220e2 100644 --- a/src/Mod/TechDraw/Gui/QGIRichAnno.h +++ b/src/Mod/TechDraw/Gui/QGIRichAnno.h @@ -150,6 +150,8 @@ protected: double m_textScaleFactor; double m_lastGoodWidthScene; + int m_tid { 0 }; + private Q_SLOTS: void onContentsChanged(); }; diff --git a/src/Mod/TechDraw/Gui/QGIView.cpp b/src/Mod/TechDraw/Gui/QGIView.cpp index 1b48678b7f..729752a0b3 100644 --- a/src/Mod/TechDraw/Gui/QGIView.cpp +++ b/src/Mod/TechDraw/Gui/QGIView.cpp @@ -244,9 +244,9 @@ void QGIView::dragFinished() } bool ownTransaction = (viewObj->getDocument()->getTransactionID(true) == 0); - + if (ownTransaction) { - Gui::Command::openCommand("Drag view"); + viewObj->getDocument()->openTransaction("Drag view"); } // tell the feature that we have moved Gui::ViewProvider *vp = getViewProvider(viewObj); @@ -264,7 +264,7 @@ void QGIView::dragFinished() snapping = false; } if (ownTransaction) { - Gui::Command::commitCommand(); + viewObj->getDocument()->commitTransaction(); } } diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp index 7f26c6dd2e..270b9f68ee 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -503,7 +505,9 @@ void QGIViewBalloon::balloonLabelDragFinished() //set feature position (x, y) from graphic position double x = Rez::appX(balloonLabel->getCenterX() / scale); double y = Rez::appX(balloonLabel->getCenterY() / scale); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Drag Balloon")); + + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Drag Balloon")); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", dvb->getNameInDocument(), x); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", @@ -527,7 +531,7 @@ void QGIViewBalloon::balloonLabelDragFinished() dvb->getNameInDocument(), originAppUnrotated.y); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); m_dragInProgress = false; m_originDragged = false; diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index a275c821be..8001f94263 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -37,7 +37,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -303,12 +305,14 @@ void QGIViewDimension::datumLabelDragFinished() } double x = Rez::appX(datumLabel->X()), y = Rez::appX(datumLabel->Y()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Drag Dimension")); + + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Drag Dimension")); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", dim->getNameInDocument(), x); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", dim->getNameInDocument(), -y); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } //this is for formatting and finding centers, not display diff --git a/src/Mod/TechDraw/Gui/QGSPage.cpp b/src/Mod/TechDraw/Gui/QGSPage.cpp index ba60876331..24f226624e 100644 --- a/src/Mod/TechDraw/Gui/QGSPage.cpp +++ b/src/Mod/TechDraw/Gui/QGSPage.cpp @@ -607,7 +607,8 @@ void QGSPage::createBalloon(QPointF origin, DrawView* parent) std::string featName = getDrawPage()->getDocument()->getUniqueObjectName("Balloon"); std::string pageName = getDrawPage()->getNameInDocument(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Balloon")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Balloon")); + Command::doCommand(Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewBalloon', '%s')", featName.c_str()); @@ -643,7 +644,7 @@ void QGSPage::createBalloon(QPointF origin, DrawView* parent) Command::doCommand(Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), featName.c_str()); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); // Touch the parent feature so the balloon in tree view appears as a child parent->touch(true); diff --git a/src/Mod/TechDraw/Gui/TaskActiveView.cpp b/src/Mod/TechDraw/Gui/TaskActiveView.cpp index b9af1c1bc3..1bd8a1f3d9 100644 --- a/src/Mod/TechDraw/Gui/TaskActiveView.cpp +++ b/src/Mod/TechDraw/Gui/TaskActiveView.cpp @@ -56,8 +56,13 @@ constexpr int SXGAHeight{1024}; // ctor for creation TaskActiveView::TaskActiveView(TechDraw::DrawPage* pageFeat) - : ui(new Ui_TaskActiveView), m_pageFeat(pageFeat), m_imageFeat(nullptr), - m_previewImageFeat(nullptr), m_btnOK(nullptr), m_btnCancel(nullptr) + : ui(new Ui_TaskActiveView) + , m_pageFeat(pageFeat) + , m_imageFeat(nullptr) + , m_previewImageFeat(nullptr) + , m_btnOK(nullptr) + , m_btnCancel(nullptr) + , m_tid(0) { ui->setupUi(this); @@ -68,11 +73,11 @@ TaskActiveView::TaskActiveView(TechDraw::DrawPage* pageFeat) connect(ui->cbCrop, &QCheckBox::clicked, this, &TaskActiveView::onCropChanged); // For live preview - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create ActiveView")); + m_tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create ActiveView")); m_previewImageFeat = createActiveView(); if (!m_previewImageFeat) { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(m_tid); this->setEnabled(false); return; } @@ -90,14 +95,14 @@ TaskActiveView::TaskActiveView(TechDraw::DrawPage* pageFeat) TaskActiveView::~TaskActiveView() { if (m_previewImageFeat) { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(m_tid); } } bool TaskActiveView::accept() { if (m_previewImageFeat) { - Gui::Command::commitCommand(); + Gui::Command::commitCommand(m_tid); m_imageFeat = m_previewImageFeat; m_previewImageFeat = nullptr; } @@ -108,7 +113,7 @@ bool TaskActiveView::accept() bool TaskActiveView::reject() { if (m_previewImageFeat) { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(m_tid); m_previewImageFeat = nullptr; } Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); diff --git a/src/Mod/TechDraw/Gui/TaskActiveView.h b/src/Mod/TechDraw/Gui/TaskActiveView.h index 9c43df1464..fc16787f4d 100644 --- a/src/Mod/TechDraw/Gui/TaskActiveView.h +++ b/src/Mod/TechDraw/Gui/TaskActiveView.h @@ -84,6 +84,7 @@ private: QPushButton* m_btnOK; QPushButton* m_btnCancel; + int m_tid; }; diff --git a/src/Mod/TechDraw/Gui/TaskBalloon.cpp b/src/Mod/TechDraw/Gui/TaskBalloon.cpp index ec1e3af195..645e42b206 100644 --- a/src/Mod/TechDraw/Gui/TaskBalloon.cpp +++ b/src/Mod/TechDraw/Gui/TaskBalloon.cpp @@ -119,7 +119,7 @@ bool TaskBalloon::accept() m_guiDocument->commitCommand(); } else { // see comment in reject(). this may not do what we want. - Gui::Command::abortCommand(); + m_guiDocument->abortCommand(); } m_guiDocument->resetEdit(); diff --git a/src/Mod/TechDraw/Gui/TaskCenterLine.cpp b/src/Mod/TechDraw/Gui/TaskCenterLine.cpp index ade5e2a94e..13740fe1e3 100644 --- a/src/Mod/TechDraw/Gui/TaskCenterLine.cpp +++ b/src/Mod/TechDraw/Gui/TaskCenterLine.cpp @@ -398,7 +398,7 @@ Mode TaskCenterLine::checkPathologicalVertices(Mode inMode) //****************************************************************************** void TaskCenterLine::createCenterLine() { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Centerline")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Centerline")); // check for illogical parameters if (m_type == Type::EDGE) { @@ -412,7 +412,7 @@ void TaskCenterLine::createCenterLine() CenterLine* cl = CenterLine::CenterLineBuilder(m_partFeat, m_subNames, m_mode, false); if (!cl) { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(tid); return; } @@ -434,7 +434,7 @@ void TaskCenterLine::createCenterLine() m_partFeat->recomputeFeature(); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); // entering the edit mode m_editMode = true; @@ -532,7 +532,7 @@ bool TaskCenterLine::accept() return false; Gui::Command::updateActive(); - Gui::Command::commitCommand(); + doc->commitCommand(); doc->resetEdit(); return true; diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp index 76b8206ad1..586c007eba 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp @@ -550,7 +550,7 @@ void TaskComplexSection::applyAligned() //pointer to created view is not returned, but stored in m_section void TaskComplexSection::createComplexSection() { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Complex Section")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Complex Section")); if (!m_section) { const std::string objectName{QT_TR_NOOP("ComplexSection")}; m_sectionName = m_page->getDocument()->getUniqueObjectName(objectName.c_str()); @@ -635,7 +635,7 @@ void TaskComplexSection::createComplexSection() m_sectionName.c_str(), rotation); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } void TaskComplexSection::updateComplexSection() @@ -645,7 +645,7 @@ void TaskComplexSection::updateComplexSection() return; } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit Section View")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit Section View")); if (m_section) { QString qTemp = ui->leSymbol->text(); std::string temp = qTemp.toStdString(); @@ -694,7 +694,7 @@ void TaskComplexSection::updateComplexSection() Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Rotation = %.6f", m_sectionName.c_str(), rotation); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } std::string TaskComplexSection::makeSectionLabel(const QString& symbol) diff --git a/src/Mod/TechDraw/Gui/TaskCosVertex.cpp b/src/Mod/TechDraw/Gui/TaskCosVertex.cpp index 6df9a6c37f..6e1958658d 100644 --- a/src/Mod/TechDraw/Gui/TaskCosVertex.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosVertex.cpp @@ -131,14 +131,14 @@ void TaskCosVertex::updateUi() //! create the cv as entered, addCosmeticVertex will invert it void TaskCosVertex::addCosVertex(QPointF qPos) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Cosmetic Vertex")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Add Cosmetic Vertex")); // Base::Vector3d pos = DU::invertY(DU::toVector3d(qPos)); // int idx = (void) m_baseFeat->addCosmeticVertex(DU::toVector3d(qPos)); m_baseFeat->requestPaint(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp index 11e976d42f..db399c9e43 100644 --- a/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp @@ -226,7 +226,7 @@ void TaskCosmeticCircle::createCosmeticCircle(void) { // Base::Console().message("TCL::createCosmeticCircle()\n"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Circle")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Circle")); // point from Page/View is conventional coordinates (Y+ up), unscaled, unrotated, but centered (Csriz) // this is Canonical form with out inversion. @@ -254,7 +254,7 @@ void TaskCosmeticCircle::createCosmeticCircle(void) m_ce = m_partFeat->getCosmeticEdge(m_tag); m_ce->setFormat(LineFormat::getCurrentLineFormat()); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } void TaskCosmeticCircle::updateCosmeticCircle(void) @@ -298,12 +298,12 @@ bool TaskCosmeticCircle::accept() m_partFeat->requestPaint(); } else { //update mode - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Update Cosmetic Circle")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Update Cosmetic Circle")); updateCosmeticCircle(); m_partFeat->refreshCEGeoms(); m_partFeat->requestPaint(); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp index 5152c846c2..0b923c8ea7 100644 --- a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp @@ -172,7 +172,7 @@ void TaskCosmeticLine::setUiEdit() void TaskCosmeticLine::createCosmeticLine() { // Base::Console().message("TCL::createCosmeticLine()\n"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Line")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Line")); // ui 2d points are interpreted as unscaled, unrotated, uninverted double x = ui->qsbx1->value().getValue(); @@ -200,7 +200,7 @@ void TaskCosmeticLine::createCosmeticLine() m_ce = m_partFeat->getCosmeticEdge(m_tag); m_ce->setFormat(LineFormat::getCurrentLineFormat()); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } void TaskCosmeticLine::updateCosmeticLine() @@ -248,12 +248,12 @@ bool TaskCosmeticLine::accept() m_partFeat->requestPaint(); } else { //update mode - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Update Cosmetic Line")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Update Cosmetic Line")); updateCosmeticLine(); m_partFeat->refreshCEGeoms(); m_partFeat->requestPaint(); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); diff --git a/src/Mod/TechDraw/Gui/TaskCustomizeFormat.cpp b/src/Mod/TechDraw/Gui/TaskCustomizeFormat.cpp index 8ad5c3a586..49aee8ccb0 100644 --- a/src/Mod/TechDraw/Gui/TaskCustomizeFormat.cpp +++ b/src/Mod/TechDraw/Gui/TaskCustomizeFormat.cpp @@ -181,7 +181,8 @@ bool TaskCustomizeFormat::accept() // Slot: the OK button has been pressed QString formatPreview = ui->leFormat->text(); std::string formatString = formatPreview.toUtf8().constData(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Customize Format")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Customize Format")); + if (isDimension) { auto dim = dynamic_cast(selectedObject); @@ -192,7 +193,7 @@ bool TaskCustomizeFormat::accept() auto balloon = dynamic_cast(selectedObject); balloon->Text.setValue(formatString); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); return true; } diff --git a/src/Mod/TechDraw/Gui/TaskDetail.cpp b/src/Mod/TechDraw/Gui/TaskDetail.cpp index b36d9ca1e4..ff6166ae03 100644 --- a/src/Mod/TechDraw/Gui/TaskDetail.cpp +++ b/src/Mod/TechDraw/Gui/TaskDetail.cpp @@ -430,7 +430,7 @@ void TaskDetail::enableTaskButtons(bool button) //***** Feature create & edit stuff ******************************************* void TaskDetail::createDetail() { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Detail view")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Detail view")); const std::string objectName{"Detail"}; m_detailName = m_doc->getUniqueObjectName(objectName.c_str()); @@ -462,7 +462,7 @@ void TaskDetail::createDetail() m_pageName.c_str(), m_detailName.c_str()); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); getBaseFeat()->requestPaint(); m_created = true; @@ -472,7 +472,7 @@ void TaskDetail::updateDetail() { TechDraw::DrawViewDetail* detailFeat = getDetailFeat(); try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Update Detail")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Update Detail")); double x = ui->qsbX->rawValue(); double y = ui->qsbY->rawValue(); Base::Vector3d temp(x, y, 0.0); @@ -488,7 +488,7 @@ void TaskDetail::updateDetail() detailFeat->Reference.setValue(ref); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } catch (...) { //this is probably due to appl closing while dialog is still open diff --git a/src/Mod/TechDraw/Gui/TaskDimRepair.cpp b/src/Mod/TechDraw/Gui/TaskDimRepair.cpp index af48764e99..19d2ce9a60 100644 --- a/src/Mod/TechDraw/Gui/TaskDimRepair.cpp +++ b/src/Mod/TechDraw/Gui/TaskDimRepair.cpp @@ -242,9 +242,9 @@ bool TaskDimRepair::accept() { Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); - Gui::Command::openCommand(tr("Repair dimension").toStdString().c_str()); + int tid = Gui::Command::openActiveDocumentCommand(tr("Repair dimension").toStdString().c_str()); replaceReferences(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); m_dim->recomputeFeature(); Gui::Selection().clearSelection(); diff --git a/src/Mod/TechDraw/Gui/TaskHatch.cpp b/src/Mod/TechDraw/Gui/TaskHatch.cpp index e4a7ede000..6835c9f3c4 100644 --- a/src/Mod/TechDraw/Gui/TaskHatch.cpp +++ b/src/Mod/TechDraw/Gui/TaskHatch.cpp @@ -197,7 +197,7 @@ void TaskHatch::createHatch() const std::string objectName("Hatch"); std::string FeatName = doc->getUniqueObjectName(objectName.c_str()); - Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Hatch")); + int tid = Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Hatch")); Command::doCommand(Command::Doc, "App.activeDocument().addObject('TechDraw::DrawHatch', '%s')", FeatName.c_str()); Command::doCommand(Command::Doc, "App.activeDocument().%s.translateLabel('DrawHatch', 'Hatch', '%s')", @@ -226,7 +226,7 @@ void TaskHatch::createHatch() } else { Base::Console().error("TaskHatch - hatch has no ViewProvider\n"); } - Command::commitCommand(); + Command::commitCommand(tid); } void TaskHatch::updateHatch() @@ -234,7 +234,7 @@ void TaskHatch::updateHatch() // Base::Console().message("TH::updateHatch()\n"); std::string FeatName = m_hatch->getNameInDocument(); - Command::openCommand(QT_TRANSLATE_NOOP("Command", "Update Hatch")); + int tid = Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Update Hatch")); auto filespec = ui->fcFile->fileName().toStdString(); filespec = DU::cleanFilespecBackslash(filespec); @@ -249,7 +249,7 @@ void TaskHatch::updateHatch() m_vp->HatchRotation.setValue(ui->dsbRotation->value()); Base::Vector3d offset(ui->dsbOffsetX->value(), ui->dsbOffsetY->value(), 0.0); m_vp->HatchOffset.setValue(offset); - Command::commitCommand(); + Command::commitCommand(tid); } bool TaskHatch::accept() diff --git a/src/Mod/TechDraw/Gui/TaskLeaderLine.cpp b/src/Mod/TechDraw/Gui/TaskLeaderLine.cpp index 30275dae5e..072c14ee6b 100644 --- a/src/Mod/TechDraw/Gui/TaskLeaderLine.cpp +++ b/src/Mod/TechDraw/Gui/TaskLeaderLine.cpp @@ -341,7 +341,7 @@ void TaskLeaderLine::createLeaderFeature(std::vector sceneDeltas std::string PageName = m_basePage->getNameInDocument(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Leader")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Leader")); Command::doCommand(Command::Doc, "App.activeDocument().addObject('%s', '%s')", m_leaderType.c_str(), m_leaderName.c_str()); Command::doCommand(Command::Doc, "App.activeDocument().%s.translateLabel('DrawLeaderLine', 'LeaderLine', '%s')", @@ -402,7 +402,7 @@ void TaskLeaderLine::createLeaderFeature(std::vector sceneDeltas } Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); //trigger claimChildren in tree if (m_baseFeat) { @@ -428,7 +428,7 @@ void TaskLeaderLine::dumpTrackerPoints(std::vector& tPoints) con void TaskLeaderLine::updateLeaderFeature() { // Base::Console().message("TTL::updateLeaderFeature()\n"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit Leader")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit Leader")); //waypoints & x, y are updated by QGILeaderLine (for edits only!) commonFeatureUpdate(); Base::Color ac; @@ -438,7 +438,7 @@ void TaskLeaderLine::updateLeaderFeature() m_lineVP->LineStyle.setValue(ui->cboxStyle->currentIndex()); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); if (m_baseFeat) { m_baseFeat->requestPaint(); diff --git a/src/Mod/TechDraw/Gui/TaskProjGroup.cpp b/src/Mod/TechDraw/Gui/TaskProjGroup.cpp index 0594bd4019..42bdc31496 100644 --- a/src/Mod/TechDraw/Gui/TaskProjGroup.cpp +++ b/src/Mod/TechDraw/Gui/TaskProjGroup.cpp @@ -837,8 +837,8 @@ bool TaskProjGroup::reject() } else { //set the DPG and its views back to entry state. - if (Gui::Command::hasPendingCommand()) { - Gui::Command::abortCommand(); + if (doc->hasPendingCommand()) { + doc->abortCommand(); } // Restore views to initial spacing if (multiView) { @@ -885,10 +885,10 @@ void TaskDlgProjGroup::open() { if (!widget->getCreateMode()) { //this is an edit session, start a transaction if (dynamic_cast(view)) { - App::GetApplication().setActiveTransaction("Edit Projection Group", true); + App::GetApplication().setActiveTransaction(App::TransactionName{.name="Edit Projection Group", .temporary=true}); } else { - App::GetApplication().setActiveTransaction("Edit Part View", true); + App::GetApplication().setActiveTransaction(App::TransactionName{.name="Edit Part View", .temporary=true}); } } } diff --git a/src/Mod/TechDraw/Gui/TaskProjection.cpp b/src/Mod/TechDraw/Gui/TaskProjection.cpp index d2277c6e2a..a6c83c6037 100644 --- a/src/Mod/TechDraw/Gui/TaskProjection.cpp +++ b/src/Mod/TechDraw/Gui/TaskProjection.cpp @@ -77,7 +77,7 @@ bool TaskProjection::accept() dir.getValue(x, y,z); std::vector shapes = Gui::Selection().getObjectsOfType(); - Gui::Command::openCommand("Project shape"); + int tid = Gui::Command::openActiveDocumentCommand("Project shape"); Gui::Command::addModule(Gui::Command::Doc, "TechDraw"); for (std::vector::iterator it = shapes.begin(); it != shapes.end(); ++it) { const char* object = (*it)->getNameInDocument(); @@ -109,7 +109,7 @@ bool TaskProjection::accept() "FreeCAD.ActiveDocument.ActiveObject.IsoLineHCompound=%s", (ui->cbHidIso->isChecked() ? "True" : "False")); } Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); return true; } diff --git a/src/Mod/TechDraw/Gui/TaskRichAnno.cpp b/src/Mod/TechDraw/Gui/TaskRichAnno.cpp index d55f22a984..eefb7b28b5 100644 --- a/src/Mod/TechDraw/Gui/TaskRichAnno.cpp +++ b/src/Mod/TechDraw/Gui/TaskRichAnno.cpp @@ -109,7 +109,8 @@ TaskRichAnno::TaskRichAnno(TechDrawGui::ViewProviderRichAnno* annoVP) : graphicsView = m_vpp->getQGVPage(); m_toolbar = new MRichTextEdit(graphicsView->viewport()); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit Annotation")); + m_tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit Annotation")); + ui->setupUi(this); @@ -149,7 +150,7 @@ TaskRichAnno::TaskRichAnno(TechDraw::DrawView* baseFeat, m_qgParent = m_vpp->getQGSPage()->findQViewForDocObj(baseFeat); } - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Annotation")); + m_tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Annotation")); ui->setupUi(this); m_title = QObject::tr("Rich Text Creator"); @@ -675,7 +676,7 @@ void TaskRichAnno::createAnnoFeature(const QPointF* scenePos) } App::DocumentObject* obj = m_basePage->getDocument()->getObject(annoName.c_str()); if (!obj) { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(m_tid); throw Base::RuntimeError("TaskRichAnno - new RichAnno object not found"); } if (obj->isDerivedFrom()) { @@ -833,7 +834,7 @@ bool TaskRichAnno::accept() m_qgiAnno->setEditMode(false); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(m_tid); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); m_annoFeat->getDocument()->recompute(); @@ -853,7 +854,7 @@ bool TaskRichAnno::reject() removeViewFilter(); - Gui::Command::abortCommand(); + Gui::Command::abortCommand(m_tid); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); if (!m_createMode) { // Feature gone and m_annoFeat dangling if we are creating! diff --git a/src/Mod/TechDraw/Gui/TaskRichAnno.h b/src/Mod/TechDraw/Gui/TaskRichAnno.h index 0efed92c8e..2a596f19f9 100644 --- a/src/Mod/TechDraw/Gui/TaskRichAnno.h +++ b/src/Mod/TechDraw/Gui/TaskRichAnno.h @@ -133,6 +133,8 @@ private: QPointer m_toolbar {nullptr}; QPointer m_viewport {nullptr}; + + int m_tid {0}; }; class TaskDlgRichAnno : public Gui::TaskView::TaskDialog diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.cpp b/src/Mod/TechDraw/Gui/TaskSectionView.cpp index 5cafa69d08..65d768077c 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.cpp +++ b/src/Mod/TechDraw/Gui/TaskSectionView.cpp @@ -490,7 +490,7 @@ TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) std::string baseName = m_base->getNameInDocument(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Section View")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Section View")); if (!m_section) { const std::string objectName("SectionView"); m_sectionName = m_base->getDocument()->getUniqueObjectName(objectName.c_str()); @@ -555,7 +555,7 @@ TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Rotation = %.6f", m_sectionName.c_str(), rotation); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); return m_section; } @@ -569,7 +569,7 @@ void TaskSectionView::updateSectionView() const std::string objectName("SectionView"); std::string baseName = m_base->getNameInDocument(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit Section View")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit Section View")); if (m_section) { Command::doCommand(Command::Doc, "App.ActiveDocument.%s.SectionDirection = '%s'", m_sectionName.c_str(), m_dirName.c_str()); @@ -618,7 +618,7 @@ void TaskSectionView::updateSectionView() directionChanged(false); } } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); } std::string TaskSectionView::makeSectionLabel(QString symbol) diff --git a/src/Mod/TechDraw/Gui/TaskSurfaceFinishSymbols.cpp b/src/Mod/TechDraw/Gui/TaskSurfaceFinishSymbols.cpp index bb785be769..b60365b680 100644 --- a/src/Mod/TechDraw/Gui/TaskSurfaceFinishSymbols.cpp +++ b/src/Mod/TechDraw/Gui/TaskSurfaceFinishSymbols.cpp @@ -397,7 +397,7 @@ void TaskSurfaceFinishSymbols::onASME() bool TaskSurfaceFinishSymbols::accept() // Slot: dialog finished using OK { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Surface Finish Symbols")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Surface Finish Symbols")); App::Document *doc = Application::Instance->activeDocument()->getDocument(); auto* surfaceSymbol = doc->addObject("SurfaceSymbol"); surfaceSymbol->Symbol.setValue(completeSymbol()); @@ -421,7 +421,7 @@ bool TaskSurfaceFinishSymbols::accept() page->addView(surfaceSymbol); } - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); return true; } diff --git a/src/Mod/TechDraw/Gui/TaskWeldingSymbol.cpp b/src/Mod/TechDraw/Gui/TaskWeldingSymbol.cpp index 216053efe2..f8d6f2f469 100644 --- a/src/Mod/TechDraw/Gui/TaskWeldingSymbol.cpp +++ b/src/Mod/TechDraw/Gui/TaskWeldingSymbol.cpp @@ -555,15 +555,15 @@ bool TaskWeldingSymbol::accept() { // Base::Console().message("TWS::accept()\n"); if (m_createMode) { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Weld Symbol")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Create Weld Symbol")); m_weldFeat = createWeldingSymbol(); updateTiles(); Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); m_weldFeat->recomputeFeature(); // m_weldFeat->requestPaint(); //not a dv! } else { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit Weld Symbol")); + int tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Edit Weld Symbol")); try { updateWeldingSymbol(); updateTiles(); @@ -573,7 +573,7 @@ bool TaskWeldingSymbol::accept() } Gui::Command::updateActive(); - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); m_weldFeat->recomputeFeature(); // m_weldFeat->requestPaint(); //not a dv! } diff --git a/src/Mod/TechDraw/TechDrawTools/CommandAxoLengthDimension.py b/src/Mod/TechDraw/TechDrawTools/CommandAxoLengthDimension.py index 4dd4fe2c8a..eafca9dd1f 100644 --- a/src/Mod/TechDraw/TechDrawTools/CommandAxoLengthDimension.py +++ b/src/Mod/TechDraw/TechDrawTools/CommandAxoLengthDimension.py @@ -75,7 +75,7 @@ class CommandAxoLengthDimension: def Activated(self): """Run the following code when the command is activated (button press).""" - App.setActiveTransaction("Create axonometric length dimension") + App.ActiveDocument.openTransaction("Create axonometric length dimension") vertexes = [] edges = [] @@ -157,7 +157,7 @@ class CommandAxoLengthDimension: view.requestPaint() Gui.Selection.clearSelection() - App.closeActiveTransaction() + App.ActiveDocument.commitTransaction() view.touch() # make view claim its new child def IsActive(self): diff --git a/src/Mod/TechDraw/TechDrawTools/TaskAddOffsetVertex.py b/src/Mod/TechDraw/TechDrawTools/TaskAddOffsetVertex.py index 1d4ea9dbb4..14d8423096 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskAddOffsetVertex.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskAddOffsetVertex.py @@ -46,14 +46,14 @@ class TaskAddOffsetVertex(): self.view = view self.vertex = vertex - App.setActiveTransaction("Add offset vertex") + App.ActiveDocument.openTransaction("Add offset vertex") def accept(self): '''slot: OK pressed''' point = self.vertex.Point # this is unscaled and inverted, but is also rotated. # unrotate point. Note that since this is already unscaled, we need to set the # third parameter to False to avoid an extra descaling. - point = TechDraw.makeCanonicalPoint(self.view, point, False); + point = TechDraw.makeCanonicalPoint(self.view, point, False) xOffset = self.form.dSpinBoxX.value() yOffset = self.form.dSpinBoxY.value() offset = App.Vector(xOffset,yOffset,0) # the offset is applied to the canonical @@ -61,8 +61,8 @@ class TaskAddOffsetVertex(): # uninverted relative value. self.view.makeCosmeticVertex(point+offset) Gui.Control.closeDialog() - App.closeActiveTransaction() + App.ActiveDocument.commitTransaction() def reject(self): - App.closeActiveTransaction(True) + App.ActiveDocument.abortTransaction() return True diff --git a/src/Mod/TechDraw/TechDrawTools/TaskFillTemplateFields.py b/src/Mod/TechDraw/TechDrawTools/TaskFillTemplateFields.py index 2b0ae21d41..f6a8097846 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskFillTemplateFields.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskFillTemplateFields.py @@ -532,18 +532,16 @@ class TaskFillTemplateFields: transactionName = QtCore.QT_TRANSLATE_NOOP( "Techdraw_FillTemplateFields", "Fill template fields" ) - App.setActiveTransaction(transactionName) + App.ActiveDocument.openTransaction(transactionName) i = 0 for cb in self.checkBoxList: if cb.isChecked(): self.texts[keyLst[i]] = self.lineTextList[i].text() i += 1 self.page.Template.EditableTexts = self.texts - App.closeActiveTransaction(False) + App.ActiveDocument.commitTransaction() self.close() - App.closeActiveTransaction() - def close(self): self.dialog.hide() keyLst.clear() diff --git a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py index 529a8c6bb7..71df6aeb24 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py @@ -95,7 +95,7 @@ class TaskHoleShaftFit: self.form.rbShaftBase.clicked.connect(partial(self.on_HoleShaftChanged, False)) self.form.cbField.currentIndexChanged.connect(self.on_FieldChanged) - App.setActiveTransaction("Add hole or shaft fit") + App.ActiveDocument.openTransaction("Add hole or shaft fit") def setHoleFields(self): """set hole fields in the combo box""" @@ -170,10 +170,10 @@ class TaskHoleShaftFit: else: dim.FormatSpecUnderTolerance = "( %-0.6w)" Gui.Control.closeDialog() - App.closeActiveTransaction() + App.ActiveDocument.commitTransaction() def reject(self): - App.closeActiveTransaction(True) + App.ActiveDocument.abortTransaction() return True diff --git a/src/Mod/TechDraw/TechDrawTools/TaskMoveView.py b/src/Mod/TechDraw/TechDrawTools/TaskMoveView.py index ab956fac08..bd97d12092 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskMoveView.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskMoveView.py @@ -57,11 +57,11 @@ class TaskMoveView: self.dialogOpen = False - App.setActiveTransaction("Move view") + App.ActiveDocument.openTransaction("Move view") def accept(self): # print ("Accept") - App.closeActiveTransaction() + App.ActiveDocument.commitTransaction() view = App.ActiveDocument.getObject(self.viewName) fromPage = App.ActiveDocument.getObject(self.fromPageName) toPage = App.ActiveDocument.getObject(self.toPageName) @@ -70,7 +70,7 @@ class TaskMoveView: def reject(self): # print ("Reject") - App.closeActiveTransaction(True) + App.ActiveDocument.abortTransaction() return True def pickView(self): diff --git a/src/Mod/TechDraw/TechDrawTools/TaskShareView.py b/src/Mod/TechDraw/TechDrawTools/TaskShareView.py index 4d812eaf92..8c8b119544 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskShareView.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskShareView.py @@ -57,7 +57,7 @@ class TaskShareView: self.dialogOpen = False - App.setActiveTransaction("Share view") + App.ActiveDocument.openTransaction("Share view") def accept(self): # print ("Accept") @@ -66,12 +66,12 @@ class TaskShareView: toPage = App.ActiveDocument.getObject(self.toPageName) TDToolsMovers.moveView(view, fromPage, toPage, True) - App.closeActiveTransaction() + App.ActiveDocument.commitTransaction() return True def reject(self): # print ("Reject") - App.closeActiveTransaction(True) + App.ActiveDocument.abortTransaction() return True def pickView(self): diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 940449ddac..585a490f7e 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -2691,3 +2691,49 @@ class DocumentAutoCreatedCases(unittest.TestCase): FreeCAD.closeDocument("TestDoc") self.assertIn("TestDoc", FreeCAD.listDocuments()) self.assertIn("SavedDoc", FreeCAD.listDocuments()) + + +# Test if actions done on two documents are undone together +# (working toward making this test pass) +class MultiDocumentUndo(unittest.TestCase): + def setUp(self): + self.Doc1 = FreeCAD.newDocument("Doc1") + self.Doc2 = FreeCAD.newDocument("Doc2") + self.Doc1.UndoMode = 1 + self.Doc2.UndoMode = 1 + + def testAddObjects(self): + self.Doc1.openTransaction("transact1") + self.Doc2.openTransaction("transact2") + + obj1 = self.Doc1.addObject("App::DocumentObject", "Obj1Name") + obj2 = self.Doc2.addObject("App::DocumentObject", "Obj2Name") + + self.assertNotEqual(self.Doc1.getBookedTransactionID(), self.Doc2.getBookedTransactionID()) + + self.Doc1.commitTransaction() + self.Doc2.commitTransaction() + + with self.assertRaises(TypeError): + self.Doc1.getObject([1]) + self.Doc2.getObject([1]) + + self.assertEqual(self.Doc1.getObject("Obj1Name"), obj1) + self.assertEqual(self.Doc2.getObject("Obj2Name"), obj2) + + self.Doc1.undo() + self.assertEqual(self.Doc1.getObject("Obj1Name"), None) + self.assertEqual(self.Doc2.getObject("Obj2Name"), obj2) + + self.Doc2.undo() + self.assertEqual(self.Doc1.getObject("Obj1Name"), None) + self.assertEqual(self.Doc2.getObject("Obj2Name"), None) + + self.Doc1.redo() + self.assertEqual(self.Doc1.getObject("Obj1Name"), obj1) + self.assertEqual(self.Doc2.getObject("Obj2Name"), None) + + def tearDown(self): + # closing doc + FreeCAD.closeDocument("Doc1") + FreeCAD.closeDocument("Doc2") diff --git a/tests/src/App/Property.cpp b/tests/src/App/Property.cpp index d7cb1665df..06b4d95f90 100644 --- a/tests/src/App/Property.cpp +++ b/tests/src/App/Property.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -377,8 +376,9 @@ TEST_F(RenameProperty, undoRenameProperty) // Act bool isRenamed = false; { - App::AutoTransaction transaction("Rename Property"); + _doc->openTransaction("Rename Property"); isRenamed = varSet->renameDynamicProperty(prop, "NewName"); + _doc->commitTransaction(); } // Assert @@ -409,8 +409,9 @@ TEST_F(RenameProperty, redoRenameProperty) // Act bool isRenamed = false; { - App::AutoTransaction transaction("Rename Property"); + _doc->openTransaction("Rename Property"); isRenamed = varSet->renameDynamicProperty(prop, "NewName"); + _doc->commitTransaction(); } // Assert From 7db3f901fdbe427868c5732d43957298f24e342b Mon Sep 17 00:00:00 2001 From: theo-vt Date: Thu, 12 Feb 2026 16:43:44 -0500 Subject: [PATCH 02/12] Fix diapearing sketcher tools --- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index a5c80f39f7..1fa3a9ba8a 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -3915,8 +3915,6 @@ void ViewProviderSketch::unsetEdit(int ModNum) auto gridnode = getGridNode(); pcRoot->removeChild(gridnode); - Workbench::leaveEditMode(); - if (listener) { Gui::getMainWindow()->removeEventFilter(listener.get()); listener.reset(); From 4ba5504e7af5309ba5c388867ca8b1cceecb6cf8 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Wed, 18 Feb 2026 16:03:26 -0500 Subject: [PATCH 03/12] Remove dupplicate clone code causing crash --- src/Mod/PartDesign/Gui/Command.cpp | 40 ------------------------------ 1 file changed, 40 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 8c377d6694..008e5fb014 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -504,7 +504,6 @@ void CmdPartDesignClone::activated(int iMsg) Part::Feature::getClassTypeId() ); - App::DocumentObject* obj = nullptr; if (objs.size() == 1) { // As suggested in https://forum.freecad.org/viewtopic.php?f=3&t=25265&p=198547#p207336 // put the clone into its own new body. @@ -557,45 +556,6 @@ void CmdPartDesignClone::activated(int iMsg) copyVisual(cloneObj, "DisplayMode", obj); commitCommand(); } - - // As suggested in https://forum.freecad.org/viewtopic.php?f=3&t=25265&p=198547#p207336 - // put the clone into its own new body. - // This also fixes bug #3447 because the clone is a PD feature and thus - // requires a body where it is part of. - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Clone")); - - auto objCmd = getObjectCmd(obj); - std::string cloneName = getUniqueObjectName("Clone", obj); - std::string bodyName = getUniqueObjectName("Body", obj); - - // Create body and clone - Gui::cmdAppDocument(obj, std::stringstream() << "addObject('PartDesign::Body','" << bodyName << "')"); - Gui::cmdAppDocument( - obj, - std::stringstream() << "addObject('PartDesign::FeatureBase','" << cloneName << "')" - ); - - auto bodyObj = obj->getDocument()->getObject(bodyName.c_str()); - auto cloneObj = obj->getDocument()->getObject(cloneName.c_str()); - - // In the first step set the group link and tip of the body - Gui::cmdAppObject(bodyObj, std::stringstream() << "Group = [" << getObjectCmd(cloneObj) << "]"); - Gui::cmdAppObject(bodyObj, std::stringstream() << "Tip = " << getObjectCmd(cloneObj)); - - // In the second step set the link of the base feature - Gui::cmdAppObject(cloneObj, std::stringstream() << "BaseFeature = " << objCmd); - Gui::cmdAppObject(cloneObj, std::stringstream() << "Placement = " << objCmd << ".Placement"); - Gui::cmdAppObject(cloneObj, std::stringstream() << "setEditorMode('Placement', 0)"); - - updateActive(); - copyVisual(cloneObj, "ShapeAppearance", obj); - copyVisual(cloneObj, "LineColor", obj); - copyVisual(cloneObj, "PointColor", obj); - copyVisual(cloneObj, "Transparency", obj); - copyVisual(cloneObj, "DisplayMode", obj); - - commitCommand(); } bool CmdPartDesignClone::isActive() From d59471b6bd6f5cd4127c3a730f1633fb63421053 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sat, 21 Feb 2026 12:52:57 -0500 Subject: [PATCH 04/12] Gui: Allow a selection gate per document --- src/Gui/Selection/Selection.cpp | 116 ++++++++++++++++++-------------- src/Gui/Selection/Selection.h | 24 +++++-- 2 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/Gui/Selection/Selection.cpp b/src/Gui/Selection/Selection.cpp index 30a167f691..e09c5c4820 100644 --- a/src/Gui/Selection/Selection.cpp +++ b/src/Gui/Selection/Selection.cpp @@ -62,6 +62,7 @@ using namespace Gui; using namespace std; namespace sp = std::placeholders; +// TODO-theo-vt remove! SelectionGateFilterExternal::SelectionGateFilterExternal(const char* docName, const char* objName) { if (docName) { @@ -104,10 +105,6 @@ SelectionObserver::SelectionObserver(const ViewProviderDocumentObject* vp, bool : resolve(resolve) , blockedSelection(false) { - if (vp && vp->getObject() && vp->getObject()->getDocument()) { - filterDocName = vp->getObject()->getDocument()->getName(); - filterObjName = vp->getObject()->getNameInDocument(); - } if (attach) { attachSelection(); } @@ -149,12 +146,6 @@ void SelectionObserver::attachSelection() std::bind(&SelectionObserver::_onSelectionChanged, this, sp::_1) ); // NOLINTEND - - if (!filterDocName.empty()) { - Selection().addSelectionGate( - new SelectionGateFilterExternal(filterDocName.c_str(), filterObjName.c_str()) - ); - } } } @@ -182,9 +173,6 @@ void SelectionObserver::detachSelection() { if (connectSelection.connected()) { connectSelection.disconnect(); - if (!filterDocName.empty()) { - Selection().rmvSelectionGate(); - } } } @@ -827,24 +815,29 @@ int SelectionSingleton::setPreselect( ) { if (!pDocName || !pObjectName) { - rmvPreselect(); + rmvPreselect(); // Invalid request return 0; } + if (!pSubName) { pSubName = ""; } if (DocName == pDocName && FeatName == pObjectName && SubName == pSubName) { - return -1; + return -1; // Already pre-selected } rmvPreselect(); - if (ActiveGate && signal != SelectionChanges::MsgSource::Internal) { - App::Document* pDoc = getDocument(pDocName); - if (!pDoc || !pObjectName) { - return 0; - } + App::Document* pDoc = getDocument(pDocName); + if (!pDoc) { + return 0; // Invalid request + } + auto foundGate = docSelectionGate.find(pDoc); + + if (foundGate != docSelectionGate.end() && signal != SelectionChanges::MsgSource::Internal) { + SelectionGate* gate = foundGate->second; + App::ElementNamePair elementName; auto pObject = pDoc->getObject(pObjectName); if (!pObject) { @@ -852,14 +845,14 @@ int SelectionSingleton::setPreselect( } const char* subelement = pSubName; - if (gateResolve != ResolveMode::NoResolve) { + if (gate->resolveMode != ResolveMode::NoResolve) { auto& newElementName = elementName.newName; auto& oldElementName = elementName.oldName; pObject = App::GeoFeature::resolveElement(pObject, pSubName, elementName); if (!pObject) { return 0; } - if (gateResolve > ResolveMode::OldStyleElement) { + if (gate->resolveMode > ResolveMode::OldStyleElement) { subelement = !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str(); } @@ -867,10 +860,10 @@ int SelectionSingleton::setPreselect( subelement = oldElementName.c_str(); } } - if (!ActiveGate->allow(pObject->getDocument(), pObject, subelement)) { + if (!gate->allow(pObject->getDocument(), pObject, subelement)) { QString msg; - if (ActiveGate->notAllowedReason.length() > 0) { - msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + if (gate->notAllowedReason.length() > 0) { + msg = QObject::tr(gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate("SelectionFilter", "Not allowed:"); @@ -1058,7 +1051,9 @@ void SelectionSingleton::rmvPreselect(bool signal) hy = 0; hz = 0; - if (ActiveGate && getMainWindow()) { + App::Document* activeDoc = getDocument(nullptr); + auto foundGate = docSelectionGate.find(activeDoc); + if (foundGate != docSelectionGate.end() && getMainWindow()) { Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->restoreOverrideCursor(); } @@ -1075,33 +1070,42 @@ const SelectionChanges& SelectionSingleton::getPreselection() const } // add a SelectionGate to control what is selectable -void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve) +void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve, const char* pDocName) { - if (ActiveGate) { - rmvSelectionGate(); + App::Document* doc = getDocument(pDocName); + if (!doc) { + return; } + rmvSelectionGate(doc); - ActiveGate = gate; - gateResolve = resolve; + gate->resolveMode = resolve; + docSelectionGate[doc] = gate; } // remove the active SelectionGate -void SelectionSingleton::rmvSelectionGate() +void SelectionSingleton::rmvSelectionGate(App::Document* doc) { - if (ActiveGate) { - delete ActiveGate; - ActiveGate = nullptr; + auto foundGate = docSelectionGate.find(doc); + if (foundGate != docSelectionGate.end()) { + delete foundGate->second; + docSelectionGate.erase(foundGate); - Gui::Document* doc = Gui::Application::Instance->activeDocument(); - if (doc) { - // if a document is about to be closed it has no MDI view any more - Gui::MDIView* mdi = doc->getActiveView(); - if (mdi) { + // if a document is about to be closed it has no MDI view any more + if (Gui::Document* guiDoc = Gui::Application::Instance->getDocument(doc)) { + if (Gui::MDIView* mdi = guiDoc->getActiveView()) { mdi->restoreOverrideCursor(); } } } } +void SelectionSingleton::rmvSelectionGate(const char* pDocName) +{ + App::Document* doc = getDocument(pDocName); + if (!doc) { + return; + } + rmvSelectionGate(doc); +} App::Document* SelectionSingleton::getDocument(const char* pDocName) const @@ -1217,16 +1221,26 @@ bool SelectionSingleton::addSelection( temp.y = y; temp.z = z; + App::Document* doc = getDocument(pDocName); + if (!doc) { + return false; + } + auto foundGate = docSelectionGate.find(doc); // check for a Selection Gate - if (ActiveGate) { + if (foundGate != docSelectionGate.end()) { + SelectionGate* gate = foundGate->second; const char* subelement = nullptr; - auto pObject - = getObjectOfType(temp, App::DocumentObject::getClassTypeId(), gateResolve, &subelement); - if (!ActiveGate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { + auto pObject = getObjectOfType( + temp, + App::DocumentObject::getClassTypeId(), + gate->resolveMode, + &subelement + ); + if (!gate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { if (getMainWindow()) { QString msg; - if (ActiveGate->notAllowedReason.length() > 0) { - msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + if (gate->notAllowedReason.length() > 0) { + msg = QObject::tr(gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate( @@ -1238,7 +1252,7 @@ bool SelectionSingleton::addSelection( Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(Qt::ForbiddenCursor); } - ActiveGate->notAllowedReason.clear(); + gate->notAllowedReason.clear(); QApplication::beep(); return false; } @@ -2178,8 +2192,7 @@ SelectionSingleton::SelectionSingleton() hx = 0; hy = 0; hz = 0; - ActiveGate = nullptr; - gateResolve = ResolveMode::OldStyleElement; + // NOLINTBEGIN App::GetApplication().signalDeletedObject.connect( std::bind(&Gui::SelectionSingleton::slotDeletedObject, this, sp::_1) @@ -3063,13 +3076,14 @@ PyObject* SelectionSingleton::sAddSelectionGate(PyObject* /*self*/, PyObject* ar PyObject* SelectionSingleton::sRemoveSelectionGate(PyObject* /*self*/, PyObject* args) { - if (!PyArg_ParseTuple(args, "")) { + const char* pDocName = ""; + if (!PyArg_ParseTuple(args, "|s", &pDocName)) { return nullptr; } PY_TRY { - Selection().rmvSelectionGate(); + Selection().rmvSelectionGate(pDocName); Py_Return; } PY_CATCH; diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index 4244a21f13..4929f08158 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -284,6 +284,7 @@ public: * literal into QT_TR_NOOP() for translatability. */ std::string notAllowedReason; + ResolveMode resolveMode; }; /** SelectionGateFilterExternal @@ -415,10 +416,19 @@ public: void setPreselectCoord(float x, float y, float z); /// returns the present preselection const SelectionChanges& getPreselection() const; - /// add a SelectionGate to control what is selectable - void addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve = ResolveMode::OldStyleElement); - /// remove the active SelectionGate - void rmvSelectionGate(); + /// add a SelectionGate to control what is selectable in a document's scope, by default the + /// active document is selected + // which is usually the intended behavior + void addSelectionGate( + Gui::SelectionGate* gate, + ResolveMode resolve = ResolveMode::OldStyleElement, + const char* pDocName = nullptr + ); + /// remove the document's SelectionGate, by default the active document is selected, which is + /// usually the intended behavior + void rmvSelectionGate(const char* pDocName = nullptr); + /// remove the document's SelectionGate (assumes valid pointer) + void rmvSelectionGate(App::Document* doc); int disableCommandLog(); int enableCommandLog(bool silent = false); @@ -833,13 +843,15 @@ protected: static SelectionSingleton* _pcSingleton; + // each document can have at most 1 selection gate active + std::map docSelectionGate; + + // Preselection helpers - it's a mess std::string DocName; std::string FeatName; std::string SubName; float hx, hy, hz; - Gui::SelectionGate* ActiveGate; - ResolveMode gateResolve; int logDisabled = 0; bool logHasSelection = false; From 3076ce66be3da747a7836e593952378c5260eef3 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sun, 22 Feb 2026 11:37:16 -0500 Subject: [PATCH 05/12] Scope the selection per document --- src/Gui/Selection/Selection.cpp | 695 +++++++++++++++++++------------- src/Gui/Selection/Selection.h | 107 ++--- src/Gui/Tree.cpp | 2 +- 3 files changed, 469 insertions(+), 335 deletions(-) diff --git a/src/Gui/Selection/Selection.cpp b/src/Gui/Selection/Selection.cpp index e09c5c4820..594c5c406b 100644 --- a/src/Gui/Selection/Selection.cpp +++ b/src/Gui/Selection/Selection.cpp @@ -178,16 +178,37 @@ void SelectionObserver::detachSelection() // ------------------------------------------- -bool SelectionSingleton::hasSelection() const -{ - return !_SelList.empty(); -} - bool SelectionSingleton::hasPreselection() const { return !CurrentPreselection.Object.getObjectName().empty(); } +unsigned int SelectionSingleton::size(const char* pDocName) const +{ + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return 0; + } + return static_cast(context->selList.size()); +} + +std::size_t SelectionSingleton::selStackBackSize(const char* pDocName) const +{ + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return 0; + } + return context->selStackBack.size(); +} +std::size_t SelectionSingleton::selStackForwardSize(const char* pDocName) const +{ + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return 0; + } + return context->selStackForward.size(); +} + std::vector SelectionSingleton::getCompleteSelection(ResolveMode resolve) const { return getSelection("*", resolve); @@ -204,24 +225,20 @@ std::vector SelectionSingleton::getSelection( temp.reserve(1); } SelObj tempSelObj; - - App::Document* pcDoc = nullptr; - if (!pDocName || strcmp(pDocName, "*") != 0) { - pcDoc = getDocument(pDocName); - if (!pcDoc) { - return temp; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; } std::map> objMap; - for (auto& sel : _SelList) { + for (auto& sel : context->selList) { if (!sel.pDoc) { continue; } const char* subelement = nullptr; auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve, &subelement); - if (!obj || (pcDoc && sel.pObject->getDocument() != pcDoc)) { + if (!obj) { continue; } @@ -252,22 +269,22 @@ std::vector SelectionSingleton::getSelection( return temp; } - -bool SelectionSingleton::hasSelection(const char* doc, ResolveMode resolve) const +bool SelectionSingleton::hasSelection(const char* pDocName) const { - App::Document* pcDoc = nullptr; - if (!doc || strcmp(doc, "*") != 0) { - pcDoc = getDocument(doc); - if (!pcDoc) { - return false; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; } - for (auto& sel : _SelList) { - if (!sel.pDoc) { - continue; - } - auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve); - if (obj && (!pcDoc || sel.pObject->getDocument() == pcDoc)) { + return !context->selList.empty(); +} +bool SelectionSingleton::hasSelection(const char* pDocName, ResolveMode resolve) const +{ + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + for (auto& sel : context->selList) { + if (getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve)) { return true; } } @@ -275,19 +292,13 @@ bool SelectionSingleton::hasSelection(const char* doc, ResolveMode resolve) cons return false; } -bool SelectionSingleton::hasSubSelection(const char* doc, bool subElement) const +bool SelectionSingleton::hasSubSelection(const char* pDocName, bool subElement) const { - App::Document* pcDoc = nullptr; - if (!doc || strcmp(doc, "*") != 0) { - pcDoc = getDocument(doc); - if (!pcDoc) { - return false; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; } - for (auto& sel : _SelList) { - if (pcDoc && pcDoc != sel.pDoc) { - continue; - } + for (auto& sel : context->selList) { if (sel.SubName.empty()) { continue; } @@ -307,28 +318,23 @@ std::vector SelectionSingleton::getPickedList(const std::vector temp; SelObj tempSelObj; - App::Document* pcDoc = nullptr; - if (!pDocName || strcmp(pDocName, "*") != 0) { - pcDoc = getDocument(pDocName); - if (!pcDoc) { - return temp; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; } - for (std::list<_SelObj>::const_iterator It = _PickedList.begin(); It != _PickedList.end(); ++It) { - if (!pcDoc || It->pDoc == pcDoc) { - tempSelObj.DocName = It->DocName.c_str(); - tempSelObj.FeatName = It->FeatName.c_str(); - tempSelObj.SubName = It->SubName.c_str(); - tempSelObj.TypeName = It->TypeName.c_str(); - tempSelObj.pObject = It->pObject; - tempSelObj.pResolvedObject = It->pResolvedObject; - tempSelObj.pDoc = It->pDoc; - tempSelObj.x = It->x; - tempSelObj.y = It->y; - tempSelObj.z = It->z; - temp.push_back(tempSelObj); - } + for (const auto& picked : context->pickedList) { + tempSelObj.DocName = picked.DocName.c_str(); + tempSelObj.FeatName = picked.FeatName.c_str(); + tempSelObj.SubName = picked.SubName.c_str(); + tempSelObj.TypeName = picked.TypeName.c_str(); + tempSelObj.pObject = picked.pObject; + tempSelObj.pResolvedObject = picked.pResolvedObject; + tempSelObj.pDoc = picked.pDoc; + tempSelObj.x = picked.x; + tempSelObj.y = picked.y; + tempSelObj.z = picked.z; + temp.push_back(tempSelObj); } return temp; @@ -440,7 +446,11 @@ std::vector SelectionSingleton::getSelectionEx( bool single ) const { - return getObjectList(pDocName, typeId, _SelList, resolve, single); + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; + } + return getObjectList(pDocName, typeId, context->selList, resolve, single); } std::vector SelectionSingleton::getPickedListEx( @@ -448,13 +458,17 @@ std::vector SelectionSingleton::getPickedListEx( Base::Type typeId ) const { - return getObjectList(pDocName, typeId, _PickedList, ResolveMode::NoResolve); + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; + } + return getObjectList(pDocName, typeId, context->pickedList, ResolveMode::NoResolve); } std::vector SelectionSingleton::getObjectList( const char* pDocName, Base::Type typeId, - std::list<_SelObj>& objList, + const std::list& objList, ResolveMode resolve, bool single ) const @@ -520,16 +534,25 @@ std::vector SelectionSingleton::getObjectList( return temp; } -bool SelectionSingleton::needPickedList() const +bool SelectionSingleton::needPickedList(const char* pDocName) const { - return _needPickedList; + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + return context->needPickedList; } -void SelectionSingleton::enablePickedList(bool enable) +void SelectionSingleton::enablePickedList(bool enable, const char* pDocName) { - if (enable != _needPickedList) { - _needPickedList = enable; - _PickedList.clear(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + + if (enable != context->needPickedList) { + context->needPickedList = enable; + context->pickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } } @@ -599,9 +622,13 @@ void SelectionSingleton::notify(SelectionChanges&& Chng) } } -bool SelectionSingleton::hasPickedList() const +bool SelectionSingleton::hasPickedList(const char* pDocName) const { - return !_PickedList.empty(); + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + return !context->pickedList.empty(); } int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList& prop) const @@ -633,7 +660,7 @@ int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList& prop) } App::DocumentObject* SelectionSingleton::getObjectOfType( - _SelObj& sel, + const SelectionDescription& sel, Base::Type typeId, ResolveMode resolve, const char** subelement @@ -676,31 +703,19 @@ vector SelectionSingleton::getObjectsOfType( ResolveMode resolve ) const { - std::vector temp; - - App::Document* pcDoc = nullptr; - if (!pDocName || strcmp(pDocName, "*") != 0) { - pcDoc = getDocument(pDocName); - if (!pcDoc) { - return temp; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; } std::set objs; - for (auto& sel : _SelList) { - if (pcDoc && pcDoc != sel.pDoc) { - continue; - } - App::DocumentObject* pObject = getObjectOfType(sel, typeId, resolve); - if (pObject) { - auto ret = objs.insert(pObject); - if (ret.second) { - temp.push_back(pObject); - } + for (auto& sel : context->selList) { + if (App::DocumentObject* pObject = getObjectOfType(sel, typeId, resolve)) { + objs.insert(pObject); } } - return temp; + return std::vector(objs.begin(), objs.end()); } std::vector SelectionSingleton::getObjectsOfType( @@ -722,16 +737,13 @@ unsigned int SelectionSingleton::countObjectsOfType( ResolveMode resolve ) const { - App::Document* pcDoc = nullptr; - if (!pDocName || strcmp(pDocName, "*") != 0) { - pcDoc = getDocument(pDocName); - if (!pcDoc) { - return 0; - } + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return 0; } - return std::count_if(_SelList.begin(), _SelList.end(), [&](auto& sel) { - return (!pcDoc || pcDoc == sel.pDoc) && getObjectOfType(sel, typeId, resolve); + return std::count_if(context->selList.begin(), context->selList.end(), [&](auto& sel) { + return getObjectOfType(sel, typeId, resolve); }); } @@ -831,13 +843,11 @@ int SelectionSingleton::setPreselect( App::Document* pDoc = getDocument(pDocName); if (!pDoc) { - return 0; // Invalid request + return 0; } - auto foundGate = docSelectionGate.find(pDoc); - - if (foundGate != docSelectionGate.end() && signal != SelectionChanges::MsgSource::Internal) { - SelectionGate* gate = foundGate->second; + SelectionContext* context = &docSelectionContext[pDoc]; + if (context->gate && signal != SelectionChanges::MsgSource::Internal) { App::ElementNamePair elementName; auto pObject = pDoc->getObject(pObjectName); if (!pObject) { @@ -845,14 +855,14 @@ int SelectionSingleton::setPreselect( } const char* subelement = pSubName; - if (gate->resolveMode != ResolveMode::NoResolve) { + if (context->resolveMode != ResolveMode::NoResolve) { auto& newElementName = elementName.newName; auto& oldElementName = elementName.oldName; pObject = App::GeoFeature::resolveElement(pObject, pSubName, elementName); if (!pObject) { return 0; } - if (gate->resolveMode > ResolveMode::OldStyleElement) { + if (context->resolveMode > ResolveMode::OldStyleElement) { subelement = !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str(); } @@ -860,10 +870,10 @@ int SelectionSingleton::setPreselect( subelement = oldElementName.c_str(); } } - if (!gate->allow(pObject->getDocument(), pObject, subelement)) { + if (!context->gate->allow(pObject->getDocument(), pObject, subelement)) { QString msg; - if (gate->notAllowedReason.length() > 0) { - msg = QObject::tr(gate->notAllowedReason.c_str()); + if (context->gate->notAllowedReason.length() > 0) { + msg = QObject::tr(context->gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate("SelectionFilter", "Not allowed:"); @@ -886,7 +896,7 @@ int SelectionSingleton::setPreselect( mdi->restoreOverrideCursor(); } - DocName = pDocName; + DocName = pDocName == nullptr ? "" : pDocName; FeatName = pObjectName; SubName = pSubName; hx = x; @@ -1044,6 +1054,13 @@ void SelectionSingleton::rmvPreselect(bool signal) // reset the current preselection CurrentPreselection = SelectionChanges(); + const SelectionContext* context = getSelectionContext(DocName.c_str()); + if (context && context->gate && getMainWindow()) { + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->restoreOverrideCursor(); + } + + // Reset preselection helpers DocName = ""; FeatName = ""; SubName = ""; @@ -1051,13 +1068,6 @@ void SelectionSingleton::rmvPreselect(bool signal) hy = 0; hz = 0; - App::Document* activeDoc = getDocument(nullptr); - auto foundGate = docSelectionGate.find(activeDoc); - if (foundGate != docSelectionGate.end() && getMainWindow()) { - Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); - mdi->restoreOverrideCursor(); - } - FC_TRACE("rmv preselect"); // notify observing objects @@ -1078,17 +1088,18 @@ void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode } rmvSelectionGate(doc); - gate->resolveMode = resolve; - docSelectionGate[doc] = gate; + SelectionContext& context = docSelectionContext[doc]; + context.resolveMode = resolve; + context.gate = gate; } // remove the active SelectionGate void SelectionSingleton::rmvSelectionGate(App::Document* doc) { - auto foundGate = docSelectionGate.find(doc); - if (foundGate != docSelectionGate.end()) { - delete foundGate->second; - docSelectionGate.erase(foundGate); + auto foundContext = docSelectionContext.find(doc); + if (foundContext != docSelectionContext.end() && foundContext->second.gate) { + delete foundContext->second.gate; + foundContext->second.gate = nullptr; // if a document is about to be closed it has no MDI view any more if (Gui::Document* guiDoc = Gui::Application::Instance->getDocument(doc)) { @@ -1137,7 +1148,9 @@ int SelectionSingleton::enableCommandLog(bool silent) } } else { - for (auto& sel : _SelList) { + SelectionContext* context = getSelectionContext(nullptr); // get selection context of + // current active file + for (auto& sel : context->selList) { sel.log(); } } @@ -1145,7 +1158,7 @@ int SelectionSingleton::enableCommandLog(bool silent) return logDisabled; } -void SelectionSingleton::_SelObj::log(bool remove, bool clearPreselect) +void SelectionSingleton::SelectionDescription::log(bool remove, bool clearPreselect) { if (logged && !remove) { return; @@ -1170,7 +1183,7 @@ void SelectionSingleton::_SelObj::log(bool remove, bool clearPreselect) Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); } -std::string SelectionSingleton::_SelObj::getSubString() const +std::string SelectionSingleton::SelectionDescription::getSubString() const { if (!SubName.empty()) { if (!elementName.oldName.empty() && !elementName.newName.empty()) { @@ -1193,11 +1206,16 @@ bool SelectionSingleton::addSelection( bool clearPreselect ) { + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + if (pickedList) { - _PickedList.clear(); + context->pickedList.clear(); for (const auto& sel : *pickedList) { - _PickedList.emplace_back(); - auto& s = _PickedList.back(); + context->pickedList.emplace_back(); + auto& s = context->pickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; @@ -1211,7 +1229,7 @@ bool SelectionSingleton::addSelection( notify(SelectionChanges(SelectionChanges::PickedListChanged)); } - _SelObj temp; + SelectionDescription temp; int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp); if (ret != 0) { return false; @@ -1221,26 +1239,21 @@ bool SelectionSingleton::addSelection( temp.y = y; temp.z = z; - App::Document* doc = getDocument(pDocName); - if (!doc) { - return false; - } - auto foundGate = docSelectionGate.find(doc); + // check for a Selection Gate - if (foundGate != docSelectionGate.end()) { - SelectionGate* gate = foundGate->second; + if (context->gate) { const char* subelement = nullptr; auto pObject = getObjectOfType( temp, App::DocumentObject::getClassTypeId(), - gate->resolveMode, + context->resolveMode, &subelement ); - if (!gate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { + if (!context->gate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { if (getMainWindow()) { QString msg; - if (gate->notAllowedReason.length() > 0) { - msg = QObject::tr(gate->notAllowedReason.c_str()); + if (context->gate->notAllowedReason.length() > 0) { + msg = QObject::tr(context->gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate( @@ -1252,7 +1265,7 @@ bool SelectionSingleton::addSelection( Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(Qt::ForbiddenCursor); } - gate->notAllowedReason.clear(); + context->gate->notAllowedReason.clear(); QApplication::beep(); return false; } @@ -1262,8 +1275,8 @@ bool SelectionSingleton::addSelection( temp.log(false, clearPreselect); } - _SelList.push_back(temp); - _SelStackForward.clear(); + context->selList.push_back(temp); + context->selStackForward.clear(); if (clearPreselect) { rmvPreselect(); @@ -1296,8 +1309,13 @@ bool SelectionSingleton::addSelection( return isSelected(temp.DocName.c_str(), temp.FeatName.c_str(), temp.SubName.c_str()); } -void SelectionSingleton::selStackPush(bool clearForward, bool overwrite) +void SelectionSingleton::selStackPush(bool clearForward, bool overwrite, const char* pDocName) { + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + static int stackSize; if (!stackSize) { stackSize = App::GetApplication() @@ -1305,51 +1323,56 @@ void SelectionSingleton::selStackPush(bool clearForward, bool overwrite) ->GetInt("SelectionStackSize", 100); } if (clearForward) { - _SelStackForward.clear(); + context->selStackForward.clear(); } - if (_SelList.empty()) { + if (context->selList.empty()) { return; } - if ((int)_SelStackBack.size() >= stackSize) { - _SelStackBack.pop_front(); + if ((int)context->selStackBack.size() >= stackSize) { + context->selStackBack.pop_front(); } SelStackItem item; - for (auto& sel : _SelList) { + for (auto& sel : context->selList) { item.emplace(sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str()); } - if (!_SelStackBack.empty() && _SelStackBack.back() == item) { + if (!context->selStackBack.empty() && context->selStackBack.back() == item) { return; } - if (!overwrite || _SelStackBack.empty()) { - _SelStackBack.emplace_back(); + if (!overwrite || context->selStackBack.empty()) { + context->selStackBack.emplace_back(); } - _SelStackBack.back() = std::move(item); + context->selStackBack.back() = std::move(item); } -void SelectionSingleton::selStackGoBack(int count) +void SelectionSingleton::selStackGoBack(int count, const char* pDocName) { - if ((int)_SelStackBack.size() < count) { - count = _SelStackBack.size(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + + if ((int)context->selStackBack.size() < count) { + count = context->selStackBack.size(); } if (count <= 0) { return; } - if (!_SelList.empty()) { - selStackPush(false, true); - clearCompleteSelection(); + if (!context->selList.empty()) { + selStackPush(false, true, pDocName); + clearCompleteSelection(pDocName); } else { --count; } for (int i = 0; i < count; ++i) { - _SelStackForward.push_front(std::move(_SelStackBack.back())); - _SelStackBack.pop_back(); + context->selStackForward.push_front(std::move(context->selStackBack.back())); + context->selStackBack.pop_back(); } std::deque tmpStack; - _SelStackForward.swap(tmpStack); - while (!_SelStackBack.empty()) { + context->selStackForward.swap(tmpStack); + while (!context->selStackBack.empty()) { bool found = false; - for (auto& sobjT : _SelStackBack.back()) { + for (auto& sobjT : context->selStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), @@ -1362,34 +1385,39 @@ void SelectionSingleton::selStackGoBack(int count) if (found) { break; } - tmpStack.push_front(std::move(_SelStackBack.back())); - _SelStackBack.pop_back(); + tmpStack.push_front(std::move(context->selStackBack.back())); + context->selStackBack.pop_back(); } - _SelStackForward = std::move(tmpStack); + context->selStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } -void SelectionSingleton::selStackGoForward(int count) +void SelectionSingleton::selStackGoForward(int count, const char* pDocName) { - if ((int)_SelStackForward.size() < count) { - count = _SelStackForward.size(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + + if ((int)context->selStackForward.size() < count) { + count = context->selStackForward.size(); } if (count <= 0) { return; } - if (!_SelList.empty()) { - selStackPush(false, true); - clearCompleteSelection(); + if (!context->selList.empty()) { + selStackPush(false, true, pDocName); + clearCompleteSelection(pDocName); } for (int i = 0; i < count; ++i) { - _SelStackBack.push_back(_SelStackForward.front()); - _SelStackForward.pop_front(); + context->selStackBack.push_back(context->selStackForward.front()); + context->selStackForward.pop_front(); } std::deque tmpStack; - _SelStackForward.swap(tmpStack); + context->selStackForward.swap(tmpStack); while (true) { bool found = false; - for (auto& sobjT : _SelStackBack.back()) { + for (auto& sobjT : context->selStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), @@ -1402,10 +1430,10 @@ void SelectionSingleton::selStackGoForward(int count) if (found || tmpStack.empty()) { break; } - _SelStackBack.push_back(tmpStack.front()); + context->selStackBack.push_back(tmpStack.front()); tmpStack.pop_front(); } - _SelStackForward = std::move(tmpStack); + context->selStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } @@ -1415,24 +1443,29 @@ std::vector SelectionSingleton::selStackGet( int index ) const { + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return {}; + } + const SelStackItem* item = nullptr; if (index >= 0) { - if (index >= (int)_SelStackBack.size()) { + if (index >= (int)context->selStackBack.size()) { return {}; } - item = &_SelStackBack[_SelStackBack.size() - 1 - index]; + item = &context->selStackBack[context->selStackBack.size() - 1 - index]; } else { index = -index - 1; - if (index >= (int)_SelStackForward.size()) { + if (index >= (int)context->selStackForward.size()) { return {}; } - item = &_SelStackBack[_SelStackForward.size() - 1 - index]; + item = &context->selStackBack[context->selStackForward.size() - 1 - index]; } - std::list<_SelObj> selList; + std::list selList; for (auto& sobjT : *item) { - _SelObj sel; + SelectionDescription sel; if (checkSelection( sobjT.getDocumentName().c_str(), sobjT.getObjectName().c_str(), @@ -1455,8 +1488,13 @@ bool SelectionSingleton::addSelections( const std::vector& pSubNames ) { - if (!_PickedList.empty()) { - _PickedList.clear(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + + if (!context->pickedList.empty()) { + context->pickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } @@ -1470,7 +1508,7 @@ bool SelectionSingleton::addSelections( bool update = false; for (const auto& pSubName : pSubNames) { - _SelObj temp; + SelectionDescription temp; int ret = checkSelection(pDocName, pObjectName, pSubName.c_str(), ResolveMode::NoResolve, temp); if (ret != 0) { continue; @@ -1490,8 +1528,8 @@ bool SelectionSingleton::addSelections( ss << temp.getSubString(); } - _SelList.push_back(temp); - _SelStackForward.clear(); + context->selList.push_back(temp); + context->selStackForward.clear(); SelectionChanges Chng( SelectionChanges::AddSelection, @@ -1609,11 +1647,16 @@ void SelectionSingleton::rmvSelection( const std::vector* pickedList ) { + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + if (pickedList) { - _PickedList.clear(); + context->pickedList.clear(); for (const auto& sel : *pickedList) { - _PickedList.emplace_back(); - auto& s = _PickedList.back(); + context->pickedList.emplace_back(); + auto& s = context->pickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; @@ -1631,14 +1674,14 @@ void SelectionSingleton::rmvSelection( return; } - _SelObj temp; + SelectionDescription temp; int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp); if (ret < 0) { return; } std::vector changes; - for (auto It = _SelList.begin(), ItNext = It; It != _SelList.end(); It = ItNext) { + for (auto It = context->selList.begin(), ItNext = It; It != context->selList.end(); It = ItNext) { ++ItNext; if (It->DocName != temp.DocName || It->FeatName != temp.FeatName) { continue; @@ -1663,8 +1706,8 @@ void SelectionSingleton::rmvSelection( It->TypeName ); - // destroy the _SelObj item - _SelList.erase(It); + // destroy the SelectionDescription item + context->selList.erase(It); } // NOTE: It can happen that there are nested calls of rmvSelection() @@ -1695,7 +1738,7 @@ struct SelInfo {} }; -void SelectionSingleton::setVisible(VisibleState vis) +void SelectionSingleton::setVisible(VisibleState vis, const char* pDocName) { std::set> filter; int visible; @@ -1710,10 +1753,15 @@ void SelectionSingleton::setVisible(VisibleState vis) visible = 0; } + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + // Copy the selection in case it changes during this function std::vector sels; - sels.reserve(_SelList.size()); - for (auto& sel : _SelList) { + sels.reserve(context->selList.size()); + for (auto& sel : context->selList) { if (sel.DocName.empty() || sel.FeatName.empty() || !sel.pObject) { continue; } @@ -1814,14 +1862,10 @@ void SelectionSingleton::setVisible(VisibleState vis) void SelectionSingleton::setSelection(const char* pDocName, const std::vector& sel) { - App::Document* pcDoc; - pcDoc = getDocument(pDocName); - if (!pcDoc) { - return; - } + SelectionContext* context = getSelectionContext(pDocName); - if (!_PickedList.empty()) { - _PickedList.clear(); + if (!context->pickedList.empty()) { + context->pickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } @@ -1830,18 +1874,18 @@ void SelectionSingleton::setSelection(const char* pDocName, const std::vectorisAttachedToDocument()) { continue; } - _SelObj temp; + SelectionDescription temp; int ret = checkSelection(pDocName, obj->getNameInDocument(), nullptr, ResolveMode::NoResolve, temp); if (ret != 0) { continue; } touched = true; - _SelList.push_back(temp); + context->selList.push_back(temp); } if (touched) { - _SelStackForward.clear(); + context->selStackForward.clear(); notify(SelectionChanges(SelectionChanges::SetSelection, pDocName)); getMainWindow()->updateActions(); } @@ -1853,58 +1897,64 @@ void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelec // clearSelection(0) behave as clearCompleteSelection(), which is the same // behavior of python Selection.clearSelection(None) if (!pDocName || !pDocName[0] || strcmp(pDocName, "*") == 0) { - clearCompleteSelection(clearPreSelect); + clearCompleteSelection(pDocName, clearPreSelect); return; } - if (!_PickedList.empty()) { - _PickedList.clear(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + + if (!context->pickedList.empty()) { + context->pickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } - App::Document* pDoc; - pDoc = getDocument(pDocName); - if (pDoc) { - std::string docName = pDocName; - if (clearPreSelect && DocName == docName) { - rmvPreselect(); - } - - bool touched = false; - for (auto it = _SelList.begin(); it != _SelList.end();) { - if (it->DocName == docName) { - touched = true; - it = _SelList.erase(it); - } - else { - ++it; - } - } - - if (!touched) { - return; - } - - if (!logDisabled) { - std::ostringstream ss; - ss << "Gui.Selection.clearSelection('" << docName << "'"; - if (!clearPreSelect) { - ss << ", False"; - } - ss << ')'; - Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); - } - - notify(SelectionChanges(SelectionChanges::ClrSelection, docName.c_str())); - - getMainWindow()->updateActions(); + std::string docName = pDocName == nullptr ? "" : pDocName; + if (clearPreSelect && DocName == docName) { + rmvPreselect(); } + + bool touched = false; + for (auto it = context->selList.begin(); it != context->selList.end();) { + if (it->DocName == docName) { + touched = true; + it = context->selList.erase(it); + } + else { + ++it; + } + } + + if (!touched) { + return; + } + + if (!logDisabled) { + std::ostringstream ss; + ss << "Gui.Selection.clearSelection('" << docName << "'"; + if (!clearPreSelect) { + ss << ", False"; + } + ss << ')'; + Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); + } + + notify(SelectionChanges(SelectionChanges::ClrSelection, docName.c_str())); + + getMainWindow()->updateActions(); } -void SelectionSingleton::clearCompleteSelection(bool clearPreSelect) +void SelectionSingleton::clearCompleteSelection(const char* pDocName, bool clearPreSelect) { - if (!_PickedList.empty()) { - _PickedList.clear(); + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + + if (!context->pickedList.empty()) { + context->pickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } @@ -1912,7 +1962,7 @@ void SelectionSingleton::clearCompleteSelection(bool clearPreSelect) rmvPreselect(); } - if (_SelList.empty()) { + if (context->selList.empty()) { return; } @@ -1927,7 +1977,7 @@ void SelectionSingleton::clearCompleteSelection(bool clearPreSelect) // objects being deselected. std::set viewProviders; - for (_SelObj& sel : _SelList) { + for (SelectionDescription& sel : context->selList) { if (auto vp = Application::Instance->getViewProvider(sel.pObject)) { viewProviders.insert(vp); } @@ -1938,7 +1988,7 @@ void SelectionSingleton::clearCompleteSelection(bool clearPreSelect) vp->onSelectionChanged(Chng); } - _SelList.clear(); + context->selList.clear(); SelectionChanges Chng(SelectionChanges::ClrSelection); @@ -1955,8 +2005,13 @@ bool SelectionSingleton::isSelected( ResolveMode resolve ) const { - _SelObj sel; - return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &_SelList) > 0; + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return false; + } + + SelectionDescription sel; + return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &context->selList) > 0; } bool SelectionSingleton::isSelected( @@ -1968,14 +2023,20 @@ bool SelectionSingleton::isSelected( if (!pObject || !pObject->isAttachedToDocument() || !pObject->getDocument()) { return false; } - _SelObj sel; + + auto foundContext = docSelectionContext.find(pObject->getDocument()); + if (foundContext == docSelectionContext.end()) { + return false; + } + + SelectionDescription sel; return checkSelection( pObject->getDocument()->getName(), pObject->getNameInDocument(), pSubName, resolve, sel, - &_SelList + &foundContext->second.selList ) > 0; } @@ -1985,8 +2046,8 @@ int SelectionSingleton::checkSelection( const char* pObjectName, const char* pSubName, ResolveMode resolve, - _SelObj& sel, - const std::list<_SelObj>* selList + SelectionDescription& sel, + const std::list* selList ) const { sel.pDoc = getDocument(pDocName); @@ -1996,8 +2057,9 @@ int SelectionSingleton::checkSelection( } return -1; } + pDocName = sel.pDoc->getName(); - sel.DocName = pDocName; + sel.DocName = pDocName == nullptr ? "" : pDocName; if (pObjectName) { sel.pObject = sel.pDoc->getObject(pObjectName); @@ -2056,7 +2118,12 @@ int SelectionSingleton::checkSelection( } } if (!selList) { - selList = &_SelList; + if (const SelectionContext* context = getSelectionContext(pDocName)) { + selList = &context->selList; + } + else { + return -1; + } } if (!pSubName) { @@ -2099,16 +2166,22 @@ const char* SelectionSingleton::getSelectedElement(App::DocumentObject* obj, con if (!obj) { return nullptr; } + auto foundContext = docSelectionContext.find(obj->getDocument()); + if (foundContext == docSelectionContext.end()) { + return nullptr; + } + const SelectionContext& context = foundContext->second; - for (list<_SelObj>::const_iterator It = _SelList.begin(); It != _SelList.end(); ++It) { - if (It->pObject == obj) { - auto len = It->SubName.length(); + for (auto selected : context.selList) { + if (selected.pObject == obj) { + auto len = selected.SubName.length(); if (!len) { return ""; } - if (pSubName && strncmp(pSubName, It->SubName.c_str(), It->SubName.length()) == 0) { + if (pSubName + && strncmp(pSubName, selected.SubName.c_str(), selected.SubName.length()) == 0) { if (pSubName[len] == 0 || pSubName[len - 1] == '.') { - return It->SubName.c_str(); + return selected.SubName.c_str(); } } } @@ -2125,10 +2198,12 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) // For safety reason, don't bother checking rmvPreselect(); + SelectionContext& context = docSelectionContext[Obj.getDocument()]; + // Remove also from the selection, if selected // We don't walk down the hierarchy for each selection, so there may be stray selection std::vector changes; - for (auto it = _SelList.begin(), itNext = it; it != _SelList.end(); it = itNext) { + for (auto it = context.selList.begin(), itNext = it; it != context.selList.end(); it = itNext) { ++itNext; if (it->pResolvedObject == &Obj || it->pObject == &Obj) { changes.emplace_back( @@ -2138,7 +2213,7 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) it->SubName, it->TypeName ); - _SelList.erase(it); + context.selList.erase(it); } } if (!changes.empty()) { @@ -2151,15 +2226,16 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) getMainWindow()->updateActions(); } - if (!_PickedList.empty()) { + if (!context.pickedList.empty()) { bool changed = false; - for (auto it = _PickedList.begin(), itNext = it; it != _PickedList.end(); it = itNext) { + for (auto it = context.pickedList.begin(), itNext = it; it != context.pickedList.end(); + it = itNext) { ++itNext; auto& sel = *it; if (sel.DocName == Obj.getDocument()->getName() && sel.FeatName == Obj.getNameInDocument()) { changed = true; - _PickedList.erase(it); + context.pickedList.erase(it); } } if (changed) { @@ -2167,15 +2243,69 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) } } } - -void SelectionSingleton::setSelectionStyle(SelectionStyle selStyle) +void SelectionSingleton::slotClosedDocument(const App::Document& doc) { - selectionStyle = selStyle; + // const_cast is ok because we just use doc as a key + docSelectionContext.erase(const_cast(&doc)); } -SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle() +void SelectionSingleton::setSelectionStyle(SelectionStyle selStyle, const char* pDocName) { - return selectionStyle; + SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return; + } + context->selectionStyle = selStyle; +} + +SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle(const char* pDocName) +{ + const SelectionContext* context = getSelectionContext(pDocName); + if (!context) { + return SelectionSingleton::SelectionStyle::NormalSelection; + } + return context->selectionStyle; +} +SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionContext(const char* pDocName) +{ + // Some functions might receive "*" for document selection. + // This is because there used to be a single selection context + // for the whole application so "*" meant all documents + // now that there is a selection context per document + // we interpret it as "active document" because most (all?) + // operations on freecad are meant to act on the current active + // document anyway + if (pDocName && strcmp(pDocName, "*") == 0) { + pDocName = nullptr; + } + + if (App::Document* doc = getDocument(pDocName)) { + return &docSelectionContext[doc]; + } + return nullptr; +} +const SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionContext( + const char* pDocName +) const +{ + // Some functions might receive "*" for document selection. + // This is because there used to be a single selection context + // for the whole application so "*" meant all documents + // now that there is a selection context per document + // we interpret it as "active document" because most (all?) + // operations on freec ad are meant to act on the current active + // document anyway + if (pDocName && strcmp(pDocName, "*") == 0) { + pDocName = nullptr; + } + + if (App::Document* doc = getDocument(pDocName)) { + auto foundContext = docSelectionContext.find(doc); + if (foundContext != docSelectionContext.end()) { + return &foundContext->second; + } + } + return nullptr; } //************************************************************************** @@ -2187,16 +2317,15 @@ SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle() */ SelectionSingleton::SelectionSingleton() : CurrentPreselection(SelectionChanges::ClrSelection) - , selectionStyle(SelectionStyle::NormalSelection) { - hx = 0; - hy = 0; - hz = 0; - // NOLINTBEGIN App::GetApplication().signalDeletedObject.connect( std::bind(&Gui::SelectionSingleton::slotDeletedObject, this, sp::_1) ); + App::GetApplication().signalDeleteDocument.connect( + std::bind(&Gui::SelectionSingleton::slotClosedDocument, this, sp::_1) + ); + signalSelectionChanged.connect( std::bind(&Gui::SelectionSingleton::slotSelectionChanged, this, sp::_1) ); diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index 4929f08158..b396be9d27 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -284,7 +284,6 @@ public: * literal into QT_TR_NOOP() for translatability. */ std::string notAllowedReason; - ResolveMode resolveMode; }; /** SelectionGateFilterExternal @@ -383,7 +382,7 @@ public: /// of the active document is cleared. void clearSelection(const char* pDocName = nullptr, bool clearPreSelect = true); /// Clear the selection of all documents - void clearCompleteSelection(bool clearPreSelect = true); + void clearCompleteSelection(const char* pDocName = nullptr, bool clearPreSelect = true); /// Check if selected bool isSelected( const char* pDocName, @@ -505,8 +504,9 @@ public: /** Set selection object visibility * * @param visible: see VisibleState + * @param pDocName: name of the document that scopes the request, defaults to active document */ - void setVisible(VisibleState visible); + void setVisible(VisibleState visible, const char* pDocName = nullptr); bool isClarifySelectionActive(); void setClarifySelectionActive(bool active); @@ -590,7 +590,7 @@ public: std::vector getCompleteSelection(ResolveMode resolve = ResolveMode::OldStyleElement) const; /// Check if there is any selection - bool hasSelection() const; + bool hasSelection(const char* pDocName = nullptr) const; /** Check if there is any selection within a given document * @@ -604,7 +604,7 @@ public: * If \c resolve is false, then the match is only done with the top * level parent object. */ - bool hasSelection(const char* doc, ResolveMode resolve = ResolveMode::OldStyleElement) const; + bool hasSelection(const char* doc, ResolveMode resolve) const; /** Check if there is any sub-element selection * @@ -621,10 +621,7 @@ public: bool hasPreselection() const; /// Size of selected entities for all documents - unsigned int size() const - { - return static_cast(_SelList.size()); - } + unsigned int size(const char* pDocName = nullptr) const; /** @name Selection stack functions * @@ -633,16 +630,10 @@ public: */ //@{ /// Return the current selection stack size - std::size_t selStackBackSize() const - { - return _SelStackBack.size(); - } + std::size_t selStackBackSize(const char* pDocName = nullptr) const; /// Return the current forward selection stack size - std::size_t selStackForwardSize() const - { - return _SelStackForward.size(); - } + std::size_t selStackForwardSize(const char* pDocName = nullptr) const; /** Obtain selected objects from stack * @@ -660,28 +651,31 @@ public: /** Go back selection history * * @param count: optional number of steps to go back + * @param pDocName: the name of the document to index the context, defaults to active document * * This function pops the selection stack, and populate the current * selection with the content of the last pop'd entry */ - void selStackGoBack(int count = 1); + void selStackGoBack(int count = 1, const char* pDocName = nullptr); /** Go forward selection history * * @param count: optional number of steps to go back + * @param pDocName: the name of the document to index the context, defaults to active document * * This function pops the selection stack, and populate the current * selection with the content of the last pop'd entry */ - void selStackGoForward(int count = 1); + void selStackGoForward(int count = 1, const char* pDocName = nullptr); /** Save the current selection on to the stack * * @param clearForward: whether to clear forward selection stack * @param overwrite: whether to overwrite the current top entry of the * stack instead of pushing a new entry. + * @param pDocName: the name of the document to index the context, defaults to active document */ - void selStackPush(bool clearForward = true, bool overwrite = false); + void selStackPush(bool clearForward = true, bool overwrite = false, const char* pDocName = nullptr); //@} /** @name Picked list functions @@ -692,11 +686,11 @@ public: */ //@{ /// Check whether picked list is enabled - bool needPickedList() const; + bool needPickedList(const char* pDocName = nullptr) const; /// Turn on or off picked list - void enablePickedList(bool); + void enablePickedList(bool, const char* pDocName = nullptr); /// Check if there is any selection inside picked list - bool hasPickedList() const; + bool hasPickedList(const char* pDocName = nullptr) const; /// Return select objects inside picked list std::vector getPickedList(const char* pDocName) const; /// Return selected object inside picked list grouped by top level parents @@ -718,9 +712,9 @@ public: GreedySelection }; /// Changes the style of selection between greedy and normal. - void setSelectionStyle(SelectionStyle selStyle); + void setSelectionStyle(SelectionStyle selStyle, const char* pDocName = nullptr); /// Get the style of selection. - SelectionStyle getSelectionStyle(); + SelectionStyle getSelectionStyle(const char* pDocName = nullptr); //@} static SelectionSingleton& instance(); @@ -766,24 +760,20 @@ protected: /// Observer message from the App doc void slotDeletedObject(const App::DocumentObject&); + void slotClosedDocument(const App::Document&); /// helper to retrieve document by name App::Document* getDocument(const char* pDocName = nullptr) const; void slotSelectionChanged(const SelectionChanges& msg); - SelectionChanges CurrentPreselection; - - std::deque NotificationQueue; - bool Notifying = false; - void notify(SelectionChanges&& Chng); void notify(const SelectionChanges& Chng) { notify(SelectionChanges(Chng)); } - struct _SelObj + struct SelectionDescription { std::string DocName; std::string FeatName; @@ -802,34 +792,26 @@ protected: void log(bool remove = false, bool clearPreselect = true); std::string getSubString() const; }; - mutable std::list<_SelObj> _SelList; - - mutable std::list<_SelObj> _PickedList; - bool _needPickedList {false}; - - using SelStackItem = std::set; - std::deque _SelStackBack; - std::deque _SelStackForward; int checkSelection( const char* pDocName, const char* pObjectName, const char* pSubName, ResolveMode resolve, - _SelObj& sel, - const std::list<_SelObj>* selList = nullptr + SelectionDescription& sel, + const std::list* selList = nullptr ) const; std::vector getObjectList( const char* pDocName, Base::Type typeId, - std::list<_SelObj>& objs, + const std::list& objs, ResolveMode resolve, bool single = false ) const; static App::DocumentObject* getObjectOfType( - _SelObj& sel, + const SelectionDescription& sel, Base::Type type, ResolveMode resolve, const char** subelement = nullptr @@ -841,23 +823,46 @@ protected: ResolveMode resolve = ResolveMode::OldStyleElement ) const; + using SelStackItem = std::set; + // Each document has a description context + struct SelectionContext + { + Gui::SelectionGate* gate {nullptr}; + ResolveMode resolveMode {ResolveMode::OldStyleElement}; + + std::list selList; + std::list pickedList; + bool needPickedList {false}; + + std::deque selStackBack; + std::deque selStackForward; + + SelectionStyle selectionStyle {SelectionStyle::NormalSelection}; + }; + + // Returns a selection context or nullptr if the document is not found + SelectionContext* getSelectionContext(const char* pDocName); + const SelectionContext* getSelectionContext(const char* pDocName) const; + static SelectionSingleton* _pcSingleton; - // each document can have at most 1 selection gate active - std::map docSelectionGate; - // Preselection helpers - it's a mess + std::map docSelectionContext; + + // Preselection helpers, it's a mess, needs clarifying -theo-vt std::string DocName; std::string FeatName; std::string SubName; - float hx, hy, hz; + float hx {0.0f}, hy {0.0f}, hz {0.0f}; + SelectionChanges CurrentPreselection; - int logDisabled = 0; - bool logHasSelection = false; - bool clarifySelectionActive = false; + int logDisabled {0}; + bool logHasSelection {false}; + bool clarifySelectionActive {false}; - SelectionStyle selectionStyle; + std::deque NotificationQueue; + bool Notifying {false}; }; /** diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 6f2e4bc653..c6d9756737 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -3722,7 +3722,7 @@ void TreeWidget::scrollItemToTop() continue; } auto doc = docItem->document()->getDocument(); - if (Gui::Selection().hasSelection(doc->getName())) { + if (Gui::Selection().hasSelection(doc->getName(), ResolveMode::OldStyleElement)) { tree->currentDocItem = docItem; docItem->selectItems(DocumentItem::SR_FORCE_EXPAND); tree->currentDocItem = nullptr; From 5c6bf5461da7c28dbd827eb5fa52af464d872a6d Mon Sep 17 00:00:00 2001 From: theo-vt Date: Wed, 25 Feb 2026 22:25:29 -0500 Subject: [PATCH 06/12] Remove most activate-deactivate --- src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp | 10 --------- src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h | 3 --- src/Mod/Fem/Gui/TaskFemConstraint.cpp | 9 -------- src/Mod/Fem/Gui/TaskFemConstraint.h | 3 --- src/Mod/Measure/Gui/TaskMeasure.cpp | 4 ---- src/Mod/Part/Gui/DlgExtrusion.cpp | 9 -------- src/Mod/Part/Gui/DlgExtrusion.h | 2 -- src/Mod/Part/Gui/DlgFilletEdges.cpp | 9 -------- src/Mod/Part/Gui/DlgFilletEdges.h | 2 -- src/Mod/Part/Gui/DlgProjectionOnSurface.cpp | 18 --------------- src/Mod/Part/Gui/DlgProjectionOnSurface.h | 4 ---- src/Mod/Part/Gui/DlgRevolution.cpp | 19 ++-------------- src/Mod/Part/Gui/DlgRevolution.h | 3 --- src/Mod/Part/Gui/Mirroring.cpp | 9 -------- src/Mod/Part/Gui/Mirroring.h | 2 -- src/Mod/Part/Gui/TaskAttacher.cpp | 12 ---------- src/Mod/Part/Gui/TaskAttacher.h | 3 --- src/Mod/Part/Gui/TaskFaceAppearances.cpp | 9 -------- src/Mod/Part/Gui/TaskFaceAppearances.h | 2 -- src/Mod/Part/Gui/TaskShapeBuilder.cpp | 9 -------- src/Mod/Part/Gui/TaskShapeBuilder.h | 2 -- .../PartDesign/Gui/TaskBooleanParameters.cpp | 9 -------- .../PartDesign/Gui/TaskBooleanParameters.h | 2 -- .../PartDesign/Gui/TaskDressUpParameters.cpp | 9 -------- .../PartDesign/Gui/TaskDressUpParameters.h | 2 -- .../PartDesign/Gui/TaskFeatureParameters.cpp | 22 ------------------- .../PartDesign/Gui/TaskFeatureParameters.h | 2 -- src/Mod/PartDesign/Gui/TaskFeaturePick.cpp | 8 ------- src/Mod/PartDesign/Gui/TaskFeaturePick.h | 3 --- src/Mod/PartDesign/Gui/TaskShapeBinder.cpp | 8 ------- src/Mod/PartDesign/Gui/TaskShapeBinder.h | 2 -- .../Gui/TaskTransformedParameters.cpp | 9 -------- .../Gui/TaskTransformedParameters.h | 3 --- src/Mod/Surface/Gui/TaskFilling.cpp | 17 -------------- src/Mod/Surface/Gui/TaskFilling.h | 2 -- src/Mod/Surface/Gui/TaskGeomFillSurface.cpp | 8 ------- src/Mod/Surface/Gui/TaskGeomFillSurface.h | 2 -- src/Mod/Surface/Gui/TaskSections.cpp | 9 -------- src/Mod/Surface/Gui/TaskSections.h | 2 -- 39 files changed, 2 insertions(+), 260 deletions(-) diff --git a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp index 0b0d1a4bd2..49c160ba07 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp +++ b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.cpp @@ -102,14 +102,4 @@ bool TaskDlgCreateNodeSet::reject() void TaskDlgCreateNodeSet::helpRequested() {} -void TaskDlgCreateNodeSet::activate() -{ - param->attachSelection(); - param->setSelectionGate(); -} -void TaskDlgCreateNodeSet::deactivate() -{ - param->detachSelection(); -} - #include "moc_TaskDlgCreateNodeSet.cpp" diff --git a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h index 201b658602..a6e38bbc0a 100644 --- a/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h +++ b/src/Mod/Fem/Gui/TaskDlgCreateNodeSet.h @@ -62,9 +62,6 @@ public: /// is called by the framework if the user press the help button void helpRequested() override; - void activate() override; - void deactivate() override; - /// returns for Close and Help button QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.cpp b/src/Mod/Fem/Gui/TaskFemConstraint.cpp index 0850f09e79..00a58286d6 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraint.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraint.cpp @@ -273,13 +273,4 @@ bool TaskDlgFemConstraint::reject() return true; } -void TaskDlgFemConstraint::activate() -{ - parameter->attachSelection(); -} -void TaskDlgFemConstraint::deactivate() -{ - parameter->detachSelection(); -} - #include "moc_TaskFemConstraint.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.h b/src/Mod/Fem/Gui/TaskFemConstraint.h index dd1746d704..6ef6a25051 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraint.h +++ b/src/Mod/Fem/Gui/TaskFemConstraint.h @@ -112,9 +112,6 @@ public: /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; - bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/Measure/Gui/TaskMeasure.cpp b/src/Mod/Measure/Gui/TaskMeasure.cpp index e3a0a44491..44042ba39a 100644 --- a/src/Mod/Measure/Gui/TaskMeasure.cpp +++ b/src/Mod/Measure/Gui/TaskMeasure.cpp @@ -626,14 +626,10 @@ void TaskMeasure::reset() void TaskMeasure::activate() { updateSelectionType(); - // engage the selectionObserver - attachSelection(); qApp->installEventFilter(this); } void TaskMeasure::deactivate() { - Gui::Selection().setSelectionStyle(SelectionStyle::NormalSelection); - detachSelection(); qApp->removeEventFilter(this); } diff --git a/src/Mod/Part/Gui/DlgExtrusion.cpp b/src/Mod/Part/Gui/DlgExtrusion.cpp index f85dd1983d..556981a801 100644 --- a/src/Mod/Part/Gui/DlgExtrusion.cpp +++ b/src/Mod/Part/Gui/DlgExtrusion.cpp @@ -936,14 +936,5 @@ void TaskExtrusion::clicked(int id) }; } } -void TaskExtrusion::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskExtrusion::deactivate() -{ - widget->detachSelection(); -} #include "moc_DlgExtrusion.cpp" diff --git a/src/Mod/Part/Gui/DlgExtrusion.h b/src/Mod/Part/Gui/DlgExtrusion.h index 775f5e80a7..fb182294c0 100644 --- a/src/Mod/Part/Gui/DlgExtrusion.h +++ b/src/Mod/Part/Gui/DlgExtrusion.h @@ -117,8 +117,6 @@ public: bool accept() override; bool reject() override; void clicked(int) override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index de35b0184b..9268a6564b 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -1197,15 +1197,6 @@ bool TaskFilletEdges::reject() Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); return true; } -void TaskFilletEdges::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskFilletEdges::deactivate() -{ - widget->detachSelection(); -} // -------------------------------------------------------------- diff --git a/src/Mod/Part/Gui/DlgFilletEdges.h b/src/Mod/Part/Gui/DlgFilletEdges.h index 798d00ed37..dc4ede763b 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.h +++ b/src/Mod/Part/Gui/DlgFilletEdges.h @@ -187,8 +187,6 @@ public: void clicked(int) override; bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp index cb778d002d..b376ac3598 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp @@ -1225,15 +1225,6 @@ bool TaskProjectionOnSurface::reject() widget->reject(); return true; } -void TaskProjectionOnSurface::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskProjectionOnSurface::deactivate() -{ - widget->detachSelection(); -} void TaskProjectionOnSurface::clicked(int id) { @@ -1661,14 +1652,5 @@ bool TaskProjectOnSurface::reject() resetEdit(); return true; } -void TaskProjectOnSurface::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskProjectOnSurface::deactivate() -{ - widget->detachSelection(); -} #include "moc_DlgProjectionOnSurface.cpp" diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.h b/src/Mod/Part/Gui/DlgProjectionOnSurface.h index 78a2c354d7..a2694294f2 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.h +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.h @@ -172,8 +172,6 @@ public: bool accept() override; bool reject() override; void clicked(int id) override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { @@ -250,8 +248,6 @@ public: public: bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/DlgRevolution.cpp b/src/Mod/Part/Gui/DlgRevolution.cpp index 722716fa4f..6a59d21fe9 100644 --- a/src/Mod/Part/Gui/DlgRevolution.cpp +++ b/src/Mod/Part/Gui/DlgRevolution.cpp @@ -528,19 +528,13 @@ void DlgRevolution::accept() QDialog::accept(); } -void DlgRevolution::setSelectionGate() -{ - if (filterSelection) { - filter = new EdgeSelection(); - Gui::Selection().addSelectionGate(filter); - } -} void DlgRevolution::onSelectLineClicked() { if (!filterSelection) { filterSelection = true; - setSelectionGate(); + filter = new EdgeSelection(); + Gui::Selection().addSelectionGate(filter); ui->selectLine->setText(tr("Selecting… (Line or Arc)")); } else { @@ -677,14 +671,5 @@ bool TaskRevolution::accept() widget->accept(); return (widget->result() == QDialog::Accepted); } -void TaskRevolution::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskRevolution::deactivate() -{ - widget->detachSelection(); -} #include "moc_DlgRevolution.cpp" diff --git a/src/Mod/Part/Gui/DlgRevolution.h b/src/Mod/Part/Gui/DlgRevolution.h index 236e9a2b5e..36722d1e01 100644 --- a/src/Mod/Part/Gui/DlgRevolution.h +++ b/src/Mod/Part/Gui/DlgRevolution.h @@ -42,7 +42,6 @@ public: explicit DlgRevolution(QWidget* parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags()); ~DlgRevolution() override; void accept() override; - void setSelectionGate(); Base::Vector3d getDirection() const; Base::Vector3d getPosition() const; @@ -96,8 +95,6 @@ public: public: bool accept() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/Mirroring.cpp b/src/Mod/Part/Gui/Mirroring.cpp index 7476465bbb..47cb363947 100644 --- a/src/Mod/Part/Gui/Mirroring.cpp +++ b/src/Mod/Part/Gui/Mirroring.cpp @@ -421,14 +421,5 @@ bool TaskMirroring::reject() { return widget->reject(); } -void TaskMirroring::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskMirroring::deactivate() -{ - widget->detachSelection(); -} #include "moc_Mirroring.cpp" diff --git a/src/Mod/Part/Gui/Mirroring.h b/src/Mod/Part/Gui/Mirroring.h index 402eb43a51..a5a38072e8 100644 --- a/src/Mod/Part/Gui/Mirroring.h +++ b/src/Mod/Part/Gui/Mirroring.h @@ -73,8 +73,6 @@ public: public: bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index bf044c94f9..2a48b7407b 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -1554,17 +1554,5 @@ bool TaskDlgAttacher::reject() return true; } -void TaskDlgAttacher::activate() -{ - if (parameter) { - parameter->attachSelection(); - } -} -void TaskDlgAttacher::deactivate() -{ - if (parameter) { - parameter->detachSelection(); - } -} #include "moc_TaskAttacher.cpp" diff --git a/src/Mod/Part/Gui/TaskAttacher.h b/src/Mod/Part/Gui/TaskAttacher.h index 09608ff218..dc6e673eb2 100644 --- a/src/Mod/Part/Gui/TaskAttacher.h +++ b/src/Mod/Part/Gui/TaskAttacher.h @@ -215,9 +215,6 @@ public: /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; - bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/Part/Gui/TaskFaceAppearances.cpp b/src/Mod/Part/Gui/TaskFaceAppearances.cpp index 72e6859c1b..d73e77fc7b 100644 --- a/src/Mod/Part/Gui/TaskFaceAppearances.cpp +++ b/src/Mod/Part/Gui/TaskFaceAppearances.cpp @@ -545,14 +545,5 @@ bool TaskFaceAppearances::reject() { return widget->reject(); } -void TaskFaceAppearances::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskFaceAppearances::deactivate() -{ - widget->detachSelection(); -} #include "moc_TaskFaceAppearances.cpp" diff --git a/src/Mod/Part/Gui/TaskFaceAppearances.h b/src/Mod/Part/Gui/TaskFaceAppearances.h index 7e9a1b1a34..2c921f4ab2 100644 --- a/src/Mod/Part/Gui/TaskFaceAppearances.h +++ b/src/Mod/Part/Gui/TaskFaceAppearances.h @@ -90,8 +90,6 @@ public: bool accept() override; bool reject() override; void clicked(int) override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Part/Gui/TaskShapeBuilder.cpp b/src/Mod/Part/Gui/TaskShapeBuilder.cpp index bfccef4114..7273ca1b06 100644 --- a/src/Mod/Part/Gui/TaskShapeBuilder.cpp +++ b/src/Mod/Part/Gui/TaskShapeBuilder.cpp @@ -644,14 +644,5 @@ bool TaskShapeBuilder::reject() { return widget->reject(); } -void TaskShapeBuilder::activate() -{ - widget->setSelectionGate(); - widget->attachSelection(); -} -void TaskShapeBuilder::deactivate() -{ - widget->detachSelection(); -} #include "moc_TaskShapeBuilder.cpp" diff --git a/src/Mod/Part/Gui/TaskShapeBuilder.h b/src/Mod/Part/Gui/TaskShapeBuilder.h index 8ceab390cf..b1fc60e08c 100644 --- a/src/Mod/Part/Gui/TaskShapeBuilder.h +++ b/src/Mod/Part/Gui/TaskShapeBuilder.h @@ -79,8 +79,6 @@ public: bool accept() override; bool reject() override; void clicked(int) override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp index d2bbd86654..e2ce0900fe 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp @@ -432,14 +432,5 @@ bool TaskDlgBooleanParameters::reject() return true; } -void TaskDlgBooleanParameters::activate() -{ - parameter->attachSelection(); -} -void TaskDlgBooleanParameters::deactivate() -{ - parameter->detachSelection(); -} - #include "moc_TaskBooleanParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h index f3c65083fc..87f14ea01e 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h @@ -112,8 +112,6 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; bool isAllowedAlterDocument() const override { diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp index e4f3178677..a129e5b7df 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp @@ -513,14 +513,5 @@ bool TaskDlgDressUpParameters::reject() getViewObject()->highlightReferences(false); return TaskDlgFeatureParameters::reject(); } -void TaskDlgDressUpParameters::activate() -{ - parameter->attachSelection(); - parameter->setSelectionGate(); -} -void TaskDlgDressUpParameters::deactivate() -{ - parameter->detachSelection(); -} #include "moc_TaskDressUpParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h index f20ec3d785..b203d4f0c5 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h @@ -139,8 +139,6 @@ public: /// is called by the framework if the dialog is accepted (Ok) bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; protected: TaskDressUpParameters* parameter; diff --git a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp index 1f1da97189..5c67248544 100644 --- a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp @@ -285,27 +285,5 @@ bool TaskDlgFeatureParameters::reject() return true; } -void TaskDlgFeatureParameters::activate() -{ - std::vector subwidgets = getDialogContent(); - for (auto it : subwidgets) { - TaskSketchBasedParameters* param = qobject_cast(it); - if (param) { - param->attachSelection(); - } - } -} -void TaskDlgFeatureParameters::deactivate() -{ - // detach the task panel from the selection to avoid to invoke - // eventually onAddSelection when the selection changes - std::vector subwidgets = getDialogContent(); - for (auto it : subwidgets) { - TaskSketchBasedParameters* param = qobject_cast(it); - if (param) { - param->detachSelection(); - } - } -} #include "moc_TaskFeatureParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskFeatureParameters.h b/src/Mod/PartDesign/Gui/TaskFeatureParameters.h index 74d7f24892..3746aec053 100644 --- a/src/Mod/PartDesign/Gui/TaskFeatureParameters.h +++ b/src/Mod/PartDesign/Gui/TaskFeatureParameters.h @@ -156,8 +156,6 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; template T* getViewObject() const diff --git a/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp b/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp index 5d91cd6801..286bdf137c 100644 --- a/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp +++ b/src/Mod/PartDesign/Gui/TaskFeaturePick.cpp @@ -677,14 +677,6 @@ bool TaskDlgFeaturePick::reject() accepted = false; return true; } -void TaskDlgFeaturePick::activate() -{ - pick->attachSelection(); -} -void TaskDlgFeaturePick::deactivate() -{ - pick->detachSelection(); -} void TaskDlgFeaturePick::showExternal(bool val) { diff --git a/src/Mod/PartDesign/Gui/TaskFeaturePick.h b/src/Mod/PartDesign/Gui/TaskFeaturePick.h index 96e8f792bd..4ef06e5802 100644 --- a/src/Mod/PartDesign/Gui/TaskFeaturePick.h +++ b/src/Mod/PartDesign/Gui/TaskFeaturePick.h @@ -130,9 +130,6 @@ public: /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; - bool isAllowedAlterDocument() const override { return false; diff --git a/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp b/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp index db1aa26b9f..dfdd8b2b2f 100644 --- a/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp +++ b/src/Mod/PartDesign/Gui/TaskShapeBinder.cpp @@ -461,13 +461,5 @@ bool TaskDlgShapeBinder::reject() } return true; } -void TaskDlgShapeBinder::activate() -{ - parameter->attachSelection(); -} -void TaskDlgShapeBinder::deactivate() -{ - parameter->detachSelection(); -} #include "moc_TaskShapeBinder.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskShapeBinder.h b/src/Mod/PartDesign/Gui/TaskShapeBinder.h index 61f54f9130..f097e61db7 100644 --- a/src/Mod/PartDesign/Gui/TaskShapeBinder.h +++ b/src/Mod/PartDesign/Gui/TaskShapeBinder.h @@ -112,8 +112,6 @@ public: bool accept() override; /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; protected: TaskShapeBinder* parameter; diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index a4c10bd78b..3896c9d6df 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -634,14 +634,5 @@ bool TaskDlgTransformedParameters::reject() parameter->exitSelectionMode(); return TaskDlgFeatureParameters::reject(); } -void TaskDlgTransformedParameters::activate() -{ - parameter->attachSelection(); -} -void TaskDlgTransformedParameters::deactivate() -{ - parameter->detachSelection(); -} - #include "moc_TaskTransformedParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h index 9be92754f6..8f322224db 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h @@ -236,9 +236,6 @@ public: /// is called by the framework if the dialog is rejected (Cancel) bool reject() override; - void activate() override; - void deactivate() override; - protected: TaskTransformedParameters* parameter = nullptr; TaskTransformedMessages* message = nullptr; diff --git a/src/Mod/Surface/Gui/TaskFilling.cpp b/src/Mod/Surface/Gui/TaskFilling.cpp index 5023770b83..60103ff771 100644 --- a/src/Mod/Surface/Gui/TaskFilling.cpp +++ b/src/Mod/Surface/Gui/TaskFilling.cpp @@ -1031,23 +1031,6 @@ bool TaskFilling::reject() return ok; } -void TaskFilling::activate() -{ - widget1->attachSelection(); - widget1->setSelectionGate(); - - widget2->attachSelection(); - widget2->setSelectionGate(); - - widget3->attachSelection(); - widget3->setSelectionGate(); -} -void TaskFilling::deactivate() -{ - widget1->detachSelection(); - widget2->detachSelection(); - widget3->detachSelection(); -} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskFilling.h b/src/Mod/Surface/Gui/TaskFilling.h index 144095fdfc..87a8ad149f 100644 --- a/src/Mod/Surface/Gui/TaskFilling.h +++ b/src/Mod/Surface/Gui/TaskFilling.h @@ -141,8 +141,6 @@ public: void closed() override; bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp b/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp index 715d793532..a550293355 100644 --- a/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp +++ b/src/Mod/Surface/Gui/TaskGeomFillSurface.cpp @@ -687,14 +687,6 @@ bool TaskGeomFillSurface::reject() { return widget->reject(); } -void TaskGeomFillSurface::activate() -{ - widget->attachSelection(); -} -void TaskGeomFillSurface::deactivate() -{ - widget->detachSelection(); -} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskGeomFillSurface.h b/src/Mod/Surface/Gui/TaskGeomFillSurface.h index 4c363b4c93..e2f23daa9d 100644 --- a/src/Mod/Surface/Gui/TaskGeomFillSurface.h +++ b/src/Mod/Surface/Gui/TaskGeomFillSurface.h @@ -129,8 +129,6 @@ public: void open() override; bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { diff --git a/src/Mod/Surface/Gui/TaskSections.cpp b/src/Mod/Surface/Gui/TaskSections.cpp index c27d0bfb2e..5ccf8e609d 100644 --- a/src/Mod/Surface/Gui/TaskSections.cpp +++ b/src/Mod/Surface/Gui/TaskSections.cpp @@ -670,15 +670,6 @@ bool TaskSections::reject() return ok; } -void TaskSections::activate() -{ - widget1->setSelectionGate(); - widget1->attachSelection(); -} -void TaskSections::deactivate() -{ - widget1->detachSelection(); -} } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/TaskSections.h b/src/Mod/Surface/Gui/TaskSections.h index 6f22d97d96..3f5d024ab1 100644 --- a/src/Mod/Surface/Gui/TaskSections.h +++ b/src/Mod/Surface/Gui/TaskSections.h @@ -132,8 +132,6 @@ public: void open() override; bool accept() override; bool reject() override; - void activate() override; - void deactivate() override; QDialogButtonBox::StandardButtons getStandardButtons() const override { From 7cb3815fb1385574abda9a68384008b9c0144101 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Thu, 26 Feb 2026 21:49:39 -0500 Subject: [PATCH 07/12] Filter events per document at the SelectionObserver level --- src/App/Application.cpp | 9 + src/App/Application.h | 2 + src/Gui/Selection/Selection.cpp | 430 ++++++++++++++++---------------- src/Gui/Selection/Selection.h | 19 +- 4 files changed, 243 insertions(+), 217 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 02a8482e6e..dd8c531037 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -587,6 +587,15 @@ Document* Application::getDocument(const char *Name) const return pos->second; } +Document* Application::getDocumentOrActive(const char *Name) const +{ + if (!Base::Tools::isNullOrEmpty(Name)) { + return getDocument(Name); + } + else { + return getActiveDocument(); + } +} const char * Application::getDocumentName(const Document* doc) const { diff --git a/src/App/Application.h b/src/App/Application.h index d32d5df5c6..9a425a4a57 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -205,6 +205,8 @@ public: */ App::Document* getDocument(const char *Name) const; + App::Document* getDocumentOrActive(const char *Name) const; + /// %Path matching modes for getDocumentByPath() enum class PathMatchMode { diff --git a/src/Gui/Selection/Selection.cpp b/src/Gui/Selection/Selection.cpp index 594c5c406b..e242f10d59 100644 --- a/src/Gui/Selection/Selection.cpp +++ b/src/Gui/Selection/Selection.cpp @@ -96,6 +96,9 @@ SelectionObserver::SelectionObserver(bool attach, ResolveMode resolve) : resolve(resolve) , blockedSelection(false) { + if (auto doc = App::GetApplication().getActiveDocument()) { + pDocumentScopeName = doc->getName(); + } if (attach) { attachSelection(); } @@ -105,6 +108,9 @@ SelectionObserver::SelectionObserver(const ViewProviderDocumentObject* vp, bool : resolve(resolve) , blockedSelection(false) { + if (auto doc = App::GetApplication().getActiveDocument()) { + pDocumentScopeName = doc->getName(); + } if (attach) { attachSelection(); } @@ -152,7 +158,8 @@ void SelectionObserver::attachSelection() void SelectionObserver::_onSelectionChanged(const SelectionChanges& msg) { try { - if (blockedSelection) { + if (blockedSelection + || (pDocumentScopeName && msg.pDocName && strcmp(pDocumentScopeName, msg.pDocName) != 0)) { return; } onSelectionChanged(msg); @@ -185,28 +192,28 @@ bool SelectionSingleton::hasPreselection() const unsigned int SelectionSingleton::size(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return 0; } - return static_cast(context->selList.size()); + return static_cast(context.info->selList.size()); } std::size_t SelectionSingleton::selStackBackSize(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return 0; } - return context->selStackBack.size(); + return context.info->selStackBack.size(); } std::size_t SelectionSingleton::selStackForwardSize(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return 0; } - return context->selStackForward.size(); + return context.info->selStackForward.size(); } std::vector SelectionSingleton::getCompleteSelection(ResolveMode resolve) const @@ -225,14 +232,14 @@ std::vector SelectionSingleton::getSelection( temp.reserve(1); } SelObj tempSelObj; - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } std::map> objMap; - for (auto& sel : context->selList) { + for (auto& sel : context.info->selList) { if (!sel.pDoc) { continue; } @@ -271,19 +278,19 @@ std::vector SelectionSingleton::getSelection( } bool SelectionSingleton::hasSelection(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - return !context->selList.empty(); + return !context.info->selList.empty(); } bool SelectionSingleton::hasSelection(const char* pDocName, ResolveMode resolve) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - for (auto& sel : context->selList) { + for (auto& sel : context.info->selList) { if (getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve)) { return true; } @@ -294,11 +301,11 @@ bool SelectionSingleton::hasSelection(const char* pDocName, ResolveMode resolve) bool SelectionSingleton::hasSubSelection(const char* pDocName, bool subElement) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - for (auto& sel : context->selList) { + for (auto& sel : context.info->selList) { if (sel.SubName.empty()) { continue; } @@ -318,12 +325,12 @@ std::vector SelectionSingleton::getPickedList(const std::vector temp; SelObj tempSelObj; - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } - for (const auto& picked : context->pickedList) { + for (const auto& picked : context.info->pickedList) { tempSelObj.DocName = picked.DocName.c_str(); tempSelObj.FeatName = picked.FeatName.c_str(); tempSelObj.SubName = picked.SubName.c_str(); @@ -446,11 +453,11 @@ std::vector SelectionSingleton::getSelectionEx( bool single ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } - return getObjectList(pDocName, typeId, context->selList, resolve, single); + return getObjectList(pDocName, typeId, context.info->selList, resolve, single); } std::vector SelectionSingleton::getPickedListEx( @@ -458,11 +465,11 @@ std::vector SelectionSingleton::getPickedListEx( Base::Type typeId ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } - return getObjectList(pDocName, typeId, context->pickedList, ResolveMode::NoResolve); + return getObjectList(pDocName, typeId, context.info->pickedList, ResolveMode::NoResolve); } std::vector SelectionSingleton::getObjectList( @@ -536,24 +543,24 @@ std::vector SelectionSingleton::getObjectList( bool SelectionSingleton::needPickedList(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - return context->needPickedList; + return context.info->needPickedList; } void SelectionSingleton::enablePickedList(bool enable, const char* pDocName) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - if (enable != context->needPickedList) { - context->needPickedList = enable; - context->pickedList.clear(); - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + if (enable != context.info->needPickedList) { + context.info->needPickedList = enable; + context.info->pickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } } @@ -624,11 +631,11 @@ void SelectionSingleton::notify(SelectionChanges&& Chng) bool SelectionSingleton::hasPickedList(const char* pDocName) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - return !context->pickedList.empty(); + return !context.info->pickedList.empty(); } int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList& prop) const @@ -703,13 +710,13 @@ vector SelectionSingleton::getObjectsOfType( ResolveMode resolve ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } std::set objs; - for (auto& sel : context->selList) { + for (auto& sel : context.info->selList) { if (App::DocumentObject* pObject = getObjectOfType(sel, typeId, resolve)) { objs.insert(pObject); } @@ -737,12 +744,12 @@ unsigned int SelectionSingleton::countObjectsOfType( ResolveMode resolve ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return 0; } - return std::count_if(context->selList.begin(), context->selList.end(), [&](auto& sel) { + return std::count_if(context.info->selList.begin(), context.info->selList.end(), [&](auto& sel) { return getObjectOfType(sel, typeId, resolve); }); } @@ -845,9 +852,9 @@ int SelectionSingleton::setPreselect( if (!pDoc) { return 0; } - SelectionContext* context = &docSelectionContext[pDoc]; + auto context = getSelectionContext(pDocName); - if (context->gate && signal != SelectionChanges::MsgSource::Internal) { + if (context.info->gate && signal != SelectionChanges::MsgSource::Internal) { App::ElementNamePair elementName; auto pObject = pDoc->getObject(pObjectName); if (!pObject) { @@ -855,14 +862,14 @@ int SelectionSingleton::setPreselect( } const char* subelement = pSubName; - if (context->resolveMode != ResolveMode::NoResolve) { + if (context.info->resolveMode != ResolveMode::NoResolve) { auto& newElementName = elementName.newName; auto& oldElementName = elementName.oldName; pObject = App::GeoFeature::resolveElement(pObject, pSubName, elementName); if (!pObject) { return 0; } - if (context->resolveMode > ResolveMode::OldStyleElement) { + if (context.info->resolveMode > ResolveMode::OldStyleElement) { subelement = !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str(); } @@ -870,10 +877,10 @@ int SelectionSingleton::setPreselect( subelement = oldElementName.c_str(); } } - if (!context->gate->allow(pObject->getDocument(), pObject, subelement)) { + if (!context.info->gate->allow(pObject->getDocument(), pObject, subelement)) { QString msg; - if (context->gate->notAllowedReason.length() > 0) { - msg = QObject::tr(context->gate->notAllowedReason.c_str()); + if (context.info->gate->notAllowedReason.length() > 0) { + msg = QObject::tr(context.info->gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate("SelectionFilter", "Not allowed:"); @@ -896,7 +903,7 @@ int SelectionSingleton::setPreselect( mdi->restoreOverrideCursor(); } - DocName = pDocName == nullptr ? "" : pDocName; + DocName = context.docName.c_str(); FeatName = pObjectName; SubName = pSubName; hx = x; @@ -907,7 +914,7 @@ int SelectionSingleton::setPreselect( SelectionChanges Chng( signal == SelectionChanges::MsgSource::Internal ? SelectionChanges::SetPreselectSignal : SelectionChanges::SetPreselect, - DocName, + context.docName.c_str(), FeatName, SubName, std::string(), @@ -1054,8 +1061,8 @@ void SelectionSingleton::rmvPreselect(bool signal) // reset the current preselection CurrentPreselection = SelectionChanges(); - const SelectionContext* context = getSelectionContext(DocName.c_str()); - if (context && context->gate && getMainWindow()) { + auto context = getSelectionContext(DocName.c_str()); + if (context.info && context.info->gate && getMainWindow()) { Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->restoreOverrideCursor(); } @@ -1088,9 +1095,9 @@ void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode } rmvSelectionGate(doc); - SelectionContext& context = docSelectionContext[doc]; - context.resolveMode = resolve; - context.gate = gate; + auto context = getSelectionContext(doc->getName()); + context.info->resolveMode = resolve; + context.info->gate = gate; } // remove the active SelectionGate @@ -1121,12 +1128,7 @@ void SelectionSingleton::rmvSelectionGate(const char* pDocName) App::Document* SelectionSingleton::getDocument(const char* pDocName) const { - if (!Base::Tools::isNullOrEmpty(pDocName)) { - return App::GetApplication().getDocument(pDocName); - } - else { - return App::GetApplication().getActiveDocument(); - } + return App::GetApplication().getDocumentOrActive(pDocName); } int SelectionSingleton::disableCommandLog() @@ -1148,9 +1150,9 @@ int SelectionSingleton::enableCommandLog(bool silent) } } else { - SelectionContext* context = getSelectionContext(nullptr); // get selection context of - // current active file - for (auto& sel : context->selList) { + auto context = getSelectionContext(nullptr); // get selection context of + // current active file + for (auto& sel : context.info->selList) { sel.log(); } } @@ -1206,16 +1208,16 @@ bool SelectionSingleton::addSelection( bool clearPreselect ) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } if (pickedList) { - context->pickedList.clear(); + context.info->pickedList.clear(); for (const auto& sel : *pickedList) { - context->pickedList.emplace_back(); - auto& s = context->pickedList.back(); + context.info->pickedList.emplace_back(); + auto& s = context.info->pickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; @@ -1226,7 +1228,7 @@ bool SelectionSingleton::addSelection( s.y = sel.y; s.z = sel.z; } - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } SelectionDescription temp; @@ -1241,19 +1243,20 @@ bool SelectionSingleton::addSelection( // check for a Selection Gate - if (context->gate) { + if (context.info->gate) { const char* subelement = nullptr; auto pObject = getObjectOfType( temp, App::DocumentObject::getClassTypeId(), - context->resolveMode, + context.info->resolveMode, &subelement ); - if (!context->gate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { + if (!context.info->gate + ->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { if (getMainWindow()) { QString msg; - if (context->gate->notAllowedReason.length() > 0) { - msg = QObject::tr(context->gate->notAllowedReason.c_str()); + if (context.info->gate->notAllowedReason.length() > 0) { + msg = QObject::tr(context.info->gate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate( @@ -1265,7 +1268,7 @@ bool SelectionSingleton::addSelection( Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(Qt::ForbiddenCursor); } - context->gate->notAllowedReason.clear(); + context.info->gate->notAllowedReason.clear(); QApplication::beep(); return false; } @@ -1275,8 +1278,8 @@ bool SelectionSingleton::addSelection( temp.log(false, clearPreselect); } - context->selList.push_back(temp); - context->selStackForward.clear(); + context.info->selList.push_back(temp); + context.info->selStackForward.clear(); if (clearPreselect) { rmvPreselect(); @@ -1284,7 +1287,7 @@ bool SelectionSingleton::addSelection( SelectionChanges Chng( SelectionChanges::AddSelection, - temp.DocName, + context.docName.c_str(), temp.FeatName, temp.SubName, temp.TypeName, @@ -1311,8 +1314,8 @@ bool SelectionSingleton::addSelection( void SelectionSingleton::selStackPush(bool clearForward, bool overwrite, const char* pDocName) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } @@ -1323,41 +1326,41 @@ void SelectionSingleton::selStackPush(bool clearForward, bool overwrite, const c ->GetInt("SelectionStackSize", 100); } if (clearForward) { - context->selStackForward.clear(); + context.info->selStackForward.clear(); } - if (context->selList.empty()) { + if (context.info->selList.empty()) { return; } - if ((int)context->selStackBack.size() >= stackSize) { - context->selStackBack.pop_front(); + if ((int)context.info->selStackBack.size() >= stackSize) { + context.info->selStackBack.pop_front(); } SelStackItem item; - for (auto& sel : context->selList) { + for (auto& sel : context.info->selList) { item.emplace(sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str()); } - if (!context->selStackBack.empty() && context->selStackBack.back() == item) { + if (!context.info->selStackBack.empty() && context.info->selStackBack.back() == item) { return; } - if (!overwrite || context->selStackBack.empty()) { - context->selStackBack.emplace_back(); + if (!overwrite || context.info->selStackBack.empty()) { + context.info->selStackBack.emplace_back(); } - context->selStackBack.back() = std::move(item); + context.info->selStackBack.back() = std::move(item); } void SelectionSingleton::selStackGoBack(int count, const char* pDocName) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - if ((int)context->selStackBack.size() < count) { - count = context->selStackBack.size(); + if ((int)context.info->selStackBack.size() < count) { + count = context.info->selStackBack.size(); } if (count <= 0) { return; } - if (!context->selList.empty()) { + if (!context.info->selList.empty()) { selStackPush(false, true, pDocName); clearCompleteSelection(pDocName); } @@ -1365,14 +1368,14 @@ void SelectionSingleton::selStackGoBack(int count, const char* pDocName) --count; } for (int i = 0; i < count; ++i) { - context->selStackForward.push_front(std::move(context->selStackBack.back())); - context->selStackBack.pop_back(); + context.info->selStackForward.push_front(std::move(context.info->selStackBack.back())); + context.info->selStackBack.pop_back(); } std::deque tmpStack; - context->selStackForward.swap(tmpStack); - while (!context->selStackBack.empty()) { + context.info->selStackForward.swap(tmpStack); + while (!context.info->selStackBack.empty()) { bool found = false; - for (auto& sobjT : context->selStackBack.back()) { + for (auto& sobjT : context.info->selStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), @@ -1385,39 +1388,39 @@ void SelectionSingleton::selStackGoBack(int count, const char* pDocName) if (found) { break; } - tmpStack.push_front(std::move(context->selStackBack.back())); - context->selStackBack.pop_back(); + tmpStack.push_front(std::move(context.info->selStackBack.back())); + context.info->selStackBack.pop_back(); } - context->selStackForward = std::move(tmpStack); + context.info->selStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } void SelectionSingleton::selStackGoForward(int count, const char* pDocName) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - if ((int)context->selStackForward.size() < count) { - count = context->selStackForward.size(); + if ((int)context.info->selStackForward.size() < count) { + count = context.info->selStackForward.size(); } if (count <= 0) { return; } - if (!context->selList.empty()) { + if (!context.info->selList.empty()) { selStackPush(false, true, pDocName); clearCompleteSelection(pDocName); } for (int i = 0; i < count; ++i) { - context->selStackBack.push_back(context->selStackForward.front()); - context->selStackForward.pop_front(); + context.info->selStackBack.push_back(context.info->selStackForward.front()); + context.info->selStackForward.pop_front(); } std::deque tmpStack; - context->selStackForward.swap(tmpStack); + context.info->selStackForward.swap(tmpStack); while (true) { bool found = false; - for (auto& sobjT : context->selStackBack.back()) { + for (auto& sobjT : context.info->selStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), @@ -1430,10 +1433,10 @@ void SelectionSingleton::selStackGoForward(int count, const char* pDocName) if (found || tmpStack.empty()) { break; } - context->selStackBack.push_back(tmpStack.front()); + context.info->selStackBack.push_back(tmpStack.front()); tmpStack.pop_front(); } - context->selStackForward = std::move(tmpStack); + context.info->selStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } @@ -1443,24 +1446,24 @@ std::vector SelectionSingleton::selStackGet( int index ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return {}; } const SelStackItem* item = nullptr; if (index >= 0) { - if (index >= (int)context->selStackBack.size()) { + if (index >= (int)context.info->selStackBack.size()) { return {}; } - item = &context->selStackBack[context->selStackBack.size() - 1 - index]; + item = &context.info->selStackBack[context.info->selStackBack.size() - 1 - index]; } else { index = -index - 1; - if (index >= (int)context->selStackForward.size()) { + if (index >= (int)context.info->selStackForward.size()) { return {}; } - item = &context->selStackBack[context->selStackForward.size() - 1 - index]; + item = &context.info->selStackBack[context.info->selStackForward.size() - 1 - index]; } std::list selList; @@ -1488,14 +1491,14 @@ bool SelectionSingleton::addSelections( const std::vector& pSubNames ) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } - if (!context->pickedList.empty()) { - context->pickedList.clear(); - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + if (!context.info->pickedList.empty()) { + context.info->pickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } std::ostringstream ss; @@ -1528,12 +1531,12 @@ bool SelectionSingleton::addSelections( ss << temp.getSubString(); } - context->selList.push_back(temp); - context->selStackForward.clear(); + context.info->selList.push_back(temp); + context.info->selStackForward.clear(); SelectionChanges Chng( SelectionChanges::AddSelection, - temp.DocName, + context.docName.c_str(), temp.FeatName, temp.SubName, temp.TypeName @@ -1569,19 +1572,23 @@ bool SelectionSingleton::updateSelection( if (!pSubName) { pSubName = ""; } + + auto pDoc = getDocument(pDocName); + if (!pDoc) { + return false; + } if (DocName == pDocName && FeatName == pObjectName && SubName == pSubName) { if (show) { FC_TRACE("preselect signal"); - notify(SelectionChanges(SelectionChanges::SetPreselectSignal, DocName, FeatName, SubName)); + notify( + SelectionChanges(SelectionChanges::SetPreselectSignal, pDoc->getName(), FeatName, SubName) + ); } else { rmvPreselect(); } } - auto pDoc = getDocument(pDocName); - if (!pDoc) { - return false; - } + auto pObject = pDoc->getObject(pObjectName); if (!pObject) { return false; @@ -1592,7 +1599,7 @@ bool SelectionSingleton::updateSelection( SelectionChanges Chng( show ? SelectionChanges::ShowSelection : SelectionChanges::HideSelection, - pDocName, + pDoc->getName(), pObjectName, pSubName, pObject->getTypeId().getName() @@ -1647,16 +1654,16 @@ void SelectionSingleton::rmvSelection( const std::vector* pickedList ) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } if (pickedList) { - context->pickedList.clear(); + context.info->pickedList.clear(); for (const auto& sel : *pickedList) { - context->pickedList.emplace_back(); - auto& s = context->pickedList.back(); + context.info->pickedList.emplace_back(); + auto& s = context.info->pickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; @@ -1667,7 +1674,7 @@ void SelectionSingleton::rmvSelection( s.y = sel.y; s.z = sel.z; } - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } if (!pDocName) { @@ -1681,7 +1688,8 @@ void SelectionSingleton::rmvSelection( } std::vector changes; - for (auto It = context->selList.begin(), ItNext = It; It != context->selList.end(); It = ItNext) { + for (auto It = context.info->selList.begin(), ItNext = It; It != context.info->selList.end(); + It = ItNext) { ++ItNext; if (It->DocName != temp.DocName || It->FeatName != temp.FeatName) { continue; @@ -1707,7 +1715,7 @@ void SelectionSingleton::rmvSelection( ); // destroy the SelectionDescription item - context->selList.erase(It); + context.info->selList.erase(It); } // NOTE: It can happen that there are nested calls of rmvSelection() @@ -1753,15 +1761,15 @@ void SelectionSingleton::setVisible(VisibleState vis, const char* pDocName) visible = 0; } - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } // Copy the selection in case it changes during this function std::vector sels; - sels.reserve(context->selList.size()); - for (auto& sel : context->selList) { + sels.reserve(context.info->selList.size()); + for (auto& sel : context.info->selList) { if (sel.DocName.empty() || sel.FeatName.empty() || !sel.pObject) { continue; } @@ -1862,11 +1870,11 @@ void SelectionSingleton::setVisible(VisibleState vis, const char* pDocName) void SelectionSingleton::setSelection(const char* pDocName, const std::vector& sel) { - SelectionContext* context = getSelectionContext(pDocName); + auto context = getSelectionContext(pDocName); - if (!context->pickedList.empty()) { - context->pickedList.clear(); - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + if (!context.info->pickedList.empty()) { + context.info->pickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } bool touched = false; @@ -1881,12 +1889,12 @@ void SelectionSingleton::setSelection(const char* pDocName, const std::vectorselList.push_back(temp); + context.info->selList.push_back(temp); } if (touched) { - context->selStackForward.clear(); - notify(SelectionChanges(SelectionChanges::SetSelection, pDocName)); + context.info->selStackForward.clear(); + notify(SelectionChanges(SelectionChanges::SetSelection, context.docName.c_str())); getMainWindow()->updateActions(); } } @@ -1901,26 +1909,25 @@ void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelec return; } - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - if (!context->pickedList.empty()) { - context->pickedList.clear(); - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + if (!context.info->pickedList.empty()) { + context.info->pickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } - std::string docName = pDocName == nullptr ? "" : pDocName; - if (clearPreSelect && DocName == docName) { + if (clearPreSelect && DocName == context.docName) { rmvPreselect(); } bool touched = false; - for (auto it = context->selList.begin(); it != context->selList.end();) { - if (it->DocName == docName) { + for (auto it = context.info->selList.begin(); it != context.info->selList.end();) { + if (it->DocName == context.docName.c_str()) { touched = true; - it = context->selList.erase(it); + it = context.info->selList.erase(it); } else { ++it; @@ -1933,7 +1940,7 @@ void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelec if (!logDisabled) { std::ostringstream ss; - ss << "Gui.Selection.clearSelection('" << docName << "'"; + ss << "Gui.Selection.clearSelection('" << context.docName << "'"; if (!clearPreSelect) { ss << ", False"; } @@ -1941,28 +1948,28 @@ void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelec Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); } - notify(SelectionChanges(SelectionChanges::ClrSelection, docName.c_str())); + notify(SelectionChanges(SelectionChanges::ClrSelection, context.docName.c_str())); getMainWindow()->updateActions(); } void SelectionSingleton::clearCompleteSelection(const char* pDocName, bool clearPreSelect) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - if (!context->pickedList.empty()) { - context->pickedList.clear(); - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + if (!context.info->pickedList.empty()) { + context.info->pickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged, context.docName.c_str())); } if (clearPreSelect) { rmvPreselect(); } - if (context->selList.empty()) { + if (context.info->selList.empty()) { return; } @@ -1977,20 +1984,20 @@ void SelectionSingleton::clearCompleteSelection(const char* pDocName, bool clear // objects being deselected. std::set viewProviders; - for (SelectionDescription& sel : context->selList) { + for (SelectionDescription& sel : context.info->selList) { if (auto vp = Application::Instance->getViewProvider(sel.pObject)) { viewProviders.insert(vp); } } for (auto& vp : viewProviders) { - SelectionChanges Chng(SelectionChanges::ClrSelection); + SelectionChanges Chng(SelectionChanges::ClrSelection, pDocName); vp->onSelectionChanged(Chng); } - context->selList.clear(); + context.info->selList.clear(); - SelectionChanges Chng(SelectionChanges::ClrSelection); + SelectionChanges Chng(SelectionChanges::ClrSelection, context.docName.c_str()); FC_LOG("Clear selection"); @@ -2005,13 +2012,13 @@ bool SelectionSingleton::isSelected( ResolveMode resolve ) const { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return false; } SelectionDescription sel; - return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &context->selList) > 0; + return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &context.info->selList) > 0; } bool SelectionSingleton::isSelected( @@ -2118,8 +2125,9 @@ int SelectionSingleton::checkSelection( } } if (!selList) { - if (const SelectionContext* context = getSelectionContext(pDocName)) { - selList = &context->selList; + auto context = getSelectionContext(pDocName); + if (context.info) { + selList = &context.info->selList; } else { return -1; @@ -2166,13 +2174,9 @@ const char* SelectionSingleton::getSelectedElement(App::DocumentObject* obj, con if (!obj) { return nullptr; } - auto foundContext = docSelectionContext.find(obj->getDocument()); - if (foundContext == docSelectionContext.end()) { - return nullptr; - } - const SelectionContext& context = foundContext->second; + auto context = getSelectionContext(obj->getDocument()->getName()); - for (auto selected : context.selList) { + for (auto selected : context.info->selList) { if (selected.pObject == obj) { auto len = selected.SubName.length(); if (!len) { @@ -2198,12 +2202,12 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) // For safety reason, don't bother checking rmvPreselect(); - SelectionContext& context = docSelectionContext[Obj.getDocument()]; + SelectionInfo& info = docSelectionContext[Obj.getDocument()]; // Remove also from the selection, if selected // We don't walk down the hierarchy for each selection, so there may be stray selection std::vector changes; - for (auto it = context.selList.begin(), itNext = it; it != context.selList.end(); it = itNext) { + for (auto it = info.selList.begin(), itNext = it; it != info.selList.end(); it = itNext) { ++itNext; if (it->pResolvedObject == &Obj || it->pObject == &Obj) { changes.emplace_back( @@ -2213,7 +2217,7 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) it->SubName, it->TypeName ); - context.selList.erase(it); + info.selList.erase(it); } } if (!changes.empty()) { @@ -2226,20 +2230,20 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) getMainWindow()->updateActions(); } - if (!context.pickedList.empty()) { + if (!info.pickedList.empty()) { bool changed = false; - for (auto it = context.pickedList.begin(), itNext = it; it != context.pickedList.end(); + for (auto it = info.pickedList.begin(), itNext = it; it != info.pickedList.end(); it = itNext) { ++itNext; auto& sel = *it; if (sel.DocName == Obj.getDocument()->getName() && sel.FeatName == Obj.getNameInDocument()) { changed = true; - context.pickedList.erase(it); + info.pickedList.erase(it); } } if (changed) { - notify(SelectionChanges(SelectionChanges::PickedListChanged)); + notify(SelectionChanges(SelectionChanges::PickedListChanged, Obj.getDocument()->getName())); } } } @@ -2251,22 +2255,22 @@ void SelectionSingleton::slotClosedDocument(const App::Document& doc) void SelectionSingleton::setSelectionStyle(SelectionStyle selStyle, const char* pDocName) { - SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return; } - context->selectionStyle = selStyle; + context.info->selectionStyle = selStyle; } SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle(const char* pDocName) { - const SelectionContext* context = getSelectionContext(pDocName); - if (!context) { + auto context = getSelectionContext(pDocName); + if (!context.info) { return SelectionSingleton::SelectionStyle::NormalSelection; } - return context->selectionStyle; + return context.info->selectionStyle; } -SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionContext(const char* pDocName) +SelectionSingleton::SelectionContext SelectionSingleton::getSelectionContext(const char* pDocName) { // Some functions might receive "*" for document selection. // This is because there used to be a single selection context @@ -2280,11 +2284,11 @@ SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionContext(co } if (App::Document* doc = getDocument(pDocName)) { - return &docSelectionContext[doc]; + return SelectionContext {.info = &docSelectionContext[doc], .docName = doc->getName()}; } - return nullptr; + return SelectionContext {.info = nullptr, .docName = ""}; } -const SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionContext( +SelectionSingleton::SelectionConstContext SelectionSingleton::getSelectionContext( const char* pDocName ) const { @@ -2302,10 +2306,10 @@ const SelectionSingleton::SelectionContext* SelectionSingleton::getSelectionCont if (App::Document* doc = getDocument(pDocName)) { auto foundContext = docSelectionContext.find(doc); if (foundContext != docSelectionContext.end()) { - return &foundContext->second; + return SelectionConstContext {.info = &foundContext->second, .docName = doc->getName()}; } } - return nullptr; + return SelectionConstContext {.info = nullptr, .docName = ""}; } //************************************************************************** diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index b396be9d27..dde0b26f96 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -264,6 +264,7 @@ private: std::string filterDocName; std::string filterObjName; ResolveMode resolve; + const char* pDocumentScopeName {nullptr}; bool blockedSelection; }; @@ -825,7 +826,7 @@ protected: using SelStackItem = std::set; // Each document has a description context - struct SelectionContext + struct SelectionInfo { Gui::SelectionGate* gate {nullptr}; ResolveMode resolveMode {ResolveMode::OldStyleElement}; @@ -839,15 +840,25 @@ protected: SelectionStyle selectionStyle {SelectionStyle::NormalSelection}; }; + struct SelectionContext + { + SelectionInfo* info; + std::string docName; + }; + struct SelectionConstContext + { + const SelectionInfo* info; + std::string docName; + }; // Returns a selection context or nullptr if the document is not found - SelectionContext* getSelectionContext(const char* pDocName); - const SelectionContext* getSelectionContext(const char* pDocName) const; + SelectionContext getSelectionContext(const char* pDocName); + SelectionConstContext getSelectionContext(const char* pDocName) const; static SelectionSingleton* _pcSingleton; - std::map docSelectionContext; + std::map docSelectionContext; // Preselection helpers, it's a mess, needs clarifying -theo-vt std::string DocName; From 99e414efab095edc25d397737d6acabcb4661801 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Thu, 5 Mar 2026 16:09:42 -0500 Subject: [PATCH 08/12] Return string instead of const char* in Selection::getSelectedElement --- src/Gui/Selection/Selection.cpp | 6 +++--- src/Gui/Selection/Selection.h | 2 +- src/Gui/Selection/SoFCUnifiedSelection.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Gui/Selection/Selection.cpp b/src/Gui/Selection/Selection.cpp index e242f10d59..1293699820 100644 --- a/src/Gui/Selection/Selection.cpp +++ b/src/Gui/Selection/Selection.cpp @@ -2169,7 +2169,7 @@ int SelectionSingleton::checkSelection( return 0; } -const char* SelectionSingleton::getSelectedElement(App::DocumentObject* obj, const char* pSubName) const +std::string SelectionSingleton::getSelectedElement(App::DocumentObject* obj, const char* pSubName) const { if (!obj) { return nullptr; @@ -2185,12 +2185,12 @@ const char* SelectionSingleton::getSelectedElement(App::DocumentObject* obj, con if (pSubName && strncmp(pSubName, selected.SubName.c_str(), selected.SubName.length()) == 0) { if (pSubName[len] == 0 || pSubName[len - 1] == '.') { - return selected.SubName.c_str(); + return selected.SubName; } } } } - return nullptr; + return ""; } void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index dde0b26f96..de6e981cb2 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -398,7 +398,7 @@ public: ResolveMode resolve = ResolveMode::OldStyleElement ) const; - const char* getSelectedElement(App::DocumentObject*, const char* pSubName) const; + std::string getSelectedElement(App::DocumentObject*, const char* pSubName) const; /// set the preselected object (mostly by the 3D view) int setPreselect( diff --git a/src/Gui/Selection/SoFCUnifiedSelection.cpp b/src/Gui/Selection/SoFCUnifiedSelection.cpp index b225d8025d..689cb77733 100644 --- a/src/Gui/Selection/SoFCUnifiedSelection.cpp +++ b/src/Gui/Selection/SoFCUnifiedSelection.cpp @@ -791,7 +791,7 @@ bool SoFCUnifiedSelection::setSelection(const std::vector& infos, bo // Ex: Body.Pad.Face9 to Body.Pad.;g3;SKT;:H12dc,E;FAC;:H12dc:4,F;:G0;XTR;:H12dc:8,F.Face9 getFullSubElementName(subName); const char* subSelected - = Gui::Selection().getSelectedElement(vpd->getObject(), subName.c_str()); + = Gui::Selection().getSelectedElement(vpd->getObject(), subName.c_str()).c_str(); FC_TRACE( "select " << (subSelected ? subSelected : "'null'") << ", " << objectName << ", " << subName From 1665dec4e451b42bde5cfda935a64d96a536afaa Mon Sep 17 00:00:00 2001 From: theo-vt Date: Thu, 5 Mar 2026 18:00:49 -0500 Subject: [PATCH 09/12] Adress review --- src/App/TransactionDefs.h | 24 ++++++++++++++++++++++++ src/Gui/Application.cpp | 11 +++++++---- src/Gui/Command.cpp | 3 ++- src/Gui/Document.cpp | 14 ++------------ src/Gui/Selection/Selection.cpp | 31 +++---------------------------- src/Gui/Selection/Selection.h | 14 -------------- 6 files changed, 38 insertions(+), 59 deletions(-) diff --git a/src/App/TransactionDefs.h b/src/App/TransactionDefs.h index 8883c0b033..8143a5236c 100644 --- a/src/App/TransactionDefs.h +++ b/src/App/TransactionDefs.h @@ -1,3 +1,27 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2026 Théo Veilleux-Trinh * + * * + * 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 APP_TRANSACTIONDEFS_H_ #define APP_TRANSACTIONDEFS_H_ diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 0fe22dd343..9a2f154941 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1524,11 +1524,14 @@ void Application::unsetEditDocument(Gui::Document* pcDocument) } void Application::unsetEditDocumentIf(const std::function& eval) { - for (auto editDoc : d->editDocuments) { - if (eval(editDoc)) { - unsetEditDocument(editDoc); + std::erase_if(d->editDocuments, [&](Gui::Document* doc) { + if (eval(doc)) { + doc->_resetEdit(); + return true; } - } + return false; + }); + updateActions(); } Gui::MDIView* Application::editViewOfNode(SoNode* node) const { diff --git a/src/Gui/Command.cpp b/src/Gui/Command.cpp index 4e76a867c3..0ddb4b0637 100644 --- a/src/Gui/Command.cpp +++ b/src/Gui/Command.cpp @@ -735,7 +735,8 @@ void Command::resetTransactionID() } bool Command::hasPendingCommand() { - return App::GetApplication().getActiveDocument()->getBookedTransactionID() != 0; + App::Document* doc = App::GetApplication().getActiveDocument(); + return doc && doc->getBookedTransactionID() != App::NullTransaction; } bool Command::_blockCmd = false; diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index ae373f9003..143bf0ce9d 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -286,7 +286,8 @@ struct DocumentP { _editingObject = sobj; _editMode = ModNum; - _editViewProvider = svp; // Helps the viewprovider find the correct in editDocument + _editViewProvider = svp; // Used to resolve start editing (find the document in edit from + // within the viewprovider) _editViewProvider = svp->startEditing(ModNum); if (!_editViewProvider) { _editViewProviderParent = nullptr; @@ -1080,17 +1081,6 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj) }); } - if (d->_editViewProvider == viewProvider || d->_editViewProviderParent == viewProvider) { - _resetEdit(); - } - else if (Application::Instance->editDocument()) { - auto editDoc = Application::Instance->editDocument(); - if (editDoc->d->_editViewProvider == viewProvider - || editDoc->d->_editViewProviderParent == viewProvider) { - Application::Instance->setEditDocument(nullptr); - } - } - handleChildren3D(viewProvider, true); if (viewProvider && viewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { diff --git a/src/Gui/Selection/Selection.cpp b/src/Gui/Selection/Selection.cpp index 1293699820..061de9a4c9 100644 --- a/src/Gui/Selection/Selection.cpp +++ b/src/Gui/Selection/Selection.cpp @@ -62,34 +62,6 @@ using namespace Gui; using namespace std; namespace sp = std::placeholders; -// TODO-theo-vt remove! -SelectionGateFilterExternal::SelectionGateFilterExternal(const char* docName, const char* objName) -{ - if (docName) { - DocName = docName; - if (objName) { - ObjName = objName; - } - } -} - -bool SelectionGateFilterExternal::allow(App::Document* doc, App::DocumentObject* obj, const char*) -{ - if (!doc || !obj) { - return true; - } - if (!DocName.empty() && doc->getName() != DocName) { - notAllowedReason = "Cannot select external object"; - } - else if (!ObjName.empty() && ObjName == obj->getNameInDocument()) { - notAllowedReason = "Cannot select self"; - } - else { - return true; - } - return false; -} - ////////////////////////////////////////////////////////////////////////////////////////// SelectionObserver::SelectionObserver(bool attach, ResolveMode resolve) @@ -1871,6 +1843,9 @@ void SelectionSingleton::setVisible(VisibleState vis, const char* pDocName) void SelectionSingleton::setSelection(const char* pDocName, const std::vector& sel) { auto context = getSelectionContext(pDocName); + if (!context.info) { + return; + } if (!context.info->pickedList.empty()) { context.info->pickedList.clear(); diff --git a/src/Gui/Selection/Selection.h b/src/Gui/Selection/Selection.h index de6e981cb2..295f55053b 100644 --- a/src/Gui/Selection/Selection.h +++ b/src/Gui/Selection/Selection.h @@ -287,20 +287,6 @@ public: std::string notAllowedReason; }; -/** SelectionGateFilterExternal - * The selection gate disallows any external object - */ -class GuiExport SelectionGateFilterExternal: public SelectionGate -{ -public: - explicit SelectionGateFilterExternal(const char* docName, const char* objName = nullptr); - bool allow(App::Document*, App::DocumentObject*, const char*) override; - -private: - std::string DocName; - std::string ObjName; -}; - /** The Selection class * The selection singleton keeps track of the selection state of * the whole application. It gets messages from all entities which can From db1469f2194a9d466e963d511bd4be80ef4acf27 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sun, 8 Mar 2026 11:16:01 -0400 Subject: [PATCH 10/12] Add dialog for link insertion when selection buffer is empty --- src/Gui/CMakeLists.txt | 3 + src/Gui/CommandLink.cpp | 94 ++++++++++++++----------- src/Gui/TaskCommandLink.cpp | 135 ++++++++++++++++++++++++++++++++++++ src/Gui/TaskCommandLink.h | 80 +++++++++++++++++++++ src/Gui/TaskCommandLink.ui | 36 ++++++++++ 5 files changed, 307 insertions(+), 41 deletions(-) create mode 100644 src/Gui/TaskCommandLink.cpp create mode 100644 src/Gui/TaskCommandLink.h create mode 100644 src/Gui/TaskCommandLink.ui diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 9eceb4bfe8..953b68bd82 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -490,6 +490,7 @@ SET(Gui_UIC_SRCS InputVector.ui Placement.ui TaskTransform.ui + TaskCommandLink.ui TextureMapping.ui TaskView/TaskAppearance.ui TaskView/TaskOrientation.ui @@ -578,6 +579,7 @@ SET(Dialog_CPP_SRCS TaskDlgRelocation.cpp Dialogs/DlgCheckableMessageBox.cpp TaskTransform.cpp + TaskCommandLink.cpp Dialogs/DlgUndoRedo.cpp InputVector.cpp Placement.cpp @@ -620,6 +622,7 @@ SET(Dialog_HPP_SRCS Dialogs/DlgVersionMigrator.h TaskDlgRelocation.h TaskTransform.h + TaskCommandLink.h Dialogs/DlgUndoRedo.h InputVector.h Placement.h diff --git a/src/Gui/CommandLink.cpp b/src/Gui/CommandLink.cpp index 71ac668969..8d75ea0501 100644 --- a/src/Gui/CommandLink.cpp +++ b/src/Gui/CommandLink.cpp @@ -34,9 +34,11 @@ #include "Action.h" #include "Application.h" #include "Command.h" +#include "Control.h" #include "Document.h" #include "MainWindow.h" #include "Selection.h" +#include "TaskCommandLink.h" #include "Tree.h" #include "ViewProviderDocumentObject.h" #include "WaitCursor.h" @@ -280,6 +282,51 @@ void StdCmdLinkMake::activated(int) return; } + auto exec = [=](std::vector objs) { + doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Make link")); + try { + if (objs.empty()) { + std::string name = doc->getUniqueObjectName("Link"); + Command::doCommand( + Command::Doc, + "App.getDocument('%s').addObject('App::Link','%s')", + doc->getName(), + name.c_str() + ); + Selection().addSelection(doc->getName(), name.c_str()); + } + else { + for (auto obj : objs) { + std::string name = doc->getUniqueObjectName("Link"); + Command::doCommand( + Command::Doc, + "App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument(" + "'%s'" + ").%s)", + doc->getName(), + name.c_str(), + obj->getDocument()->getName(), + obj->getNameInDocument() + ); + setLinkLabel(obj, doc->getName(), name.c_str()); + Selection().addSelection(doc->getName(), name.c_str()); + } + } + Selection().selStackPush(); + doc->commitTransaction(); + } + catch (const Base::Exception& e) { + doc->abortTransaction(); + QMessageBox::critical( + getMainWindow(), + QObject::tr("Create link failed"), + QString::fromLatin1(e.what()) + ); + e.reportException(); + } + }; + + std::set objs; for (auto& sel : Selection().getCompleteSelection()) { if (sel.pObject && sel.pObject->isAttachedToDocument()) { @@ -287,48 +334,13 @@ void StdCmdLinkMake::activated(int) } } - Selection().selStackPush(); - Selection().clearCompleteSelection(); - - Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link")); - try { - if (objs.empty()) { - std::string name = doc->getUniqueObjectName("Link"); - Command::doCommand( - Command::Doc, - "App.getDocument('%s').addObject('App::Link','%s')", - doc->getName(), - name.c_str() - ); - Selection().addSelection(doc->getName(), name.c_str()); - } - else { - for (auto obj : objs) { - std::string name = doc->getUniqueObjectName("Link"); - Command::doCommand( - Command::Doc, - "App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s'" - ").%s)", - doc->getName(), - name.c_str(), - obj->getDocument()->getName(), - obj->getNameInDocument() - ); - setLinkLabel(obj, doc->getName(), name.c_str()); - Selection().addSelection(doc->getName(), name.c_str()); - } - } - Selection().selStackPush(); - Command::commitCommand(); + if (objs.empty()) { + Gui::Control().showDialog(new TaskCommandLinkDialog(exec)); } - catch (const Base::Exception& e) { - Command::abortCommand(); - QMessageBox::critical( - getMainWindow(), - QObject::tr("Create link failed"), - QString::fromLatin1(e.what()) - ); - e.reportException(); + else { + Selection().selStackPush(); + Selection().clearCompleteSelection(); + exec(std::vector(objs.begin(), objs.end())); } } diff --git a/src/Gui/TaskCommandLink.cpp b/src/Gui/TaskCommandLink.cpp new file mode 100644 index 0000000000..665858aacd --- /dev/null +++ b/src/Gui/TaskCommandLink.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** + * Copyright (c) 2026 Théo Veilleux-Trinh * + * * + * 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 "TaskCommandLink.h" + +#include "ui_TaskCommandLink.h" + +#include "Application.h" +#include "Document.h" +#include "ViewProvider.h" + +#include +#include +#include + +namespace Gui +{ +TaskCommandLink::TaskCommandLink() + : ui(new Ui_TaskCommandLinkDialog()) +{ + proxy = new QWidget(this); + ui->setupUi(proxy); + ui->objectsList->header()->hide(); + ui->objectsList->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); + + this->groupLayout()->addWidget(proxy); + + buildObjectsList(); +} +TaskCommandLink::~TaskCommandLink() +{ + delete proxy; + delete ui; +} +std::vector TaskCommandLink::selectedObjects() +{ + auto selected = ui->objectsList->selectedItems(); + std::vector dst; + dst.reserve(selected.size()); + + for (auto sel : selected) { + dst.push_back(sel->data(0, Qt::UserRole).value()); + } + return dst; +} +void processObjectsHelper(std::vector objs, QTreeWidgetItem* item) +{ + for (auto obj : objs) { + auto objItem = new QTreeWidgetItem(item); + + objItem->setText(0, obj->Label.getValue()); + objItem->setData(0, Qt::UserRole, QVariant::fromValue(obj)); + + Gui::ViewProvider* vp = nullptr; + if (auto doc = Application::Instance->getDocument(obj->getDocument())) { + vp = doc->getViewProvider(obj); + } + if (vp) { + objItem->setIcon(0, vp->getIcon()); + processObjectsHelper(vp->claimChildren(), objItem); + } + else { + objItem->setIcon(0, QIcon()); + } + } +} +void TaskCommandLink::buildObjectsList() +{ + ui->objectsList->clear(); + + auto allDocuments = App::GetApplication().getDocuments(); + bool collapse = true; + std::map docItemMap; + + for (auto doc : allDocuments) { + auto docItem = new QTreeWidgetItem(); + std::string itemName = doc->Label.getValue(); + + docItem->setText(0, QString::fromStdString(itemName)); + docItem->setIcon(0, QIcon(QStringLiteral(":/icons/Document.svg"))); + docItem->setFlags(docItem->flags() & ~Qt::ItemIsSelectable); // Can't link a whole document + + docItemMap[docItem] = doc; + + ui->objectsList->addTopLevelItem(docItem); + + processObjectsHelper(Application::Instance->getDocument(doc)->getTreeRootObjects(), docItem); + + if (collapse) { + ui->objectsList->collapseAll(); + } + else { + ui->objectsList->expandToDepth(0); + } + } + ui->objectsList->selectedItems(); +} + +// dialog + +TaskCommandLinkDialog::TaskCommandLinkDialog( + std::function)> executor_ +) + : executor(executor_) +{ + commandLink = new TaskCommandLink(); + Content.push_back(commandLink); +} +void TaskCommandLinkDialog::open() +{} +bool TaskCommandLinkDialog::accept() +{ + executor(commandLink->selectedObjects()); + return true; +} +} // namespace Gui diff --git a/src/Gui/TaskCommandLink.h b/src/Gui/TaskCommandLink.h new file mode 100644 index 0000000000..7b1d0bba5a --- /dev/null +++ b/src/Gui/TaskCommandLink.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (c) 2026 Théo Veilleux-Trinh * + * * + * 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 * + * * + ***************************************************************************/ + +#pragma once + +#include "TaskView/TaskDialog.h" +#include "TaskView/TaskView.h" + +#include + +#include +#include + +namespace App +{ +class DocumentObject; +} + +namespace Gui +{ +class Document; +class Ui_TaskCommandLinkDialog; + +class TaskCommandLink: public Gui::TaskView::TaskBox +{ +public: + TaskCommandLink(); + ~TaskCommandLink(); + + std::vector selectedObjects(); + +private: + void buildObjectsList(); + +private: + Ui_TaskCommandLinkDialog* ui {nullptr}; + QWidget* proxy {nullptr}; +}; + +class TaskCommandLinkDialog: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskCommandLinkDialog(std::function)> executor_); + ~TaskCommandLinkDialog() override = default; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + } + + void open() override; + bool accept() override; + +private: + TaskCommandLink* commandLink {nullptr}; + Gui::Document* document {nullptr}; + std::function)> executor; +}; +} // namespace Gui diff --git a/src/Gui/TaskCommandLink.ui b/src/Gui/TaskCommandLink.ui new file mode 100644 index 0000000000..21026ad7cc --- /dev/null +++ b/src/Gui/TaskCommandLink.ui @@ -0,0 +1,36 @@ + + + Gui::TaskCommandLinkDialog + + + + 0 + 0 + 340 + 212 + + + + Insert + + + + + + + 0 + 0 + + + + + 1 + + + + + + + + + From 51ac15b375aaf909746d9526620a0b8919dafd1f Mon Sep 17 00:00:00 2001 From: theo-vt Date: Tue, 10 Mar 2026 22:13:01 -0400 Subject: [PATCH 11/12] Allow one clipping plane dock widget per document --- src/Gui/Clipping.cpp | 31 ++++++++++-- src/Gui/Clipping.h | 10 +++- src/Gui/CommandView.cpp | 67 +++++++++++++++++++++++-- src/Mod/PartDesign/Gui/ViewProvider.cpp | 5 +- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/Gui/Clipping.cpp b/src/Gui/Clipping.cpp index ccba033736..857f8069f2 100644 --- a/src/Gui/Clipping.cpp +++ b/src/Gui/Clipping.cpp @@ -21,6 +21,7 @@ ***************************************************************************/ #include +#include #include #include @@ -29,6 +30,8 @@ #include #include +#include + #include "Clipping.h" #include "ui_Clipping.h" #include "DockWindowManager.h" @@ -51,6 +54,10 @@ public: bool flipY {false}; bool flipZ {false}; SoTimerSensor* sensor; + App::Document* shownOn {nullptr}; + QDockWidget* dockWidget {nullptr}; + fastsignals::scoped_connection activeDocConnection; + Private() { clipX = new SoClipPlane(); @@ -99,7 +106,7 @@ public: /* TRANSLATOR Gui::Dialog::Clipping */ -Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent) +Clipping::Clipping(Gui::View3DInventor* view, App::Document* showOn, QWidget* parent) : QDialog(parent) , d(new Private) { @@ -124,6 +131,7 @@ Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent) d->ui.dirZ->setRange(-max, max); d->ui.dirZ->setSingleStep(0.1f); d->ui.dirZ->setValue(1.0f); + d->shownOn = showOn; d->view = view; View3DInventorViewer* viewer = view->getViewer(); @@ -190,14 +198,15 @@ Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent) } } -Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view) +Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view, App::Document* showOn) { // embed this dialog into a QDockWidget - auto clipping = new Clipping(view); + auto clipping = new Clipping(view, showOn); Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); QDockWidget* dw = pDockMgr->addDockWindow("Clipping", clipping, Qt::LeftDockWidgetArea); dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); dw->show(); + clipping->d->dockWidget = dw; return clipping; } @@ -205,6 +214,7 @@ Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view) /** Destroys the object and frees any allocated resources */ Clipping::~Clipping() { + d->activeDocConnection.disconnect(); d->node->removeChild(d->clipX); d->node->removeChild(d->clipY); d->node->removeChild(d->clipZ); @@ -216,6 +226,8 @@ Clipping::~Clipping() void Clipping::setupConnections() { // clang-format off + d->activeDocConnection = App::GetApplication().signalActiveDocument.connect( + std::bind(&Clipping::onActiveDocument, this, std::placeholders::_1)); connect(d->ui.groupBoxX, &QGroupBox::toggled, this, &Clipping::onGroupBoxXToggled); connect(d->ui.groupBoxY, &QGroupBox::toggled, @@ -259,7 +271,18 @@ void Clipping::reject() dw->deleteLater(); } } - +void Clipping::onActiveDocument(const App::Document& doc) +{ + if (!d || !d->dockWidget) { + return; + } + if (&doc == d->shownOn) { + d->dockWidget->show(); + } + else { + d->dockWidget->hide(); + } +} void Clipping::onGroupBoxXToggled(bool on) { if (on) { diff --git a/src/Gui/Clipping.h b/src/Gui/Clipping.h index 323d258d3f..1287c7ac2d 100644 --- a/src/Gui/Clipping.h +++ b/src/Gui/Clipping.h @@ -26,6 +26,11 @@ #include #include +namespace App +{ +class Document; +} + namespace Gui { class View3DInventor; @@ -40,12 +45,13 @@ class GuiExport Clipping: public QDialog Q_OBJECT public: - static Clipping* makeDockWidget(Gui::View3DInventor*); - Clipping(Gui::View3DInventor* view, QWidget* parent = nullptr); + static Clipping* makeDockWidget(Gui::View3DInventor*, App::Document* showOn); + Clipping(Gui::View3DInventor* view, App::Document* showOn, QWidget* parent = nullptr); ~Clipping() override; protected: void setupConnections(); + void onActiveDocument(const App::Document& doc); void onGroupBoxXToggled(bool); void onGroupBoxYToggled(bool); void onGroupBoxZToggled(bool); diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index c3b1e1948e..d970ab2b8a 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -21,6 +21,9 @@ ***************************************************************************/ #include +#include +#include + #include #include #include @@ -640,7 +643,34 @@ void StdCmdFreezeViews::languageChange() // Std_ToggleClipPlane //=========================================================================== -DEF_STD_CMD_AC(StdCmdToggleClipPlane) +class StdCmdToggleClipPlane: public Gui::Command +{ +public: + StdCmdToggleClipPlane(); + virtual ~StdCmdToggleClipPlane() + {} + virtual const char* className() const + { + return "StdCmdToggleClipPlane"; + } + +protected: + virtual void activated(int iMsg); + virtual bool isActive(void); + virtual Gui::Action* createAction(void); + +private: + StdCmdToggleClipPlane(const StdCmdToggleClipPlane&) = delete; + StdCmdToggleClipPlane(StdCmdToggleClipPlane&&) = delete; + StdCmdToggleClipPlane& operator=(const StdCmdToggleClipPlane&) = delete; + StdCmdToggleClipPlane& operator=(StdCmdToggleClipPlane&&) = delete; + + void garbageCollect(); + bool hasClipping(App::Document* doc) const; + +private: + std::vector>> clippings; +}; StdCmdToggleClipPlane::StdCmdToggleClipPlane() : Command("Std_ToggleClipPlane") @@ -663,11 +693,15 @@ Action* StdCmdToggleClipPlane::createAction() void StdCmdToggleClipPlane::activated(int iMsg) { Q_UNUSED(iMsg); - static QPointer clipping = nullptr; - if (!clipping) { + App::Document* doc = getActiveDocument(); + if (!doc) { + return; + } + garbageCollect(); // remove dead pointers + if (!hasClipping(doc)) { auto view = qobject_cast(getMainWindow()->activeWindow()); if (view) { - clipping = Gui::Dialog::Clipping::makeDockWidget(view); + clippings.push_back(std::make_pair(doc, Gui::Dialog::Clipping::makeDockWidget(view, doc))); } } } @@ -678,6 +712,31 @@ bool StdCmdToggleClipPlane::isActive() return view ? true : false; } +void StdCmdToggleClipPlane::garbageCollect() +{ + // We assume the vector to be small + std::vector>> newClippings; + newClippings.reserve(clippings.size()); + std::copy_if( + clippings.begin(), + clippings.end(), + std::back_inserter(newClippings), + [](const std::pair>& clipPair) -> bool { + return clipPair.second != nullptr; + } + ); + clippings = newClippings; +} +bool StdCmdToggleClipPlane::hasClipping(App::Document* doc) const +{ + return std::ranges::find( + clippings, + doc, + &std::pair>::first + ) + != clippings.end(); +} + //=========================================================================== // StdCmdDrawStyle //=========================================================================== diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index ec6648a5ec..d0dcc58344 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -172,10 +172,7 @@ bool ViewProvider::setEdit(int ModNum) } } - auto editDoc = Gui::Application::Instance->editDocument([this](Gui::Document* editdoc) { - return editdoc->getEditViewProvider() == this; - }); - Gui::Control().showDialog(featureDlg, editDoc->getDocument()); + Gui::Control().showDialog(featureDlg); return true; } else { From ad630cbb9c8d6ec66b7580f4425bc0e5f9f30016 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sat, 14 Mar 2026 12:35:31 -0400 Subject: [PATCH 12/12] Use new transaction methods in dlgmaterial --- src/Mod/Material/Gui/DlgMaterialImp.cpp | 6 +++--- src/Mod/Material/Gui/DlgMaterialImp.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Mod/Material/Gui/DlgMaterialImp.cpp b/src/Mod/Material/Gui/DlgMaterialImp.cpp index b753e5ae8d..01604cc2d6 100644 --- a/src/Mod/Material/Gui/DlgMaterialImp.cpp +++ b/src/Mod/Material/Gui/DlgMaterialImp.cpp @@ -260,7 +260,7 @@ TaskMaterial::TaskMaterial() taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Set Material")); + tid = Gui::Command::openActiveDocumentCommand(QT_TRANSLATE_NOOP("Command", "Set Material")); } TaskMaterial::~TaskMaterial() = default; @@ -272,13 +272,13 @@ QDialogButtonBox::StandardButtons TaskMaterial::getStandardButtons() const bool TaskMaterial::accept() { - Gui::Command::commitCommand(); + Gui::Command::commitCommand(tid); return true; } bool TaskMaterial::reject() { - Gui::Command::abortCommand(); + Gui::Command::abortCommand(tid); widget->reject(); return (widget->result() == QDialog::Rejected); } diff --git a/src/Mod/Material/Gui/DlgMaterialImp.h b/src/Mod/Material/Gui/DlgMaterialImp.h index 0c0d4d74f0..a3f8bca026 100644 --- a/src/Mod/Material/Gui/DlgMaterialImp.h +++ b/src/Mod/Material/Gui/DlgMaterialImp.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -114,6 +115,7 @@ public: private: DlgMaterialImp* widget; Gui::TaskView::TaskBox* taskbox; + int tid {App::NullTransaction}; }; } // namespace MatGui \ No newline at end of file