1 // Copyright (C) 2014-2022 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "PartSet_MenuMgr.h"
21 #include "PartSet_Module.h"
22 #include "PartSet_SketcherMgr.h"
23 #include "PartSet_Tools.h"
25 #include <PartSetPlugin_Part.h>
27 #include <GeomAPI_Pnt2d.h>
28 #include <GeomDataAPI_Point2D.h>
30 #include <SketchPlugin_ConstraintCoincidence.h>
31 #include <SketchPlugin_Line.h>
32 #include <SketchPlugin_Circle.h>
33 #include <SketchPlugin_Point.h>
34 #include <SketchPlugin_Sketch.h>
36 #include <ModuleBase_ISelection.h>
37 #include <ModuleBase_Operation.h>
38 #include <ModuleBase_OperationFeature.h>
39 #include <ModuleBase_ViewerPrs.h>
40 #include <ModuleBase_IViewer.h>
41 #include <ModuleBase_Tools.h>
43 #include <XGUI_ModuleConnector.h>
44 #include <XGUI_Workshop.h>
45 #include <XGUI_Displayer.h>
46 #include <XGUI_DataModel.h>
47 #include <XGUI_OperationMgr.h>
48 #include <XGUI_ObjectsBrowser.h>
49 #include <XGUI_Tools.h>
50 #include <XGUI_ViewerProxy.h>
52 #include <Events_Loop.h>
53 #include <ModelAPI_Events.h>
54 #include <ModelAPI_Session.h>
55 #include <ModelAPI_ResultParameter.h>
57 #include <QMainWindow>
61 #include <QApplication>
64 #include <BRep_Tool.hxx>
66 PartSet_MenuMgr::PartSet_MenuMgr(PartSet_Module* theModule)
67 : QObject(theModule), myModule(theModule), myPrevId(-1)
73 QAction* PartSet_MenuMgr::action(const QString& theId) const
75 if (myActions.contains(theId))
76 return myActions[theId];
80 void PartSet_MenuMgr::addAction(const QString& theId, QAction* theAction)
82 if (myActions.contains(theId))
83 qCritical("A command with Id = '%s' already defined!", qPrintable(theId));
84 theAction->setData(theId);
85 connect(theAction, SIGNAL(triggered(bool)), this, SLOT(onAction(bool)));
86 myActions[theId] = theAction;
89 void PartSet_MenuMgr::createActions()
93 QWidget* aParent = myModule->workshop()->desktop();
94 aAction = ModuleBase_Tools::createAction(QIcon(), tr("Auxiliary"), aParent);
95 aAction->setCheckable(true);
96 addAction("AUXILIARY_CMD", aAction);
98 aAction = ModuleBase_Tools::createAction(QIcon(":icons/activate.png"), tr("Activate"), aParent,
99 this, SLOT(onActivatePart(bool)));
100 myActions["ACTIVATE_PART_CMD"] = aAction;
103 aAction = ModuleBase_Tools::createAction(QIcon(":icons/activate.png"), tr("Activate"), aParent,
104 this, SLOT(onActivatePartSet(bool)));
105 myActions["ACTIVATE_PARTSET_CMD"] = aAction;
107 aAction = ModuleBase_Tools::createAction(QIcon(":icons/edit.png"), tr("Edit..."), aParent,
108 this, SLOT(onEdit(bool)));
109 myActions["EDIT_CMD"] = aAction;
111 aAction = ModuleBase_Tools::createAction(QIcon(":icons/activate.png"), tr("Load all parts"),
112 aParent, this, SLOT(onActivateAllParts()));
113 myActions["ACTIVATE_ALL_PARTS_CMD"] = aAction;
117 void PartSet_MenuMgr::onAction(bool isChecked)
119 QAction* aAction = static_cast<QAction*>(sender());
120 QString anId = aAction->data().toString();
122 if (anId == "AUXILIARY_CMD") {
123 setAuxiliary(isChecked);
127 bool PartSet_MenuMgr::addViewerMenu(const QMap<QString, QAction*>& theStdActions,
129 QMap<int, QAction*>& theMenuActions) const
133 ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
134 if (!PartSet_SketcherMgr::isSketchOperation(anOperation) &&
135 !myModule->sketchMgr()->isNestedSketchOperation(anOperation))
138 myCoinsideLines.clear();
139 ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
141 bool aIsDetach = false;
142 bool hasAttribute = false;
143 bool hasFeature = false;
145 QList<ModuleBase_ViewerPrsPtr> aPrsList = aSelection->getSelected(ModuleBase_ISelection::Viewer);
146 if (aPrsList.size() > 1) {
148 } else if (aPrsList.size() == 1) {
151 foreach(ModuleBase_ViewerPrsPtr aPrs, aPrsList) {
152 aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPrs->object());
153 if (aResult.get() != NULL) {
154 const GeomShapePtr& aShape = aPrs->shape();
155 if (aShape.get() && aShape->isEqual(aResult->shape()))
160 aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aPrs->object());
161 hasFeature = (aFeature.get() != NULL);
165 const GeomShapePtr& aShape = aPrsList.first()->shape();
166 if (aShape.get() && !aShape->isNull() && aShape->shapeType() == GeomAPI_Shape::VERTEX) {
167 // Find 2d coordinates
168 FeaturePtr aSketchFea = myModule->sketchMgr()->activeSketch();
169 if (aSketchFea->getKind() == SketchPlugin_Sketch::ID()) {
170 const TopoDS_Shape& aTDShape = aShape->impl<TopoDS_Shape>();
171 gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aTDShape));
172 std::shared_ptr<GeomAPI_Pnt> aPnt3d(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
173 std::shared_ptr<GeomAPI_Pnt2d> aSelPnt = PartSet_Tools::convertTo2D(aSketchFea, aPnt3d);
175 // Find coincident in these coordinates
176 ObjectPtr aObj = aPrsList.first()->object();
177 FeaturePtr aCoincident =
178 PartSet_Tools::findFirstCoincidence(ModelAPI_Feature::feature(aObj), aSelPnt);
179 // If we have coincidence then add Detach menu
180 if (aCoincident.get() != NULL) {
181 QList<FeaturePtr> aCoins;
182 mySelectedFeature = aCoincident;
183 QList<bool> anIsAttributes;
184 PartSet_Tools::findCoincidences(mySelectedFeature, myCoinsideLines, aCoins,
185 SketchPlugin_ConstraintCoincidence::ENTITY_A(),
187 PartSet_Tools::findCoincidences(mySelectedFeature, myCoinsideLines, aCoins,
188 SketchPlugin_ConstraintCoincidence::ENTITY_B(),
190 if (myCoinsideLines.size() > 0) {
192 QMenu* aSubMenu = new QMenu(tr("Detach"), theParent);
193 theMenuActions[anIndex++] = aSubMenu->menuAction();
196 foreach (FeaturePtr aCoinsL, myCoinsideLines) {
197 QString anItemText = QString::fromStdWString(aCoinsL->data()->name());
199 if (anIsAttributes[i])
200 anItemText += " [attribute]";
202 aAction = aSubMenu->addAction(anItemText);
203 aAction->setData(QVariant(i));
206 connect(aSubMenu, SIGNAL(hovered(QAction*)), SLOT(onLineHighlighted(QAction*)));
207 connect(aSubMenu, SIGNAL(aboutToHide()), SLOT(onDetachMenuHide()));
208 connect(aSubMenu, SIGNAL(triggered(QAction*)), SLOT(onLineDetach(QAction*)));
216 if (canSetAuxiliary(isAuxiliary)) {
217 QAction* anAction = action("AUXILIARY_CMD");
218 theMenuActions[anIndex++] = anAction;
219 anAction->setChecked(isAuxiliary);
223 if (!aIsDetach && hasFeature) {
224 // Delete item should be the last in the list of actions
225 theMenuActions[1000] = theStdActions["DELETE_CMD"];
231 void PartSet_MenuMgr::updateViewerMenu(const QMap<QString, QAction*>& theStdActions)
233 ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
235 bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
236 myModule->sketchMgr()->isNestedSketchOperation(anOperation);
237 if (isActiveSketch) {
238 theStdActions["WIREFRAME_CMD"]->setEnabled(false);
239 theStdActions["SHADING_CMD"]->setEnabled(false);
240 theStdActions["SHOW_ONLY_CMD"]->setEnabled(false);
241 theStdActions["SHOW_CMD"]->setEnabled(false);
242 theStdActions["HIDE_CMD"]->setEnabled(false);
243 theStdActions["HIDEALL_CMD"]->setEnabled(false);
248 void PartSet_MenuMgr::onLineHighlighted(QAction* theAction)
250 if (myPrevId != -1) {
251 // Restore color for previous object
252 setLineColor(myPrevId, myColor, false);
254 myPrevId = theAction->data().toInt();
255 myColor = setLineColor(myPrevId, Qt::white, true);
258 QColor PartSet_MenuMgr::setLineColor(int theId, const QColor theColor, bool theUpdate)
260 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
261 XGUI_Workshop* aWorkshop = aConnector->workshop();
262 XGUI_Displayer* aDisplayer = aWorkshop->displayer();
264 FeaturePtr aLine = myCoinsideLines.at(myPrevId);
265 std::list<ResultPtr>::const_iterator aIt;
266 const std::list<ResultPtr>& aResults = aLine->results();
268 for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
269 aColor = aDisplayer->setObjectColor((*aIt), theColor, false);
272 aDisplayer->updateViewer();
277 void addRefCoincidentFeatures(const std::set<AttributePtr>& theRefList,
278 std::shared_ptr<GeomAPI_Pnt2d>& theRefPnt,
279 QObjectPtrList& theOutList)
281 std::set<AttributePtr>::const_iterator aIt;
282 for (aIt = theRefList.cbegin(); aIt != theRefList.cend(); ++aIt) {
283 std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
284 FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
285 if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
286 std::shared_ptr<GeomAPI_Pnt2d> aPnt = PartSet_Tools::getCoincedencePoint(aConstrFeature);
287 if (aPnt.get() == NULL)
289 if (theRefPnt->isEqual(aPnt) && (!theOutList.contains(aConstrFeature))) {
290 theOutList.append(aConstrFeature);
296 void PartSet_MenuMgr::onLineDetach(QAction* theAction)
298 int aId = theAction->data().toInt();
299 FeaturePtr aLine = myCoinsideLines.at(aId);
300 std::shared_ptr<GeomAPI_Pnt2d> aOrig = PartSet_Tools::getCoincedencePoint(mySelectedFeature);
304 const std::set<AttributePtr>& aRefsList = aLine->data()->refsToMe();
306 QObjectPtrList aToDelFeatures;
308 addRefCoincidentFeatures(aRefsList, aOrig, aToDelFeatures);
310 const std::list<ResultPtr>& aResults = aLine->results();
311 std::list<ResultPtr>::const_iterator aResIt;
312 for (aResIt = aResults.cbegin(); aResIt != aResults.cend(); aResIt++) {
313 ResultPtr aResult = (*aResIt);
314 const std::set<AttributePtr>& aRefList = aResult->data()->refsToMe();
315 addRefCoincidentFeatures(aRefList, aOrig, aToDelFeatures);
317 if (aToDelFeatures.size() > 0) {
318 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
319 XGUI_Workshop* aWorkshop = aConnector->workshop();
321 ModuleBase_Operation* anOpAction = new ModuleBase_Operation(tr("Detach %1").arg(
322 QString::fromStdWString(aLine->data()->name())), myModule);
323 XGUI_OperationMgr* anOpMgr = aConnector->workshop()->operationMgr();
324 // the active nested sketch operation should be aborted unconditionally
325 // the Delete action should be additionally granted for the Sketch operation
326 // in order to do not abort/commit it
328 if (!anOpMgr->canStartOperation(tr("Detach"), isCommitted))
329 return; // the objects are processed but can not be deleted
331 anOpMgr->startOperation(anOpAction);
332 aWorkshop->deleteFeatures(aToDelFeatures);
334 anOpMgr->commitOperation();
336 myCoinsideLines.clear();
340 void PartSet_MenuMgr::onDetachMenuHide()
342 if (myPrevId != -1) {
343 // Restore color for previous object
344 setLineColor(myPrevId, myColor, false);
346 // Clear previous definitions
351 void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
353 ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
355 CompositeFeaturePtr aSketch = myModule->sketchMgr()->activeSketch();
356 bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
357 myModule->sketchMgr()->isNestedSketchOperation(anOperation);
361 QObjectPtrList anObjects;
362 bool isUseTransaction = false;
363 // 1. change auxiliary type of a created feature
364 if (myModule->sketchMgr()->isNestedCreateOperation(anOperation, aSketch) &&
365 PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
366 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
369 anObjects.append(aFOperation->feature());
372 isUseTransaction = true;
373 // 2. change auxiliary type of selected sketch entities
374 ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
375 anObjects = aSelection->selectedPresentations();
378 QAction* anAction = action("AUXILIARY_CMD");
379 //SessionPtr aMgr = ModelAPI_Session::get();
380 ModuleBase_Operation* anOpAction = 0;
381 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
382 XGUI_OperationMgr* anOpMgr = aConnector->workshop()->operationMgr();
383 if (isUseTransaction) {
384 anOpAction = new ModuleBase_Operation(anAction->text(), myModule);
387 if (!anOpMgr->canStartOperation(anOpAction->id(), isCommitted))
388 return; // the objects are processed but can not be deleted
390 anOpMgr->startOperation(anOpAction);
392 static const Events_ID anVisualEvent = Events_Loop::eventByName(EVENT_VISUAL_ATTRIBUTES);
393 if (anObjects.size() > 0) {
394 QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
395 for (; anIt != aLast; anIt++) {
396 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
397 if (aFeature.get() != NULL) {
398 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
399 std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
400 if (aSketchFeature.get() != NULL) {
401 std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
403 std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr =
404 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(
405 aSketchFeature->data()->attribute(anAttribute));
407 anAuxiliaryAttr->setValue(isChecked);
408 ModelAPI_EventCreator::get()->sendUpdated(aSketchFeature, anVisualEvent);
413 if (isUseTransaction)
414 anOpMgr->commitOperation();
416 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
417 Events_Loop::loop()->flush(anVisualEvent);
420 bool PartSet_MenuMgr::canSetAuxiliary(bool& theValue) const
422 bool anEnabled = false;
423 ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
425 CompositeFeaturePtr aSketch = myModule->sketchMgr()->activeSketch();
426 bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
427 myModule->sketchMgr()->isNestedSketchOperation(anOperation);
431 QObjectPtrList anObjects;
432 // 1. change auxiliary type of a created feature
433 if (myModule->sketchMgr()->isNestedCreateOperation(anOperation, aSketch) &&
434 PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
435 ModuleBase_OperationFeature* aFOperation =
436 dynamic_cast<ModuleBase_OperationFeature*>(anOperation);
438 anObjects.append(aFOperation->feature());
441 /// The operation should not be aborted here, because the method does not changed
442 /// the auxilliary state, but checks the possibility to perform this
443 ///if (myModule->sketchMgr()->isNestedSketchOperation(anOperation))
444 /// anOperation->abort();
445 // 2. change auxiliary type of selected sketch entities
446 ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
447 anObjects = aSelection->selectedPresentations();
450 bool isNotAuxiliaryFound = false;
451 if (anObjects.size() > 0) {
452 QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
453 for (; anIt != aLast && !isNotAuxiliaryFound; anIt++) {
454 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
455 if ((aFeature.get() != NULL) && PartSet_SketcherMgr::isEntity(aFeature->getKind())) {
457 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
458 std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
459 if (aSketchFeature.get() != NULL) {
460 std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
462 std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr =
463 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(
464 aSketchFeature->data()->attribute(anAttribute));
466 isNotAuxiliaryFound = !anAuxiliaryAttr->value();
471 theValue = anObjects.size() && !isNotAuxiliaryFound;
475 void PartSet_MenuMgr::onActivatePart(bool)
477 if (myModule->workshop()->currentOperation())
479 QObjectPtrList aObjects = myModule->workshop()->selection()->selectedObjects();
480 if (aObjects.size() > 0) {
481 ObjectPtr aObj = aObjects.first();
482 ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
484 FeaturePtr aPartFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
485 if (aPartFeature.get() && (aPartFeature->getKind() == PartSetPlugin_Part::ID())) {
486 aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aPartFeature->firstResult());
491 myModule->workshop()->updateCommandStatus();
496 void PartSet_MenuMgr::activatePart(ResultPartPtr thePart) const
498 bool isFirstLoad = !thePart->partDoc().get();
499 ModuleBase_Tools::blockUpdateViewer(true);
502 XGUI_Workshop* aWorkshop = myModule->getWorkshop();
503 XGUI_ObjectsBrowser* aObjBrowser = aWorkshop->objectBrowser();
504 ModuleBase_Tools::setDisplaying(thePart);
505 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
506 aObjBrowser->onSelectionChanged();
507 DocumentPtr aDoc = thePart->partDoc();
508 std::list<bool> aStates;
509 aDoc->restoreNodesState(aStates);
510 aObjBrowser->setStateForDoc(aDoc, aStates);
512 ModuleBase_Tools::blockUpdateViewer(false);
515 void PartSet_MenuMgr::onActivateAllParts()
517 SessionPtr aMgr = ModelAPI_Session::get();
518 if (aMgr->isOperation())
521 DocumentPtr aDoc = aMgr->moduleDocument();
522 int aNbParts = aDoc->size(ModelAPI_ResultPart::group());
523 QList<ResultPartPtr> aPartsToLoad;
524 for (int i = 0; i < aNbParts; i++) {
525 ObjectPtr aObj = aDoc->object(ModelAPI_ResultPart::group(), i);
526 ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
527 if (!aPartRes->partDoc().get())
528 aPartsToLoad.append(aPartRes);
530 if (!aPartsToLoad.isEmpty()) {
531 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
532 aMgr->startOperation("All Parts loading");
533 foreach(ResultPartPtr aPartRes, aPartsToLoad) {
534 aPartRes->loadPart();
536 aMgr->finishOperation();
538 XGUI_Workshop* aWorkshop = myModule->getWorkshop();
539 XGUI_ObjectsBrowser* aObjBrowser = aWorkshop->objectBrowser();
540 aObjBrowser->update();
541 aWorkshop->viewer()->update();
542 aWorkshop->updateCommandStatus();
543 QApplication::restoreOverrideCursor();
547 void PartSet_MenuMgr::onActivatePartSet(bool)
549 if (myModule->workshop()->currentOperation())
554 void PartSet_MenuMgr::activatePartSet() const
556 SessionPtr aMgr = ModelAPI_Session::get();
557 bool isNewTransaction = !aMgr->isOperation();
558 // activation may cause changes in current features in document, so it must be in transaction
559 if (isNewTransaction)
560 aMgr->startOperation("Activation");
561 aMgr->setActiveDocument(aMgr->moduleDocument());
562 if (isNewTransaction)
563 aMgr->finishOperation();
565 myModule->workshop()->updateCommandStatus();
566 myModule->workshop()->viewer()->update();
569 void PartSet_MenuMgr::grantedOperationIds(ModuleBase_Operation* theOperation,
570 QStringList& theIds) const
572 if (PartSet_SketcherMgr::isSketchOperation(theOperation)) {
573 theIds.append(tr("Detach"));
574 theIds.append(tr("Auxiliary"));
578 void PartSet_MenuMgr::onEdit(bool)
580 QObjectPtrList aObjects = myModule->workshop()->selection()->selectedObjects();
582 foreach(ObjectPtr aObj, aObjects) {
583 aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
587 if (aFeature.get() == NULL) {
588 ResultParameterPtr aParam;
589 foreach(ObjectPtr aObj, aObjects) {
590 aParam = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aObj);
594 if (aParam.get() != NULL)
595 aFeature = ModelAPI_Feature::feature(aParam);
597 if (aFeature.get() != NULL)
598 myModule->editFeature(aFeature);
601 bool PartSet_MenuMgr::eventFilter(QObject* theObj, QEvent* theEvent)
603 if (theEvent->type() == QEvent::MouseButtonDblClick) {
604 SessionPtr aMgr = ModelAPI_Session::get();
605 if (aMgr->activeDocument() != aMgr->moduleDocument())
608 return QObject::eventFilter(theObj, theEvent);