Salome HOME
Correction for selector(greed) widgets. Using preselection.
[modules/shaper.git] / src / ParametersPlugin / ParametersPlugin_EvalListener.cpp
1 /*
2  * ParametersPlugin_EvalListener.cpp
3  *
4  *  Created on: Apr 28, 2015
5  *      Author: sbh
6  */
7
8 #include <pyconfig.h>
9
10 #include <ParametersPlugin_EvalListener.h>
11 #include <ParametersPlugin_Parameter.h>
12 #include <ParametersPlugin_PyInterp.h>
13
14 #include <Events_Error.h>
15
16 #include <ModelAPI_AttributeDouble.h>
17 #include <ModelAPI_AttributeInteger.h>
18 #include <ModelAPI_AttributeRefList.h>
19 #include <ModelAPI_AttributeString.h>
20 #include <ModelAPI_AttributeValidator.h>
21 #include <ModelAPI_Document.h>
22 #include <ModelAPI_Events.h>
23 #include <ModelAPI_Session.h>
24 #include <ModelAPI_Tools.h>
25
26 #include <GeomDataAPI_Point.h>
27 #include <GeomDataAPI_Point2D.h>
28
29 #include <QMessageBox>
30
31 #include <string>
32 #include <set>
33 #include <sstream>
34
35 //------------------------------------------------------------------------------
36 // Tools
37
38 std::string toStdString(double theValue)
39 {
40   std::ostringstream sstream;
41   sstream << theValue;
42   size_t aPos = sstream.str().find(".");
43   std::string aPnt = "";
44   if (aPos == std::string::npos)
45     aPnt = ".";
46   return sstream.str() + aPnt;
47 }
48
49 std::set<std::string> toSet(const std::list<std::string>& theContainer)
50 {
51   return std::set<std::string>(theContainer.begin(), theContainer.end());
52 }
53
54 //------------------------------------------------------------------------------
55
56 ParametersPlugin_EvalListener::ParametersPlugin_EvalListener()
57 {
58   Events_Loop* aLoop = Events_Loop::loop();
59
60   Events_ID anEvents_IDs[] = {
61       ModelAPI_AttributeEvalMessage::eventId(),
62       ModelAPI_ObjectRenamedMessage::eventId(),
63       ModelAPI_ReplaceParameterMessage::eventId()
64   };
65
66   for (int i = 0; i < sizeof(anEvents_IDs)/sizeof(anEvents_IDs[0]); ++i)
67     aLoop->registerListener(this, anEvents_IDs[i], NULL, true);
68
69   myInterp = std::shared_ptr<ParametersPlugin_PyInterp>(new ParametersPlugin_PyInterp());
70   myInterp->initialize();
71 }
72
73 ParametersPlugin_EvalListener::~ParametersPlugin_EvalListener()
74 {
75 }
76
77 void ParametersPlugin_EvalListener::processEvent(
78     const std::shared_ptr<Events_Message>& theMessage)
79 {
80   if (!theMessage.get())
81     return;
82
83   const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId();
84   const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId();
85   const Events_ID kReplaceParameterEvent = ModelAPI_ReplaceParameterMessage::eventId();
86
87   if (theMessage->eventID() == kEvaluationEvent) {
88     processEvaluationEvent(theMessage);
89   } else if (theMessage->eventID() == kObjectRenamedEvent) {
90     processObjectRenamedEvent(theMessage);
91   } else if (theMessage->eventID() == kReplaceParameterEvent) {
92     processReplaceParameterEvent(theMessage);
93   } else {
94     Events_Error::send(std::string("ParametersPlugin python interpreter, unhandled message caught: ")
95                        + theMessage->eventID().eventText());
96   }
97 }
98
99 double ParametersPlugin_EvalListener::evaluate(FeaturePtr theParameter,
100   const std::string& theExpression, std::string& theError)
101 {
102   std::list<std::string> anExprParams = myInterp->compile(theExpression);
103   // find expression's params in the model
104   std::list<std::string> aContext;
105   std::list<std::string>::iterator it = anExprParams.begin();
106   for ( ; it != anExprParams.end(); it++) {
107     double aValue;
108     ResultParameterPtr aParamRes;
109     // If variable does not exist python interpreter will generate an error. It is OK.
110     // But due to the issue 1479 it should not check the history position of parameters relatively
111     // to feature that contains expression
112     if (!ModelAPI_Tools::findVariable(/*theParameter*/ FeaturePtr(), 
113       *it, aValue, aParamRes, theParameter->document()))
114       continue;
115
116     aContext.push_back(*it + "=" + toStdString(aValue));
117   }
118   myInterp->extendLocalContext(aContext);
119   double result = myInterp->evaluate(theExpression, theError);
120   myInterp->clearLocalContext();
121   return result;
122 }
123
124 void ParametersPlugin_EvalListener::processEvaluationEvent(
125     const std::shared_ptr<Events_Message>& theMessage)
126 {
127   std::shared_ptr<ModelAPI_AttributeEvalMessage> aMessage =
128       std::dynamic_pointer_cast<ModelAPI_AttributeEvalMessage>(theMessage);
129
130   FeaturePtr aParamFeature = 
131     std::dynamic_pointer_cast<ModelAPI_Feature>(aMessage->attribute()->owner());
132   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeInteger::typeId()) {
133     AttributeIntegerPtr anAttribute =
134         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(aMessage->attribute());
135     std::string anError;
136     int aValue = (int)evaluate(aParamFeature, anAttribute->text(), anError);
137     bool isValid = anError.empty();
138     if (isValid)
139       anAttribute->setCalculatedValue(aValue);
140     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
141     anAttribute->setExpressionInvalid(!isValid);
142     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
143   } else
144   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) {
145     AttributeDoublePtr anAttribute =
146         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aMessage->attribute());
147     std::string anError;
148     double aValue = evaluate(aParamFeature, anAttribute->text(), anError);
149     bool isValid = anError.empty();
150     if (isValid)
151       anAttribute->setCalculatedValue(aValue);
152     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
153     anAttribute->setExpressionInvalid(!isValid);
154     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
155   } else
156   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) {
157     AttributePointPtr anAttribute =
158         std::dynamic_pointer_cast<GeomDataAPI_Point>(aMessage->attribute());
159     std::string aText[] = {
160       anAttribute->textX(),
161       anAttribute->textY(),
162       anAttribute->textZ()
163     };
164     double aCalculatedValue[] = {
165       anAttribute->x(),
166       anAttribute->y(),
167       anAttribute->z()
168     };
169     for (int i = 0; i < 3; ++i) {
170       std::string anError;
171       double aValue = evaluate(aParamFeature, aText[i], anError);
172       bool isValid = anError.empty();
173       if (isValid) aCalculatedValue[i] = aValue;
174       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
175       anAttribute->setExpressionInvalid(i, !isValid);
176       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
177     }
178     anAttribute->setCalculatedValue(aCalculatedValue[0],
179                                     aCalculatedValue[1],
180                                     aCalculatedValue[2]);
181   } else
182   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) {
183     AttributePoint2DPtr anAttribute =
184         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMessage->attribute());
185     std::string aText[] = {
186       anAttribute->textX(),
187       anAttribute->textY()
188     };
189     double aCalculatedValue[] = {
190       anAttribute->x(),
191       anAttribute->y()
192     };
193     for (int i = 0; i < 2; ++i) {
194       std::string anError;
195       double aValue = evaluate(aParamFeature, aText[i], anError);
196       bool isValid = anError.empty();
197       if (isValid) aCalculatedValue[i] = aValue;
198       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
199       anAttribute->setExpressionInvalid(i, !isValid);
200       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
201     }
202     anAttribute->setCalculatedValue(aCalculatedValue[0],
203                                     aCalculatedValue[1]);
204   }
205 }
206
207 std::string ParametersPlugin_EvalListener::renameInPythonExpression(
208     const std::string& theExpression,
209     const std::string& theOldName,
210     const std::string& theNewName)
211 {
212   std::string anExpressionString = theExpression;
213
214   std::list<std::pair<int, int> > aPositions =
215       myInterp->positions(anExpressionString, theOldName);
216
217   if (aPositions.empty())
218     return anExpressionString;
219
220   std::map<int, std::list<int> > aLines;
221   std::list<std::pair<int, int> >::const_iterator it = aPositions.begin();
222   for (; it != aPositions.end(); ++it)
223     aLines[it->first].push_back(it->second);
224
225   // Start renaming from the end to keep indexes if theNewName is longer then theOldName
226   std::map<int, std::list<int> >::const_reverse_iterator ritLine = aLines.rbegin();
227   for (; ritLine != aLines.rend(); ++ritLine) {
228     // Calculate the start of the line (find the aLineNo occurrence of "\n" )
229     int aLineNo = ritLine->first - 1;
230     size_t aLineStart = 0;
231     for (int i = 0; i < aLineNo; ++i)
232       aLineStart = anExpressionString.find("\n", aLineStart) + 1;
233
234     const std::list<int>& aColOffsets = ritLine->second;
235     std::list<int>::const_reverse_iterator ritOffset = aColOffsets.rbegin();
236     for (; ritOffset != aColOffsets.rend(); ++ritOffset) {
237       int anOffset = *ritOffset;
238       anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName);
239     }
240   }
241
242   return anExpressionString;
243 }
244
245 void ParametersPlugin_EvalListener::renameInParameter(
246     std::shared_ptr<ParametersPlugin_Parameter> theParameter,
247     const std::string& theOldName,
248     const std::string& theNewName)
249 {
250   std::shared_ptr<ModelAPI_AttributeString> anExpressionAttribute =
251       theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID());
252
253   std::string anExpressionString = anExpressionAttribute->value();
254   anExpressionString = renameInPythonExpression(anExpressionString,
255                                                 theOldName,
256                                                 theNewName);
257   // Issue #588. No need for reevaluating expression. 
258   // Moreover, current history may not contain necessary parameters.
259   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(true);
260   anExpressionAttribute->setValue(anExpressionString);
261   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(false);
262 }
263
264 void ParametersPlugin_EvalListener::renameInAttribute(
265     std::shared_ptr<ModelAPI_Attribute> theAttribute,
266     const std::string& theOldName,
267     const std::string& theNewName)
268 {
269   if (theAttribute->attributeType() == ModelAPI_AttributeInteger::typeId()) {
270     AttributeIntegerPtr anAttribute =
271         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
272     std::string anExpressionString = anAttribute->text();
273     anExpressionString = renameInPythonExpression(anExpressionString,
274                                                   theOldName, theNewName);
275     anAttribute->setText(anExpressionString);
276   } else
277   if (theAttribute->attributeType() == ModelAPI_AttributeDouble::typeId()) {
278     AttributeDoublePtr anAttribute =
279         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
280     std::string anExpressionString = anAttribute->text();
281     anExpressionString = renameInPythonExpression(anExpressionString,
282                                                   theOldName, theNewName);
283     anAttribute->setText(anExpressionString);
284   } else
285   if (theAttribute->attributeType() == GeomDataAPI_Point::typeId()) {
286     AttributePointPtr anAttribute =
287         std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
288     std::string anExpressionString[3] = {
289       anAttribute->textX(),
290       anAttribute->textY(),
291       anAttribute->textZ()
292     };
293     for (int i = 0; i < 3; ++i)
294       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
295                                                        theOldName, theNewName);
296     anAttribute->setText(anExpressionString[0],
297                          anExpressionString[1],
298                          anExpressionString[2]);
299   } else
300   if (theAttribute->attributeType() == GeomDataAPI_Point2D::typeId()) {
301     AttributePoint2DPtr anAttribute =
302         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
303     std::string anExpressionString[2] = {
304       anAttribute->textX(),
305       anAttribute->textY()
306     };
307     for (int i = 0; i < 2; ++i)
308       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
309                                                        theOldName, theNewName);
310     anAttribute->setText(anExpressionString[0],
311                          anExpressionString[1]);
312   }
313 }
314
315 void ParametersPlugin_EvalListener::renameInDependents(std::shared_ptr<ModelAPI_ResultParameter> theResultParameter,
316                                                        const std::string& theOldName,
317                                                        const std::string& theNewName)
318 {
319   std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes =
320       theResultParameter->data()->refsToMe();
321   std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator anAttributeIt =
322       anAttributes.cbegin();
323   for (; anAttributeIt != anAttributes.cend(); ++anAttributeIt) {
324     const AttributePtr& anAttribute = *anAttributeIt;
325     if (anAttribute->attributeType() == ModelAPI_AttributeRefList::typeId()) {
326       std::shared_ptr<ParametersPlugin_Parameter> aParameter =
327           std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
328               anAttribute->owner());
329       if (aParameter.get())
330         // Rename
331         renameInParameter(aParameter, theOldName, theNewName);
332     } else
333         // Rename
334         renameInAttribute(anAttribute, theOldName, theNewName);
335   }
336 }
337
338 bool isValidAttribute(const AttributePtr& theAttribute)
339 {
340   std::string aValidator, anError;
341   return ModelAPI_Session::get()->validators()->validate(theAttribute, aValidator, anError);
342 }
343
344 void setParameterName(ResultParameterPtr theResultParameter, const std::string& theName)
345 {
346   theResultParameter->data()->blockSendAttributeUpdated(true);
347   theResultParameter->data()->setName(theName);
348   theResultParameter->data()->blockSendAttributeUpdated(false);
349
350   std::shared_ptr<ParametersPlugin_Parameter> aParameter = 
351       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
352           ModelAPI_Feature::feature(theResultParameter));
353
354   aParameter->data()->blockSendAttributeUpdated(true);
355   aParameter->data()->setName(theName);
356   aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(theName);
357   aParameter->data()->blockSendAttributeUpdated(false);
358 }
359
360 void ParametersPlugin_EvalListener::processObjectRenamedEvent(
361     const std::shared_ptr<Events_Message>& theMessage)
362 {
363   std::shared_ptr<ModelAPI_ObjectRenamedMessage> aMessage =
364       std::dynamic_pointer_cast<ModelAPI_ObjectRenamedMessage>(theMessage);
365
366   // Empty new name is not available too but it will be rejected by
367   // name validator in isValidAttribute.
368   if (!aMessage.get() || aMessage->oldName().empty())
369     return;
370
371   // check if the renamed object is a result parameter
372   ResultParameterPtr aResultParameter =
373       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aMessage->object());
374   if (!aResultParameter.get()) 
375     return;
376
377   // get parameter feature for the result
378   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
379       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
380           ModelAPI_Feature::feature(aResultParameter));
381   if (!aParameter.get())
382     return;
383
384   std::string aNotActivatedNames;
385   if (!ModelAPI_Tools::allDocumentsActivated(aNotActivatedNames)) {
386     QMessageBox::StandardButton aRes = QMessageBox::warning(0, QObject::tr("Warning"),
387                QObject::tr("Selected objects can be used in Part documents which are not loaded: \
388 %1. Whould you like to continue?").arg(aNotActivatedNames.c_str()),
389                QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
390     if (aRes != QMessageBox::Yes) {
391       setParameterName(aResultParameter, aMessage->oldName());
392       return;
393     }
394   }
395
396   // try to update the parameter feature according the new name
397   setParameterName(aResultParameter, aMessage->newName());
398   // TODO(spo): replace with ModelAPI_Session::get()->validators()->validate(aParameter, ParametersPlugin_Parameter::VARIABLE_ID())
399   // when ModelAPI_ValidatorsFactory::validate(const std::shared_ptr<ModelAPI_Feature>& theFeature, const std::string& theAttribute) const
400   // is ready
401   if (!isValidAttribute(aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID()))) {
402     setParameterName(aResultParameter, aMessage->oldName());
403     return;
404   }
405
406   renameInDependents(aResultParameter, aMessage->oldName(), aMessage->newName());
407 }
408
409 void ParametersPlugin_EvalListener::processReplaceParameterEvent(
410     const std::shared_ptr<Events_Message>& theMessage)
411 {
412   std::shared_ptr<ModelAPI_ReplaceParameterMessage> aMessage =
413       std::dynamic_pointer_cast<ModelAPI_ReplaceParameterMessage>(theMessage);
414
415   // get parameter feature for the object
416   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
417       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
418           ModelAPI_Feature::feature(aMessage->object()));
419   if (!aParameter.get())
420     return;
421
422   ResultParameterPtr aResultParameter =
423       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(
424           aParameter->firstResult());
425   if (!aResultParameter.get())
426     return;
427
428   double aRealValue = aResultParameter->data()->real(ModelAPI_ResultParameter::VALUE())->value();
429   std::string aValue = toStdString(aRealValue);
430
431   renameInDependents(aResultParameter, aResultParameter->data()->name(), aValue);
432 }