From 9700ee4c209cbe95d880913867a093a0a3969cf0 Mon Sep 17 00:00:00 2001 From: nds Date: Wed, 13 Jan 2016 17:52:28 +0300 Subject: [PATCH] 3.9 Clean the history --- src/XGUI/XGUI_ContextMenuMgr.cpp | 10 ++++ src/XGUI/XGUI_OperationMgr.cpp | 2 +- src/XGUI/XGUI_Tools.cpp | 49 ++++++++++++----- src/XGUI/XGUI_Tools.h | 17 +++++- src/XGUI/XGUI_Workshop.cpp | 90 +++++++++++++++++++++++++++----- 5 files changed, 142 insertions(+), 26 deletions(-) diff --git a/src/XGUI/XGUI_ContextMenuMgr.cpp b/src/XGUI/XGUI_ContextMenuMgr.cpp index ea453bb49..648abd6cc 100644 --- a/src/XGUI/XGUI_ContextMenuMgr.cpp +++ b/src/XGUI/XGUI_ContextMenuMgr.cpp @@ -71,6 +71,9 @@ void XGUI_ContextMenuMgr::createActions() aAction = new QAction(QIcon(":pictures/move.png"), XGUI_Workshop::MOVE_TO_END_COMMAND, this); addAction("MOVE_CMD", aAction); + aAction = new QAction(QIcon(":pictures/delete.png"), tr("Clean history"), this); + addAction("CLEAN_HISTORY_CMD", aAction); + aAction = new QAction(QIcon(":pictures/color.png"), tr("Color..."), this); addAction("COLOR_CMD", aAction); @@ -214,6 +217,9 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() else if (hasFeature && myWorkshop->canMoveFeature()) action("MOVE_CMD")->setEnabled(true); + else if (hasFeature) + action("CLEAN_HISTORY_CMD")->setEnabled(true); + if( aMgr->activeDocument() == aObject->document() ) { action("RENAME_CMD")->setEnabled(true); @@ -240,6 +246,8 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() if (hasFeature || hasParameter) action("DELETE_CMD")->setEnabled(true); } + if (allActive && hasFeature) + action("CLEAN_HISTORY_CMD")->setEnabled(true); } // Show/Hide command has to be disabled for objects from non active document @@ -369,6 +377,7 @@ void XGUI_ContextMenuMgr::buildObjBrowserMenu() aList.clear(); aList.append(action("DELETE_CMD")); aList.append(action("MOVE_CMD")); + aList.append(action("CLEAN_HISTORY_CMD")); aList.append(mySeparator); aList.append(action("RENAME_CMD")); myObjBrowserMenus[ModelAPI_Feature::group()] = aList; @@ -429,6 +438,7 @@ void XGUI_ContextMenuMgr::addObjBrowserMenu(QMenu* theMenu) const aActions.append(mySeparator); aActions.append(action("DELETE_CMD")); //aActions.append(action("MOVE_CMD")); + aActions.append(action("CLEAN_HISTORY_CMD")); aActions.append(action("COLOR_CMD")); } theMenu->addActions(aActions); diff --git a/src/XGUI/XGUI_OperationMgr.cpp b/src/XGUI/XGUI_OperationMgr.cpp index dff53bd91..4b6b22579 100644 --- a/src/XGUI/XGUI_OperationMgr.cpp +++ b/src/XGUI/XGUI_OperationMgr.cpp @@ -38,7 +38,7 @@ XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent, /// we need to install filter to the application in order to react to 'Delete' key button /// this key can not be a short cut for a corresponded action because we need to set /// the actions priority - qApp->installEventFilter(this); + //qApp->installEventFilter(this); } XGUI_OperationMgr::~XGUI_OperationMgr() diff --git a/src/XGUI/XGUI_Tools.cpp b/src/XGUI/XGUI_Tools.cpp index ab9ef819f..0b77c6c75 100644 --- a/src/XGUI/XGUI_Tools.cpp +++ b/src/XGUI/XGUI_Tools.cpp @@ -56,6 +56,18 @@ QString addSlash(const QString& path) return res; } +//****************************************************************** +QString unionOfObjectNames(const QObjectPtrList& theObjects, const QString& theSeparator) +{ + QStringList aObjectNames; + foreach (ObjectPtr aObj, theObjects) { + if (!aObj->data()->isValid()) + continue; + aObjectNames << QString::fromStdString(aObj->data()->name()); + } + return aObjectNames.join(", "); +} + //****************************************************************** bool isModelObject(FeaturePtr theFeature) { @@ -190,9 +202,31 @@ bool isSubOfComposite(const ObjectPtr& theObject) //************************************************************** void refsToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectPtr& theObject, + const QObjectPtrList& theIgnoreList, std::set& theDirectRefFeatures, std::set& theIndirectRefFeatures, std::set& theAlreadyProcessed) +{ + refsDirectToFeatureInAllDocuments(theSourceObject, theObject, theIgnoreList, theDirectRefFeatures, + theAlreadyProcessed); + + // Run recursion. It is possible recursive dependency, like the following: plane, extrusion uses plane, + // axis is built on extrusion. Delete of a plane should check the dependency from the axis also. + std::set::const_iterator aFeatureIt = theDirectRefFeatures.begin(); + for (; aFeatureIt != theDirectRefFeatures.end(); ++aFeatureIt) { + std::set aRecursiveRefFeatures; + refsToFeatureInAllDocuments(theSourceObject, *aFeatureIt, theIgnoreList, + aRecursiveRefFeatures, aRecursiveRefFeatures, theAlreadyProcessed); + theIndirectRefFeatures.insert(aRecursiveRefFeatures.begin(), aRecursiveRefFeatures.end()); + } + +} + +//************************************************************** +void refsDirectToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectPtr& theObject, + const QObjectPtrList& theIgnoreList, + std::set& theDirectRefFeatures, + std::set& theAlreadyProcessed) { FeaturePtr aFeature = ModelAPI_Feature::feature(theObject); if (!aFeature.get()) @@ -209,7 +243,7 @@ void refsToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectP aLast = aRefFeatures.end(); for (; anIt != aLast; anIt++) { // composite feature should not be deleted when the sub feature is to be deleted - if (!isSubOfComposite(theSourceObject, *anIt)) + if (!isSubOfComposite(theSourceObject, *anIt) && !theIgnoreList.contains(*anIt)) theDirectRefFeatures.insert(*anIt); } @@ -264,21 +298,12 @@ void refsToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectP } } } - if (aHasReferenceToObject && !isSubOfComposite(theSourceObject, aFeature)) + if (aHasReferenceToObject && !isSubOfComposite(theSourceObject, aFeature) && + !theIgnoreList.contains(aFeature)) theDirectRefFeatures.insert(aFeature); } } } - - // Run recursion. It is possible recursive dependency, like the following: plane, extrusion uses plane, - // axis is built on extrusion. Delete of a plane should check the dependency from the axis also. - std::set::const_iterator aFeatureIt = theDirectRefFeatures.begin(); - for (; aFeatureIt != theDirectRefFeatures.end(); ++aFeatureIt) { - std::set aRecursiveRefFeatures; - refsToFeatureInAllDocuments(theSourceObject, *aFeatureIt, - aRecursiveRefFeatures, aRecursiveRefFeatures, theAlreadyProcessed); - theIndirectRefFeatures.insert(aRecursiveRefFeatures.begin(), aRecursiveRefFeatures.end()); - } } } diff --git a/src/XGUI/XGUI_Tools.h b/src/XGUI/XGUI_Tools.h index 48ab0d57b..b1947fc76 100644 --- a/src/XGUI/XGUI_Tools.h +++ b/src/XGUI/XGUI_Tools.h @@ -56,6 +56,12 @@ QString XGUI_EXPORT addSlash(const QString& path); // The model concerning tools +/*! Unite object names in one string using the separator between values + \param theObjects a list of objects + \param theSeparator a separator + */ +QString unionOfObjectNames(const QObjectPtrList& theObjects, const QString& theSeparator); + /*! Returns true if the feature is a model object \param theFeature a feature @@ -110,6 +116,13 @@ void XGUI_EXPORT refsToFeatureInFeatureDocument(const ObjectPtr& theObject, */ bool XGUI_EXPORT isSubOfComposite(const ObjectPtr& theObject, const FeaturePtr& theFeature); +/*! +*/ +void refsDirectToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectPtr& theObject, + const QObjectPtrList& theIgnoreList, + std::set& theDirectRefFeatures, + std::set& theAlreadyProcessed); + /*! Returns a container of references feature to the source object. The search happens in the object document and in other Part documents if the object belongs to the PartSet. The search is recursive, @@ -118,13 +131,15 @@ bool XGUI_EXPORT isSubOfComposite(const ObjectPtr& theObject, const FeaturePtr& which has the object as a sub object. \param theSourceObject an object, which references are searched \param theObject an intermediate recursive object, should be set in the source object + \param theIgnoreList an ignore list, the found referernces which coincide with the objects are ignored \param theDirectRefFeatures direct references - \param theIndirectRefFeatures indirect references + \param theIndirectRefFeatures indirect references. These are features that refers to the direct features \param theAlreadyProcessed set of processed elements, used for optimization (do not reanalyse processed) \return a boolean value */ void XGUI_EXPORT refsToFeatureInAllDocuments(const ObjectPtr& theSourceObject, const ObjectPtr& theObject, + const QObjectPtrList& theIgnoreList, std::set& theDirectRefFeatures, std::set& theIndirectRefFeatures, std::set& theAlreadyProcessed); diff --git a/src/XGUI/XGUI_Workshop.cpp b/src/XGUI/XGUI_Workshop.cpp index 110d0bcdc..61d1796be 100755 --- a/src/XGUI/XGUI_Workshop.cpp +++ b/src/XGUI/XGUI_Workshop.cpp @@ -1129,6 +1129,8 @@ void XGUI_Workshop::onContextMenuCommand(const QString& theId, bool isChecked) QObjectPtrList aObjects = mySelector->selection()->selectedObjects(); if (theId == "DELETE_CMD") deleteObjects(); + else if (theId == "CLEAN_HISTORY_CMD") + cleanHistory(); else if (theId == "MOVE_CMD") moveObjects(); else if (theId == "COLOR_CMD") @@ -1187,17 +1189,10 @@ void XGUI_Workshop::deleteObjects() // 1. start operation QString aDescription = contextMenuMgr()->action("DELETE_CMD")->text(); - aDescription += tr(" %1"); - QStringList aObjectNames; - foreach (ObjectPtr aObj, anObjects) { - if (!aObj->data()->isValid()) - continue; - aObjectNames << QString::fromStdString(aObj->data()->name()); - } - aDescription = aDescription.arg(aObjectNames.join(", ")); + aDescription += " " + aDescription.arg(XGUI_Tools::unionOfObjectNames(anObjects, ", ")); ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(aDescription, module()); - operationMgr()->startOperation(anOpAction); + // 3. delete objects std::set anIgnoredFeatures; if (deleteFeatures(anObjects, anIgnoredFeatures, desktop(), true)) { @@ -1208,6 +1203,66 @@ void XGUI_Workshop::deleteObjects() } } +//************************************************************** +void XGUI_Workshop::cleanHistory() +{ + if (!abortAllOperations()) + return; + + QObjectPtrList anObjects = mySelector->selection()->selectedObjects(); + + // 1. find all referenced features + std::set anUnusedFeatures; + std::set aDirectRefFeatures; + foreach (ObjectPtr anObject, anObjects) { + FeaturePtr aFeature = std::dynamic_pointer_cast(anObject); + if (aFeature.get()) { + std::set alreadyProcessed; + aDirectRefFeatures.clear(); + XGUI_Tools::refsDirectToFeatureInAllDocuments(anObject, anObject, anObjects, + aDirectRefFeatures, alreadyProcessed); + if (aDirectRefFeatures.empty() && anUnusedFeatures.find(aFeature) == anUnusedFeatures.end()) + anUnusedFeatures.insert(aFeature); + } + } + + // 2. warn about the references remove, break the delete operation if the user chose it + if (!anUnusedFeatures.empty()) { + QStringList aNames; + foreach (const FeaturePtr& aFeature, anUnusedFeatures) + aNames.append(aFeature->name().c_str()); + QString anUnusedNames = aNames.join(", "); + + QString anActionId = "CLEAN_HISTORY_CMD"; + QString aDescription = contextMenuMgr()->action("DELETE_CMD")->text(); + + QMessageBox aMessageBox(desktop()); + aMessageBox.setWindowTitle(aDescription); + aMessageBox.setIcon(QMessageBox::Warning); + aMessageBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + aMessageBox.setDefaultButton(QMessageBox::No); + + QString aText = QString(tr("Unused features are the following: %1.\nThese features will be deleted.\nWould you like to continue?") + .arg(anUnusedNames)); + aMessageBox.setText(aText); + if (aMessageBox.exec() == QMessageBox::No) + return; + + // 1. start operation + aDescription += "by deleting of " + aDescription.arg(XGUI_Tools::unionOfObjectNames(anObjects, ", ")); + ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(aDescription, module()); + operationMgr()->startOperation(anOpAction); + + std::set anIgnoredFeatures; + if (removeFeatures(anObjects, anIgnoredFeatures, anActionId)) { + operationMgr()->commitOperation(); + } + else { + operationMgr()->abortOperation(operationMgr()->currentOperation()); + } + } +} + //************************************************************** void XGUI_Workshop::moveObjects() { @@ -1263,8 +1318,8 @@ bool XGUI_Workshop::deleteFeatures(const QObjectPtrList& theList, std::set aDirectRefFeatures, aIndirectRefFeatures; foreach (ObjectPtr aDeletedObj, theList) { std::set alreadyProcessed; - XGUI_Tools::refsToFeatureInAllDocuments( - aDeletedObj, aDeletedObj, aDirectRefFeatures, aIndirectRefFeatures, alreadyProcessed); + XGUI_Tools::refsToFeatureInAllDocuments(aDeletedObj, aDeletedObj, theList, aDirectRefFeatures, + aIndirectRefFeatures, alreadyProcessed); std::set aDifference; std::set_difference(aIndirectRefFeatures.begin(), aIndirectRefFeatures.end(), aDirectRefFeatures.begin(), aDirectRefFeatures.end(), @@ -1350,7 +1405,17 @@ bool XGUI_Workshop::deleteFeatures(const QObjectPtrList& theList, } QString anActionId = "DELETE_CMD"; - QString anId = QString::fromStdString(anActionId.toStdString().c_str()); + removeFeatures(theList, theIgnoredFeatures, anActionId); +} + +//************************************************************** +bool XGUI_Workshop::removeFeatures(const QObjectPtrList& theList, + const std::set& theIgnoredFeatures, + const QString& theActionId) +{ + bool isDone = false; + + QString anId = QString::fromStdString(theActionId.toStdString().c_str()); QStringList anObjectGroups = contextMenuMgr()->actionObjectGroups(anId); // 4. remove the parameter features foreach (ObjectPtr aObj, theList) { @@ -1375,6 +1440,7 @@ bool XGUI_Workshop::deleteFeatures(const QObjectPtrList& theList, qDebug(QString("remove feature :%1").arg(anInfoStr).toStdString().c_str()); #endif aDoc->removeFeature(aFeature); + isDone = true; } } } -- 2.39.2