Salome HOME
Wrap tool tip text by word in Widget action
[modules/shaper.git] / src / ModuleBase / ModuleBase_Tools.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        ModuleBase_Tools.cpp
4 // Created:     11 July 2014
5 // Author:      Vitaly Smetannikov
6
7 #include "ModuleBase_Tools.h"
8
9 #include <ModuleBase_ParamIntSpinBox.h>
10 #include <ModuleBase_ParamSpinBox.h>
11 #include <ModuleBase_WidgetFactory.h>
12 #include <ModuleBase_IWorkshop.h>
13 #include <ModuleBase_IModule.h>
14
15 #include <ModelAPI_Attribute.h>
16 #include <ModelAPI_AttributeRefAttr.h>
17 #include <ModelAPI_AttributeReference.h>
18 #include <ModelAPI_AttributeSelection.h>
19 #include <ModelAPI_AttributeSelectionList.h>
20 #include <ModelAPI_AttributeRefList.h>
21 #include <ModelAPI_AttributeRefAttrList.h>
22 #include <Events_Loop.h>
23
24 #include <ModelAPI_Data.h>
25 #include <ModelAPI_Result.h>
26 #include <ModelAPI_ResultCompSolid.h>
27 #include <ModelAPI_ResultParameter.h>
28 #include <ModelAPI_Tools.h>
29 #include <ModelAPI_Session.h>
30 #include <ModelAPI_Events.h>
31
32 #include <TopoDS_Iterator.hxx>
33
34 #include <GeomDataAPI_Point2D.h>
35 #include <Events_Error.h>
36
37 #include <Config_PropManager.h>
38
39 #include <QWidget>
40 #include <QLayout>
41 #include <QPainter>
42 #include <QBitmap>
43 #include <QDoubleSpinBox>
44 #include <QGraphicsDropShadowEffect>
45 #include <QColor>
46 #include <QApplication>
47
48 #include <sstream>
49 #include <string>
50
51 const double tolerance = 1e-7;
52
53 //#define DEBUG_ACTIVATE_WINDOW
54 //#define DEBUG_SET_FOCUS
55
56 namespace ModuleBase_Tools {
57
58 //******************************************************************
59
60 //******************************************************************
61
62 void adjustMargins(QWidget* theWidget)
63 {
64   if(!theWidget)
65     return;
66   adjustMargins(theWidget->layout());
67 }
68
69 void adjustMargins(QLayout* theLayout)
70 {
71   if(!theLayout)
72     return;
73   theLayout->setContentsMargins(2, 5, 2, 5);
74   theLayout->setSpacing(4);
75 }
76
77 void zeroMargins(QWidget* theWidget)
78 {
79   if(!theWidget)
80     return;
81   zeroMargins(theWidget->layout());
82 }
83
84 void zeroMargins(QLayout* theLayout)
85 {
86   if(!theLayout)
87     return;
88   theLayout->setContentsMargins(0, 0, 0, 0);
89   theLayout->setSpacing(5);
90 }
91
92 void activateWindow(QWidget* theWidget, const QString& theInfo)
93 {
94   theWidget->activateWindow();
95
96 #ifdef DEBUG_ACTIVATE_WINDOW
97   qDebug(QString("activateWindow: %1").arg(theInfo).toStdString().c_str());
98 #endif
99 }
100
101 void setFocus(QWidget* theWidget, const QString& theInfo)
102 {
103   theWidget->setFocus();
104
105 #ifdef DEBUG_SET_FOCUS
106   qDebug(QString("setFocus: %1").arg(theInfo).toStdString().c_str());
107 #endif
108 }
109
110 void setShadowEffect(QWidget* theWidget, const bool isSetEffect)
111 {
112   if (isSetEffect) {
113     QGraphicsDropShadowEffect* aGlowEffect = new QGraphicsDropShadowEffect();
114     aGlowEffect->setOffset(.0);
115     aGlowEffect->setBlurRadius(10.0);
116     aGlowEffect->setColor(QColor(0, 170, 255)); // Light-blue color, #00AAFF
117     theWidget->setGraphicsEffect(aGlowEffect);
118   }
119   else {
120     QGraphicsEffect* anEffect = theWidget->graphicsEffect();
121     if(anEffect)
122     anEffect->deleteLater();
123     theWidget->setGraphicsEffect(NULL);
124   }
125 }
126
127 QPixmap composite(const QString& theAdditionalIcon, const QString& theIcon)
128 {
129   QImage anIcon(theIcon);
130   QImage anAditional(theAdditionalIcon);
131
132   if (anIcon.isNull())
133     return QPixmap();
134
135   int anAddWidth = anAditional.width();
136   int anAddHeight = anAditional.height();
137
138   int aWidth = anIcon.width();
139   int aHeight = anIcon.height();
140
141   int aStartWidthPos = aWidth - anAddWidth - 1;
142   int aStartHeightPos = aHeight - anAddHeight - 1;
143
144   for (int i = 0; i < anAddWidth && i + aStartWidthPos < aWidth; i++)
145   {
146     for (int j = 0; j < anAddHeight && j + aStartHeightPos < aHeight; j++)
147     {
148       if (qAlpha(anAditional.pixel(i, j)) > 0)
149         anIcon.setPixel(i + aStartWidthPos, j + aStartHeightPos, anAditional.pixel(i, j));
150     }
151   }
152   return QPixmap::fromImage(anIcon);
153 }
154
155 QPixmap lighter(const QString& theIcon, const int theLighterValue)
156 {
157   QImage anIcon(theIcon);
158   if (anIcon.isNull())
159     return QPixmap();
160
161   QImage aResult(theIcon);
162   for (int i = 0; i < anIcon.width(); i++)
163   {
164     for (int j = 0; j < anIcon.height(); j++)
165     {
166       QRgb anRgb = anIcon.pixel(i, j);
167       QColor aPixelColor(qRed(anRgb), qGreen(anRgb), qBlue(anRgb),
168                          qAlpha(aResult.pixel(i, j)));
169
170       QColor aLighterColor = aPixelColor.lighter(theLighterValue);
171       aResult.setPixel(i, j, qRgba(aLighterColor.red(), aLighterColor.green(),
172                                     aLighterColor.blue(), aLighterColor.alpha()));
173     }
174   }
175   return QPixmap::fromImage(aResult);
176 }
177
178 void setSpinText(ModuleBase_ParamSpinBox* theSpin, const QString& theText)
179 {
180   if (theSpin->text() == theText) 
181     return;
182   // In order to avoid extra text setting because it will
183   // reset cursor position in control
184   bool isBlocked = theSpin->blockSignals(true);
185   theSpin->setText(theText);
186   theSpin->blockSignals(isBlocked);
187 }
188
189 void setSpinValue(QDoubleSpinBox* theSpin, double theValue)
190 {
191   if (fabs(theSpin->value() - theValue) < tolerance)
192     return;
193   bool isBlocked = theSpin->blockSignals(true);
194   theSpin->setValue(theValue);
195   theSpin->blockSignals(isBlocked);
196 }
197
198 void setSpinValue(ModuleBase_ParamSpinBox* theSpin, double theValue)
199 {
200   if (fabs(theSpin->value() - theValue) < tolerance)
201     return;
202   bool isBlocked = theSpin->blockSignals(true);
203   theSpin->setValue(theValue);
204   theSpin->blockSignals(isBlocked);
205 }
206
207 void setSpinText(ModuleBase_ParamIntSpinBox* theSpin, const QString& theText)
208 {
209   // In order to avoid extra text setting because it will
210   // reset cursor position in control
211   if (theSpin->text() == theText)
212     return;
213   bool isBlocked = theSpin->blockSignals(true);
214   theSpin->setText(theText);
215   theSpin->blockSignals(isBlocked);
216 }
217
218 void setSpinValue(ModuleBase_ParamIntSpinBox* theSpin, int theValue)
219 {
220   if (theSpin->value() == theValue)
221     return;
222   bool isBlocked = theSpin->blockSignals(true);
223   theSpin->setValue(theValue);
224   theSpin->blockSignals(isBlocked);
225 }
226
227 QString objectInfo(const ObjectPtr& theObj, const bool isUseAttributesInfo)
228 {
229   QString aFeatureStr = "feature";
230   if (!theObj.get())
231     return aFeatureStr;
232
233   std::ostringstream aPtrStr;
234   aPtrStr << "[" << theObj.get() << "]";
235
236   ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(theObj);
237   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
238   if(aRes.get()) {
239     aFeatureStr.append(QString("(result%1)").arg(aPtrStr.str().c_str()).toStdString() .c_str());
240     if (aRes->isDisabled())
241       aFeatureStr.append("[disabled]");
242     if (aRes->isConcealed())
243       aFeatureStr.append("[concealed]");
244     if (ModelAPI_Tools::hasSubResults(aRes))
245       aFeatureStr.append("[hasSubResults]");
246
247     aFeature = ModelAPI_Feature::feature(aRes);
248   }
249   else
250     aFeatureStr.append(aPtrStr.str().c_str());
251
252   if (aFeature.get()) {
253     aFeatureStr.append(QString(": %1").arg(aFeature->getKind().c_str()).toStdString().c_str());
254     if (aFeature->data()->isValid()) {
255       aFeatureStr.append(QString(", name=%1").arg(aFeature->data()->name().c_str()).toStdString()
256                                                                                        .c_str());
257     }
258     if (isUseAttributesInfo) {
259       std::list<AttributePtr> anAttrs = aFeature->data()->attributes("");
260       std::list<AttributePtr>::const_iterator anIt = anAttrs.begin(), aLast = anAttrs.end();
261       QStringList aValues;
262       for(; anIt != aLast; anIt++) {
263         AttributePtr anAttr = *anIt;
264         QString aValue = "not defined";
265         std::string aType = anAttr->attributeType();
266         if (aType == GeomDataAPI_Point2D::typeId()) {
267           std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
268                                                                                          anAttr);
269           if (aPoint.get())
270             aValue = QString("(%1, %2)").arg(aPoint->x()).arg(aPoint->y());
271         }
272         else if (aType == ModelAPI_AttributeRefAttr::typeId()) {
273         }
274
275         aValues.push_back(QString("%1: %2").arg(anAttr->id().c_str()).arg(aValue).toStdString().c_str());
276       }
277       if (!aValues.empty())
278         aFeatureStr.append(QString(", attributes: %1").arg(aValues.join(", ").toStdString().c_str()));
279     }
280   }
281
282   return aFeatureStr;
283 }
284
285 typedef QMap<QString, TopAbs_ShapeEnum> ShapeTypes;
286 static ShapeTypes MyShapeTypes;
287
288 TopAbs_ShapeEnum shapeType(const QString& theType)
289 {
290   if (MyShapeTypes.count() == 0) {
291     MyShapeTypes["face"] = TopAbs_FACE;
292     MyShapeTypes["faces"] = TopAbs_FACE;
293     MyShapeTypes["vertex"] = TopAbs_VERTEX;
294     MyShapeTypes["vertices"] = TopAbs_VERTEX;
295     MyShapeTypes["wire"] = TopAbs_WIRE;
296     MyShapeTypes["edge"] = TopAbs_EDGE;
297     MyShapeTypes["edges"] = TopAbs_EDGE;
298     MyShapeTypes["shell"] = TopAbs_SHELL;
299     MyShapeTypes["solid"] = TopAbs_SOLID;
300     MyShapeTypes["solids"] = TopAbs_SOLID;
301     MyShapeTypes["objects"] = TopAbs_SHAPE;
302   }
303   QString aType = theType.toLower();
304   if (MyShapeTypes.contains(aType))
305     return MyShapeTypes[aType];
306   Events_Error::send("Shape type defined in XML is not implemented!");
307   return TopAbs_SHAPE;
308 }
309
310 void checkObjects(const QObjectPtrList& theObjects, bool& hasResult, bool& hasFeature,
311                   bool& hasParameter, bool& hasCompositeOwner)
312 {
313   hasResult = false;
314   hasFeature = false;
315   hasParameter = false;
316   hasCompositeOwner = false;
317   foreach(ObjectPtr aObj, theObjects) {
318     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
319     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
320     ResultParameterPtr aConstruction = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aResult);
321
322     hasResult |= (aResult.get() != NULL);
323     hasFeature |= (aFeature.get() != NULL);
324     hasParameter |= (aConstruction.get() != NULL);
325     if (hasFeature) 
326       hasCompositeOwner |= (ModelAPI_Tools::compositeOwner(aFeature) != NULL);
327     if (hasFeature && hasResult  && hasParameter && hasCompositeOwner)
328       break;
329   }
330 }
331
332 void setDefaultDeviationCoefficient(const TopoDS_Shape& theShape,
333                                     const Handle(Prs3d_Drawer)& theDrawer)
334 {
335   if (theShape.IsNull())
336     return;
337   TopAbs_ShapeEnum aType = theShape.ShapeType();
338   if ((aType == TopAbs_EDGE) || (aType == TopAbs_WIRE)) 
339     theDrawer->SetDeviationCoefficient(1.e-4);
340 }
341
342 Quantity_Color color(const std::string& theSection,
343                      const std::string& theName,
344                      const std::string& theDefault)
345 {
346   std::vector<int> aColor = Config_PropManager::color(theSection, theName, theDefault);
347   return Quantity_Color(aColor[0] / 255., aColor[1] / 255., aColor[2] / 255., Quantity_TOC_RGB);
348 }
349
350 ObjectPtr getObject(const AttributePtr& theAttribute)
351 {
352   ObjectPtr anObject;
353   std::string anAttrType = theAttribute->attributeType();
354   if (anAttrType == ModelAPI_AttributeRefAttr::typeId()) {
355     AttributeRefAttrPtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
356     if (anAttr != NULL && anAttr->isObject())
357       anObject = anAttr->object();
358   }
359   if (anAttrType == ModelAPI_AttributeSelection::typeId()) {
360     AttributeSelectionPtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
361     if (anAttr != NULL)
362       anObject = anAttr->context();
363   }
364   if (anAttrType == ModelAPI_AttributeReference::typeId()) {
365     AttributeReferencePtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
366     if (anAttr.get() != NULL)
367       anObject = anAttr->value();
368   }
369   return anObject;
370 }
371
372 TopAbs_ShapeEnum getCompoundSubType(const TopoDS_Shape& theShape)
373 {
374   TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
375
376   // for compounds check sub-shapes: it may be compound of needed type:
377   // Booleans may produce compounds of Solids
378   if (aShapeType == TopAbs_COMPOUND) {
379     for(TopoDS_Iterator aSubs(theShape); aSubs.More(); aSubs.Next()) {
380       if (!aSubs.Value().IsNull()) {
381         TopAbs_ShapeEnum aSubType = aSubs.Value().ShapeType();
382         if (aSubType == TopAbs_COMPOUND) { // compound of compound(s)
383           aShapeType = TopAbs_COMPOUND;
384           break;
385         }
386         if (aShapeType == TopAbs_COMPOUND) {
387           aShapeType = aSubType;
388         } else if (aShapeType != aSubType) { // compound of shapes of different types
389           aShapeType = TopAbs_COMPOUND;
390           break;
391         }
392       }
393     }
394   }
395   return aShapeType;
396 }
397
398 void getParameters(QStringList& theParameters)
399 {
400   theParameters.clear();
401
402   SessionPtr aSession = ModelAPI_Session::get();
403   std::list<DocumentPtr> aDocList;
404   DocumentPtr anActiveDocument = aSession->activeDocument();
405   DocumentPtr aRootDocument = aSession->moduleDocument();
406   aDocList.push_back(anActiveDocument);
407   if (anActiveDocument != aRootDocument) {
408     aDocList.push_back(aRootDocument);
409   }
410   std::string aGroupId = ModelAPI_ResultParameter::group();
411   for(std::list<DocumentPtr>::const_iterator it = aDocList.begin(); it != aDocList.end(); ++it) {
412     DocumentPtr aDocument = *it;
413     int aSize = aDocument->size(aGroupId);
414     for (int i = 0; i < aSize; i++) {
415       ObjectPtr anObject = aDocument->object(aGroupId, i);
416       std::string aParameterName = anObject->data()->name();
417       theParameters.append(aParameterName.c_str());
418     }
419   }
420 }
421
422 std::string findGreedAttribute(ModuleBase_IWorkshop* theWorkshop, const FeaturePtr& theFeature)
423 {
424   std::string anAttributeId;
425
426   std::string aXmlCfg, aDescription;
427   theWorkshop->module()->getXMLRepresentation(theFeature->getKind(), aXmlCfg, aDescription);
428
429   ModuleBase_WidgetFactory aFactory(aXmlCfg, theWorkshop);
430   std::string anAttributeTitle;
431   aFactory.getGreedAttribute(anAttributeId);
432
433   return anAttributeId;
434 }
435
436 void setObject(const AttributePtr& theAttribute, const ObjectPtr& theObject,
437                const GeomShapePtr& theShape, ModuleBase_IWorkshop* theWorkshop,
438                const bool theTemporarily)
439 {
440   if (!theAttribute.get())
441     return;
442
443   std::string aType = theAttribute->attributeType();
444   if (aType == ModelAPI_AttributeReference::typeId()) {
445     AttributeReferencePtr aRef = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
446     ObjectPtr aObject = aRef->value();
447     if (!(aObject && aObject->isSame(theObject))) {
448       aRef->setValue(theObject);
449     }
450   } else if (aType == ModelAPI_AttributeRefAttr::typeId()) {
451     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
452
453     AttributePtr anAttribute = theWorkshop->module()->findAttribute(theObject, theShape);
454     if (anAttribute.get())
455       aRefAttr->setAttr(anAttribute);
456     else {
457       ObjectPtr aObject = aRefAttr->object();
458       if (!(aObject && aObject->isSame(theObject))) {
459         aRefAttr->setObject(theObject);
460       }
461     }
462   } else if (aType == ModelAPI_AttributeSelection::typeId()) {
463     AttributeSelectionPtr aSelectAttr =
464                              std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
465     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
466     if (aSelectAttr.get() != NULL) {
467       aSelectAttr->setValue(aResult, theShape, theTemporarily);
468     }
469   }
470   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
471     AttributeSelectionListPtr aSelectionListAttr =
472                          std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
473     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
474     if (!aSelectionListAttr->isInList(aResult, theShape, theTemporarily))
475       aSelectionListAttr->append(aResult, theShape, theTemporarily);
476   }
477   else if (aType == ModelAPI_AttributeRefList::typeId()) {
478     AttributeRefListPtr aRefListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
479     if (!aRefListAttr->isInList(theObject))
480       aRefListAttr->append(theObject);
481   }
482   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
483     AttributeRefAttrListPtr aRefAttrListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theAttribute);
484     AttributePtr anAttribute = theWorkshop->module()->findAttribute(theObject, theShape);
485
486     if (anAttribute.get()) {
487       if (!aRefAttrListAttr->isInList(anAttribute))
488         aRefAttrListAttr->append(anAttribute);
489     }
490     else {
491       if (!aRefAttrListAttr->isInList(theObject))
492         aRefAttrListAttr->append(theObject);
493     }
494   }
495 }
496
497 GeomShapePtr getShape(const AttributePtr& theAttribute, ModuleBase_IWorkshop* theWorkshop)
498 {
499   GeomShapePtr aShape;
500   if (!theAttribute.get())
501     return aShape;
502
503   std::string aType = theAttribute->attributeType();
504   if (aType == ModelAPI_AttributeReference::typeId()) {
505   } else if (aType == ModelAPI_AttributeRefAttr::typeId()) {
506     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
507     if (aRefAttr.get() && !aRefAttr->isObject()) {
508       AttributePtr anAttribute = aRefAttr->attr();
509       aShape = theWorkshop->module()->findShape(anAttribute);
510     }
511   } else if (aType == ModelAPI_AttributeSelection::typeId()) {
512     AttributeSelectionPtr aSelectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>
513                                                                                  (theAttribute);
514     aShape = aSelectAttr->value();
515   }
516   return aShape;
517 }
518
519 void flushUpdated(ObjectPtr theObject)
520 {
521   blockUpdateViewer(true);
522
523   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
524
525   blockUpdateViewer(false);
526 }
527
528 void blockUpdateViewer(const bool theValue)
529 {
530   // the viewer update should be blocked in order to avoid the temporary feature content
531   // when the solver processes the feature, the redisplay message can be flushed
532   // what caused the display in the viewer preliminary states of object
533   // e.g. fillet feature, angle value change
534   std::shared_ptr<Events_Message> aMsg;
535   if (theValue) {
536     aMsg = std::shared_ptr<Events_Message>(
537         new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)));
538   }
539   else {
540     // the viewer update should be unblocked
541     aMsg = std::shared_ptr<Events_Message>(
542         new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)));
543   }
544   Events_Loop::loop()->send(aMsg);
545 }
546
547 QString ModuleBase_Tools::wrapTextByWords(const QString& theValue, QWidget* theWidget,
548                                           int theMaxLineInPixels)
549 {
550   static QFontMetrics tfm(theWidget ? theWidget->font() : QApplication::font());
551   static qreal phi = 2.618;
552
553   QRect aBounds = tfm.boundingRect(theValue);
554   if(aBounds.width() <= theMaxLineInPixels)
555     return theValue;
556
557   qreal s = aBounds.width() * aBounds.height();
558   qreal aGoldWidth = sqrt(s*phi);
559
560   QStringList aWords = theValue.split(" ", QString::SkipEmptyParts);
561   QStringList aLines;
562   int n = aWords.count();
563   QString aLine;
564   for (int i = 0; i < n; i++) {
565     QString aLineExt = aLine + " " + aWords[i];
566     qreal anWidthNonExt = tfm.boundingRect(aLine).width();
567     qreal anWidthExt = tfm.boundingRect(aLineExt).width();
568     qreal aDeltaNonExt = fabs(anWidthNonExt-aGoldWidth);
569     qreal aDeltaExt    = fabs(anWidthExt-aGoldWidth);
570     if(aDeltaNonExt < aDeltaExt) {
571       // new line
572       aLines.append(aLine);
573       aLine = aWords[i];
574     }
575     else
576       aLine = aLineExt;
577   }
578
579   if(!aLine.isEmpty())
580     aLines.append(aLine);
581
582   QString aResult = aLines.join("\n");
583   return aResult;
584 }
585
586 } // namespace ModuleBase_Tools
587
588