X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModuleBase%2FModuleBase_Tools.cpp;h=412b5e1094f7ba66b342d2c98413daae0cff2458;hb=e32f95642855a63da2727cb324ce2a75632a712f;hp=b42db0d7f5f99623b53f37489a58b0758260f511;hpb=a957eead1cbe68a3bcd8903dbf21c74cf8165fd5;p=modules%2Fshaper.git diff --git a/src/ModuleBase/ModuleBase_Tools.cpp b/src/ModuleBase/ModuleBase_Tools.cpp old mode 100644 new mode 100755 index b42db0d7f..412b5e109 --- a/src/ModuleBase/ModuleBase_Tools.cpp +++ b/src/ModuleBase/ModuleBase_Tools.cpp @@ -5,21 +5,56 @@ // Author: Vitaly Smetannikov #include "ModuleBase_Tools.h" + +#include #include +#include +#include +#include +#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include #include #include +#include + #include #include #include #include #include +#include +#include +#include +#include + +#include +#include + +const double tolerance = 1e-7; + +//#define DEBUG_ACTIVATE_WINDOW +//#define DEBUG_SET_FOCUS namespace ModuleBase_Tools { @@ -57,9 +92,44 @@ void zeroMargins(QLayout* theLayout) theLayout->setSpacing(5); } +void activateWindow(QWidget* theWidget, const QString& theInfo) +{ + theWidget->activateWindow(); + +#ifdef DEBUG_ACTIVATE_WINDOW + qDebug(QString("activateWindow: %1").arg(theInfo).toStdString().c_str()); +#endif +} + +void setFocus(QWidget* theWidget, const QString& theInfo) +{ + theWidget->setFocus(); + +#ifdef DEBUG_SET_FOCUS + qDebug(QString("setFocus: %1").arg(theInfo).toStdString().c_str()); +#endif +} + +void setShadowEffect(QWidget* theWidget, const bool isSetEffect) +{ + if (isSetEffect) { + QGraphicsDropShadowEffect* aGlowEffect = new QGraphicsDropShadowEffect(); + aGlowEffect->setOffset(.0); + aGlowEffect->setBlurRadius(10.0); + aGlowEffect->setColor(QColor(0, 170, 255)); // Light-blue color, #00AAFF + theWidget->setGraphicsEffect(aGlowEffect); + } + else { + QGraphicsEffect* anEffect = theWidget->graphicsEffect(); + if(anEffect) + anEffect->deleteLater(); + theWidget->setGraphicsEffect(NULL); + } +} + QPixmap composite(const QString& theAdditionalIcon, const QString& theIcon) { - QImage anIcon(theIcon); + QImage anIcon = ModuleBase_IconFactory::loadImage(theIcon); QImage anAditional(theAdditionalIcon); if (anIcon.isNull()) @@ -87,22 +157,22 @@ QPixmap composite(const QString& theAdditionalIcon, const QString& theIcon) QPixmap lighter(const QString& theIcon, const int theLighterValue) { - QImage anIcon(theIcon); + QImage anIcon = ModuleBase_IconFactory::loadImage(theIcon); if (anIcon.isNull()) return QPixmap(); - QImage aResult(theIcon); - for ( int i = 0; i < anIcon.width(); i++ ) + QImage aResult = ModuleBase_IconFactory::loadImage(theIcon); + for (int i = 0; i < anIcon.width(); i++) { - for ( int j = 0; j < anIcon.height(); j++ ) + for (int j = 0; j < anIcon.height(); j++) { - QRgb anRgb = anIcon.pixel( i, j ); + QRgb anRgb = anIcon.pixel(i, j); QColor aPixelColor(qRed(anRgb), qGreen(anRgb), qBlue(anRgb), - qAlpha( aResult.pixel( i, j ) )); + qAlpha(aResult.pixel(i, j))); QColor aLighterColor = aPixelColor.lighter(theLighterValue); - aResult.setPixel(i, j, qRgba( aLighterColor.red(), aLighterColor.green(), - aLighterColor.blue(), aLighterColor.alpha() ) ); + aResult.setPixel(i, j, qRgba(aLighterColor.red(), aLighterColor.green(), + aLighterColor.blue(), aLighterColor.alpha())); } } return QPixmap::fromImage(aResult); @@ -110,6 +180,10 @@ QPixmap lighter(const QString& theIcon, const int theLighterValue) void setSpinText(ModuleBase_ParamSpinBox* theSpin, const QString& theText) { + if (theSpin->text() == theText) + return; + // In order to avoid extra text setting because it will + // reset cursor position in control bool isBlocked = theSpin->blockSignals(true); theSpin->setText(theText); theSpin->blockSignals(isBlocked); @@ -117,6 +191,37 @@ void setSpinText(ModuleBase_ParamSpinBox* theSpin, const QString& theText) void setSpinValue(QDoubleSpinBox* theSpin, double theValue) { + if (fabs(theSpin->value() - theValue) < tolerance) + return; + bool isBlocked = theSpin->blockSignals(true); + theSpin->setValue(theValue); + theSpin->blockSignals(isBlocked); +} + +void setSpinValue(ModuleBase_ParamSpinBox* theSpin, double theValue) +{ + if (fabs(theSpin->value() - theValue) < tolerance) + return; + bool isBlocked = theSpin->blockSignals(true); + theSpin->setValue(theValue); + theSpin->blockSignals(isBlocked); +} + +void setSpinText(ModuleBase_ParamIntSpinBox* theSpin, const QString& theText) +{ + // In order to avoid extra text setting because it will + // reset cursor position in control + if (theSpin->text() == theText) + return; + bool isBlocked = theSpin->blockSignals(true); + theSpin->setText(theText); + theSpin->blockSignals(isBlocked); +} + +void setSpinValue(ModuleBase_ParamIntSpinBox* theSpin, int theValue) +{ + if (theSpin->value() == theValue) + return; bool isBlocked = theSpin->blockSignals(true); theSpin->setValue(theValue); theSpin->blockSignals(isBlocked); @@ -124,18 +229,35 @@ void setSpinValue(QDoubleSpinBox* theSpin, double theValue) QString objectInfo(const ObjectPtr& theObj, const bool isUseAttributesInfo) { + QString aFeatureStr = "feature"; + if (!theObj.get()) + return aFeatureStr; + + std::ostringstream aPtrStr; + aPtrStr << "[" << theObj.get() << "]"; + ResultPtr aRes = std::dynamic_pointer_cast(theObj); FeaturePtr aFeature = std::dynamic_pointer_cast(theObj); - QString aFeatureStr = "feature"; if(aRes.get()) { - aFeatureStr.append("(Result)"); + aFeatureStr.append(QString("(result%1)").arg(aPtrStr.str().c_str()).toStdString() .c_str()); + if (aRes->isDisabled()) + aFeatureStr.append("[disabled]"); + if (aRes->isConcealed()) + aFeatureStr.append("[concealed]"); + if (ModelAPI_Tools::hasSubResults(aRes)) + aFeatureStr.append("[hasSubResults]"); + aFeature = ModelAPI_Feature::feature(aRes); } + else + aFeatureStr.append(aPtrStr.str().c_str()); + if (aFeature.get()) { aFeatureStr.append(QString(": %1").arg(aFeature->getKind().c_str()).toStdString().c_str()); - if (aFeature->data().get() && aFeature->data()->isValid()) + if (aFeature->data()->isValid()) { aFeatureStr.append(QString(", name=%1").arg(aFeature->data()->name().c_str()).toStdString() .c_str()); + } if (isUseAttributesInfo) { std::list anAttrs = aFeature->data()->attributes(""); std::list::const_iterator anIt = anAttrs.begin(), aLast = anAttrs.end(); @@ -179,6 +301,7 @@ TopAbs_ShapeEnum shapeType(const QString& theType) MyShapeTypes["shell"] = TopAbs_SHELL; MyShapeTypes["solid"] = TopAbs_SOLID; MyShapeTypes["solids"] = TopAbs_SOLID; + MyShapeTypes["objects"] = TopAbs_SHAPE; } QString aType = theType.toLower(); if (MyShapeTypes.contains(aType)) @@ -187,7 +310,550 @@ TopAbs_ShapeEnum shapeType(const QString& theType) return TopAbs_SHAPE; } +void checkObjects(const QObjectPtrList& theObjects, bool& hasResult, bool& hasFeature, + bool& hasParameter, bool& hasCompositeOwner) +{ + hasResult = false; + hasFeature = false; + hasParameter = false; + hasCompositeOwner = false; + foreach(ObjectPtr aObj, theObjects) { + FeaturePtr aFeature = std::dynamic_pointer_cast(aObj); + ResultPtr aResult = std::dynamic_pointer_cast(aObj); + ResultParameterPtr aConstruction = std::dynamic_pointer_cast(aResult); + + hasResult |= (aResult.get() != NULL); + hasFeature |= (aFeature.get() != NULL); + hasParameter |= (aConstruction.get() != NULL); + if (hasFeature) + hasCompositeOwner |= (ModelAPI_Tools::compositeOwner(aFeature) != NULL); + if (hasFeature && hasResult && hasParameter && hasCompositeOwner) + break; + } +} +void setDefaultDeviationCoefficient(const TopoDS_Shape& theShape, + const Handle(Prs3d_Drawer)& theDrawer) +{ + if (theShape.IsNull()) + return; + TopAbs_ShapeEnum aType = theShape.ShapeType(); + if ((aType == TopAbs_EDGE) || (aType == TopAbs_WIRE)) + theDrawer->SetDeviationCoefficient(1.e-4); +} + +Quantity_Color color(const std::string& theSection, + const std::string& theName, + const std::string& theDefault) +{ + std::vector aColor = Config_PropManager::color(theSection, theName, theDefault); + return Quantity_Color(aColor[0] / 255., aColor[1] / 255., aColor[2] / 255., Quantity_TOC_RGB); } +ObjectPtr getObject(const AttributePtr& theAttribute) +{ + ObjectPtr anObject; + std::string anAttrType = theAttribute->attributeType(); + if (anAttrType == ModelAPI_AttributeRefAttr::typeId()) { + AttributeRefAttrPtr anAttr = std::dynamic_pointer_cast(theAttribute); + if (anAttr != NULL && anAttr->isObject()) + anObject = anAttr->object(); + } + if (anAttrType == ModelAPI_AttributeSelection::typeId()) { + AttributeSelectionPtr anAttr = std::dynamic_pointer_cast(theAttribute); + if (anAttr != NULL) + anObject = anAttr->context(); + } + if (anAttrType == ModelAPI_AttributeReference::typeId()) { + AttributeReferencePtr anAttr = std::dynamic_pointer_cast(theAttribute); + if (anAttr.get() != NULL) + anObject = anAttr->value(); + } + return anObject; +} + +TopAbs_ShapeEnum getCompoundSubType(const TopoDS_Shape& theShape) +{ + TopAbs_ShapeEnum aShapeType = theShape.ShapeType(); + + // for compounds check sub-shapes: it may be compound of needed type: + // Booleans may produce compounds of Solids + if (aShapeType == TopAbs_COMPOUND) { + for(TopoDS_Iterator aSubs(theShape); aSubs.More(); aSubs.Next()) { + if (!aSubs.Value().IsNull()) { + TopAbs_ShapeEnum aSubType = aSubs.Value().ShapeType(); + if (aSubType == TopAbs_COMPOUND) { // compound of compound(s) + aShapeType = TopAbs_COMPOUND; + break; + } + if (aShapeType == TopAbs_COMPOUND) { + aShapeType = aSubType; + } else if (aShapeType != aSubType) { // compound of shapes of different types + aShapeType = TopAbs_COMPOUND; + break; + } + } + } + } + return aShapeType; +} + +void getParameters(QStringList& theParameters) +{ + theParameters.clear(); + + SessionPtr aSession = ModelAPI_Session::get(); + std::list aDocList; + DocumentPtr anActiveDocument = aSession->activeDocument(); + DocumentPtr aRootDocument = aSession->moduleDocument(); + aDocList.push_back(anActiveDocument); + if (anActiveDocument != aRootDocument) { + aDocList.push_back(aRootDocument); + } + std::string aGroupId = ModelAPI_ResultParameter::group(); + for(std::list::const_iterator it = aDocList.begin(); it != aDocList.end(); ++it) { + DocumentPtr aDocument = *it; + int aSize = aDocument->size(aGroupId); + for (int i = 0; i < aSize; i++) { + ObjectPtr anObject = aDocument->object(aGroupId, i); + std::string aParameterName = anObject->data()->name(); + theParameters.append(aParameterName.c_str()); + } + } +} + +std::string findGreedAttribute(ModuleBase_IWorkshop* theWorkshop, const FeaturePtr& theFeature) +{ + std::string anAttributeId; + + std::string aXmlCfg, aDescription; + theWorkshop->module()->getXMLRepresentation(theFeature->getKind(), aXmlCfg, aDescription); + + ModuleBase_WidgetFactory aFactory(aXmlCfg, theWorkshop); + std::string anAttributeTitle; + aFactory.getGreedAttribute(anAttributeId); + + return anAttributeId; +} + +void setObject(const AttributePtr& theAttribute, const ObjectPtr& theObject, + const GeomShapePtr& theShape, ModuleBase_IWorkshop* theWorkshop, + const bool theTemporarily) +{ + if (!theAttribute.get()) + return; + + std::string aType = theAttribute->attributeType(); + if (aType == ModelAPI_AttributeReference::typeId()) { + AttributeReferencePtr aRef = std::dynamic_pointer_cast(theAttribute); + ObjectPtr aObject = aRef->value(); + if (!(aObject && aObject->isSame(theObject))) { + aRef->setValue(theObject); + } + } else if (aType == ModelAPI_AttributeRefAttr::typeId()) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast(theAttribute); + + AttributePtr anAttribute = theWorkshop->module()->findAttribute(theObject, theShape); + if (anAttribute.get()) + aRefAttr->setAttr(anAttribute); + else { + ObjectPtr aObject = aRefAttr->object(); + if (!(aObject && aObject->isSame(theObject))) { + aRefAttr->setObject(theObject); + } + } + } else if (aType == ModelAPI_AttributeSelection::typeId()) { + AttributeSelectionPtr aSelectAttr = + std::dynamic_pointer_cast(theAttribute); + ResultPtr aResult = std::dynamic_pointer_cast(theObject); + if (aSelectAttr.get() != NULL) { + aSelectAttr->setValue(aResult, theShape, theTemporarily); + } + } + if (aType == ModelAPI_AttributeSelectionList::typeId()) { + AttributeSelectionListPtr aSelectionListAttr = + std::dynamic_pointer_cast(theAttribute); + ResultPtr aResult = std::dynamic_pointer_cast(theObject); + if (!aSelectionListAttr->isInList(aResult, theShape, theTemporarily)) + aSelectionListAttr->append(aResult, theShape, theTemporarily); + } + else if (aType == ModelAPI_AttributeRefList::typeId()) { + AttributeRefListPtr aRefListAttr = std::dynamic_pointer_cast(theAttribute); + if (!aRefListAttr->isInList(theObject)) + aRefListAttr->append(theObject); + } + else if (aType == ModelAPI_AttributeRefAttrList::typeId()) { + AttributeRefAttrListPtr aRefAttrListAttr = std::dynamic_pointer_cast(theAttribute); + AttributePtr anAttribute = theWorkshop->module()->findAttribute(theObject, theShape); + + if (anAttribute.get()) { + if (!aRefAttrListAttr->isInList(anAttribute)) + aRefAttrListAttr->append(anAttribute); + } + else { + if (!aRefAttrListAttr->isInList(theObject)) + aRefAttrListAttr->append(theObject); + } + } +} + +GeomShapePtr getShape(const AttributePtr& theAttribute, ModuleBase_IWorkshop* theWorkshop) +{ + GeomShapePtr aShape; + if (!theAttribute.get()) + return aShape; + + std::string aType = theAttribute->attributeType(); + if (aType == ModelAPI_AttributeReference::typeId()) { + } else if (aType == ModelAPI_AttributeRefAttr::typeId()) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast(theAttribute); + if (aRefAttr.get() && !aRefAttr->isObject()) { + AttributePtr anAttribute = aRefAttr->attr(); + aShape = theWorkshop->module()->findShape(anAttribute); + } + } else if (aType == ModelAPI_AttributeSelection::typeId()) { + AttributeSelectionPtr aSelectAttr = std::dynamic_pointer_cast + (theAttribute); + aShape = aSelectAttr->value(); + } + return aShape; +} + +void flushUpdated(ObjectPtr theObject) +{ + blockUpdateViewer(true); + + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + + blockUpdateViewer(false); +} + +void blockUpdateViewer(const bool theValue) +{ + // the viewer update should be blocked in order to avoid the temporary feature content + // when the solver processes the feature, the redisplay message can be flushed + // what caused the display in the viewer preliminary states of object + // e.g. fillet feature, angle value change + std::shared_ptr aMsg; + if (theValue) { + aMsg = std::shared_ptr( + new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED))); + } + else { + // the viewer update should be unblocked + aMsg = std::shared_ptr( + new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED))); + } + Events_Loop::loop()->send(aMsg); +} + +QString wrapTextByWords(const QString& theValue, QWidget* theWidget, + int theMaxLineInPixels) +{ + static QFontMetrics tfm(theWidget ? theWidget->font() : QApplication::font()); + static qreal phi = 2.618; + + QRect aBounds = tfm.boundingRect(theValue); + if(aBounds.width() <= theMaxLineInPixels) + return theValue; + + qreal s = aBounds.width() * aBounds.height(); + qreal aGoldWidth = sqrt(s*phi); + + QStringList aWords = theValue.split(" ", QString::SkipEmptyParts); + QStringList aLines; + int n = aWords.count(); + QString aLine; + for (int i = 0; i < n; i++) { + QString aLineExt = aLine + " " + aWords[i]; + qreal anWidthNonExt = tfm.boundingRect(aLine).width(); + qreal anWidthExt = tfm.boundingRect(aLineExt).width(); + qreal aDeltaNonExt = fabs(anWidthNonExt-aGoldWidth); + qreal aDeltaExt = fabs(anWidthExt-aGoldWidth); + if(aDeltaNonExt < aDeltaExt) { + // new line + aLines.append(aLine); + aLine = aWords[i]; + } + else + aLine = aLineExt; + } + + if(!aLine.isEmpty()) + aLines.append(aLine); + + QString aResult = aLines.join("\n"); + return aResult; +} + +void findReferences(const QObjectPtrList& theList, + std::set& aDirectRefFeatures, + std::set& aIndirectRefFeatures) +{ + foreach (ObjectPtr aDeletedObj, theList) { + std::set alreadyProcessed; + refsToFeatureInAllDocuments(aDeletedObj, aDeletedObj, theList, aDirectRefFeatures, + aIndirectRefFeatures, alreadyProcessed); + std::set aDifference; + std::set_difference(aIndirectRefFeatures.begin(), aIndirectRefFeatures.end(), + aDirectRefFeatures.begin(), aDirectRefFeatures.end(), + std::inserter(aDifference, aDifference.begin())); + aIndirectRefFeatures = aDifference; + } +} + +//************************************************************** +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()) + return; + if (theAlreadyProcessed.find(aFeature) != theAlreadyProcessed.end()) + return; + theAlreadyProcessed.insert(aFeature); + + //convert ignore object list to containt sub-features if the composite feature is in the list + QObjectPtrList aFullIgnoreList; + QObjectPtrList::const_iterator anIIt = theIgnoreList.begin(), anILast = theIgnoreList.end(); + for (; anIIt != anILast; anIIt++) { + aFullIgnoreList.append(*anIIt); + CompositeFeaturePtr aComposite = std::dynamic_pointer_cast(*anIIt); + // if the current feature is aborted, the composite is removed and has invalid data + if (aComposite.get() && aComposite->data()->isValid()) { + int aNbSubs = aComposite->numberOfSubs(); + for (int aSub = 0; aSub < aNbSubs; aSub++) { + aFullIgnoreList.append(aComposite->subFeature(aSub)); + } + } + } + + // 1. find references in the current document + std::set aRefFeatures; + refsToFeatureInFeatureDocument(theObject, aRefFeatures); + std::set::const_iterator anIt = aRefFeatures.begin(), + 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) && !aFullIgnoreList.contains(*anIt)) + theDirectRefFeatures.insert(*anIt); + } + + // 2. find references in all documents if the document of the feature is + // "PartSet". Features of this document can be used in all other documents + DocumentPtr aFeatureDoc = aFeature->document(); + + SessionPtr aMgr = ModelAPI_Session::get(); + DocumentPtr aModuleDoc = aMgr->moduleDocument(); + if (aFeatureDoc == aModuleDoc) { + // the feature and results of the feature should be found in references + std::list aObjects; + aObjects.push_back(aFeature); + typedef std::list > ResultsList; + const ResultsList& aResults = aFeature->results(); + ResultsList::const_iterator aRIter = aResults.begin(); + for (; aRIter != aResults.cend(); aRIter++) { + ResultPtr aRes = *aRIter; + if (aRes.get()) + aObjects.push_back(aRes); + } + // get all opened documents; found features in the documents; + // get a list of objects where a feature refers; + // search in these objects the deleted objects. + SessionPtr aMgr = ModelAPI_Session::get(); + std::list anOpenedDocs = aMgr->allOpenedDocuments(); + std::list::const_iterator anIt = anOpenedDocs.begin(), + aLast = anOpenedDocs.end(); + std::list > > aRefs; + for (; anIt != aLast; anIt++) { + DocumentPtr aDocument = *anIt; + if (aDocument == aFeatureDoc) + continue; // this document has been already processed in 1.1 + + int aFeaturesCount = aDocument->size(ModelAPI_Feature::group()); + for (int aId = 0; aId < aFeaturesCount; aId++) { + ObjectPtr anObject = aDocument->object(ModelAPI_Feature::group(), aId); + FeaturePtr aFeature = std::dynamic_pointer_cast(anObject); + if (!aFeature.get()) + continue; + + aRefs.clear(); + aFeature->data()->referencesToObjects(aRefs); + std::list > >::iterator aRef = aRefs.begin(); + bool aHasReferenceToObject = false; + for(; aRef != aRefs.end() && !aHasReferenceToObject; aRef++) { + std::list::iterator aRefObj = aRef->second.begin(); + for(; aRefObj != aRef->second.end() && !aHasReferenceToObject; aRefObj++) { + std::list::const_iterator aObjIt = aObjects.begin(); + for(; aObjIt != aObjects.end() && !aHasReferenceToObject; aObjIt++) { + aHasReferenceToObject = *aObjIt == *aRefObj; + } + } + } + if (aHasReferenceToObject && !isSubOfComposite(theSourceObject, aFeature) && + !theIgnoreList.contains(aFeature)) + theDirectRefFeatures.insert(aFeature); + } + } + } +} + +//************************************************************** +void refsToFeatureInFeatureDocument(const ObjectPtr& theObject, std::set& theRefFeatures) +{ + FeaturePtr aFeature = ModelAPI_Feature::feature(theObject); + if (aFeature.get()) { + DocumentPtr aFeatureDoc = aFeature->document(); + // 1. find references in the current document + aFeatureDoc->refsToFeature(aFeature, theRefFeatures, false); + } +} + + +//************************************************************** +bool isSubOfComposite(const ObjectPtr& theObject) +{ + bool isSub = false; + std::set aRefFeatures; + refsToFeatureInFeatureDocument(theObject, aRefFeatures); + std::set::const_iterator anIt = aRefFeatures.begin(), + aLast = aRefFeatures.end(); + for (; anIt != aLast && !isSub; anIt++) { + isSub = isSubOfComposite(theObject, *anIt); + } + return isSub; +} + +//************************************************************** +bool isSubOfComposite(const ObjectPtr& theObject, const FeaturePtr& theFeature) +{ + bool isSub = false; + CompositeFeaturePtr aComposite = std::dynamic_pointer_cast(theFeature); + if (aComposite.get()) { + isSub = aComposite->isSub(theObject); + // the recursive is possible, the parameters are sketch circle and extrusion cut. They are + // separated by composite sketch feature + if (!isSub) { + int aNbSubs = aComposite->numberOfSubs(); + for (int aSub = 0; aSub < aNbSubs && !isSub; aSub++) { + isSub = isSubOfComposite(theObject, aComposite->subFeature(aSub)); + } + } + } + return isSub; +} + + +//************************************************************** +bool isDeleteFeatureWithReferences(const QObjectPtrList& theList, + const std::set& aDirectRefFeatures, + const std::set& aIndirectRefFeatures, + QWidget* theParent, + bool& doDeleteReferences) +{ + doDeleteReferences = true; + + QString aDirectNames, aIndirectNames; + if (!aDirectRefFeatures.empty()) { + QStringList aDirectRefNames; + foreach (const FeaturePtr& aFeature, aDirectRefFeatures) + aDirectRefNames.append(aFeature->name().c_str()); + aDirectNames = aDirectRefNames.join(", "); + + QStringList aIndirectRefNames; + foreach (const FeaturePtr& aFeature, aIndirectRefFeatures) + aIndirectRefNames.append(aFeature->name().c_str()); + aIndirectNames = aIndirectRefNames.join(", "); + } + + bool aCanReplaceParameters = !aDirectRefFeatures.empty(); + QStringList aPartFeatureNames; + foreach (ObjectPtr aObj, theList) { + FeaturePtr aFeature = ModelAPI_Feature::feature(aObj); + // invalid feature data means that the feature is already removed in model, + // we needn't process it. E.g. delete of feature from create operation. The operation abort + // will delete the operation + if (!aFeature->data()->isValid()) + continue; + ResultPtr aFirstResult = aFeature->firstResult(); + if (!aFirstResult.get()) + continue; + std::string aResultGroupName = aFirstResult->groupName(); + if (aResultGroupName == ModelAPI_ResultPart::group()) + aPartFeatureNames.append(aFeature->name().c_str()); + + if (aCanReplaceParameters && aResultGroupName != ModelAPI_ResultParameter::group()) + aCanReplaceParameters = false; + } + QString aPartNames = aPartFeatureNames.join(", "); + + QMessageBox aMessageBox(theParent); + aMessageBox.setWindowTitle(QObject::tr("Delete features")); + aMessageBox.setIcon(QMessageBox::Warning); + aMessageBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + aMessageBox.setDefaultButton(QMessageBox::No); + + QString aText; + if (!aDirectNames.isEmpty() || !aIndirectNames.isEmpty()) { + if (aCanReplaceParameters) { + aText = QString(QObject::tr("Selected parameters are used in the following features: %1.\nThese features will be deleted.\nOr parameters could be replaced by their values.\n") + .arg(aDirectNames)); + if (!aIndirectNames.isEmpty()) + aText += QString(QObject::tr("(Also these features will be deleted: %1)\n")).arg(aIndirectNames); + QPushButton *aReplaceButton = aMessageBox.addButton(QObject::tr("Replace"), QMessageBox::ActionRole); + } else { + aText = QString(QObject::tr("Selected features are used in the following features: %1.\nThese features will be deleted.\n")).arg(aDirectNames); + if (!aIndirectNames.isEmpty()) + aText += QString(QObject::tr("(Also these features will be deleted: %1)\n")).arg(aIndirectNames); + } + } + if (!aPartNames.isEmpty()) + aText += QString(QObject::tr("The following parts will be deleted: %1.\n")).arg(aPartNames); + + if (!aText.isEmpty()) { + aText += "Would you like to continue?"; + aMessageBox.setText(aText); + aMessageBox.exec(); + QMessageBox::ButtonRole aButtonRole = aMessageBox.buttonRole(aMessageBox.clickedButton()); + + if (aButtonRole == QMessageBox::NoRole) + return false; + + if (aButtonRole == QMessageBox::ActionRole) { + foreach (ObjectPtr aObj, theList) + ModelAPI_ReplaceParameterMessage::send(aObj, 0); + doDeleteReferences = false; + } + } + return true; +} + +} // namespace ModuleBase_Tools +