Salome HOME
Show result/error of the parameter
[modules/shaper.git] / src / ModuleBase / ModuleBase_WidgetExprEditor.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 /*
4  * ModuleBase_WidgetExprEditor.cpp
5  *
6  *  Created on: Aug 28, 2014
7  *      Author: sbh
8  */
9
10 #include <ModuleBase_WidgetExprEditor.h>
11 #include <ModuleBase_Tools.h>
12
13 #include <ModelAPI_AttributeString.h>
14 #include <ModelAPI_Data.h>
15 #include <ModelAPI_Object.h>
16 #include <ModelAPI_Validator.h>
17 #include <ModelAPI_ResultParameter.h>
18
19 #include <Config_WidgetAPI.h>
20
21 #include <QVBoxLayout>
22 #include <QLabel>
23 #include <QLineEdit>
24 #include <QObject>
25 #include <QString>
26 #include <QStringListModel>
27 #include <QCompleter>
28 #include <QSize>
29 #include <QShortcut>
30 #include <QScrollBar>
31
32 #include <memory>
33 #include <string>
34
35 ExpressionEditor::ExpressionEditor(QWidget* theParent)
36 : QPlainTextEdit(theParent)
37 {
38   myCompleter = new QCompleter(this);
39   myCompleter->setWidget(this);
40   myCompleter->setCompletionMode(QCompleter::PopupCompletion);
41
42   myCompleterModel = new QStringListModel(this);
43   myCompleter->setModel(myCompleterModel);
44   // Use sorted model to accelerate completion (QCompleter will use binary search)
45   myCompleter->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
46   myCompleter->setCaseSensitivity(Qt::CaseInsensitive);
47
48   connect(myCompleter, SIGNAL(activated(const QString&)),
49           this,        SLOT(insertCompletion(const QString&)));
50   (void) new QShortcut(QKeySequence(tr("Ctrl+Space", "Complete")),
51                        this, SLOT(performCompletion()));
52 }
53
54 ExpressionEditor::~ExpressionEditor()
55 {
56
57 }
58
59 void ExpressionEditor::setCompletionList(QStringList& theList)
60 {
61   theList.sort();
62   theList.removeDuplicates();
63   myCompleterModel->setStringList(theList);
64 }
65
66 void ExpressionEditor::insertCompletion(const QString& theCompletion, bool isSingleWord)
67 {
68   QTextCursor aCursor = textCursor();
69   int numberOfCharsToComplete = theCompletion.length() -
70       myCompleter->completionPrefix().length();
71   int insertionPosition = aCursor.position();
72   aCursor.insertText(theCompletion.right(numberOfCharsToComplete));
73   if (isSingleWord) {
74     aCursor.setPosition(insertionPosition);
75     aCursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
76     myCompletedAndSelected = true;
77   }
78   setTextCursor(aCursor);
79 }
80
81 void ExpressionEditor::performCompletion()
82 {
83   QTextCursor aCursor = textCursor();
84   aCursor.select(QTextCursor::WordUnderCursor);
85   const QString aPrefix = aCursor.selectedText();
86   if (!aPrefix.isEmpty() && aPrefix.at(aPrefix.length() - 1).isLetter()) {
87     performCompletion(aPrefix);
88   }
89 }
90
91 void ExpressionEditor::performCompletion(const QString& theCompletionPrefix)
92 {
93   //populate model?
94   if (theCompletionPrefix != myCompleter->completionPrefix()) {
95     myCompleter->setCompletionPrefix(theCompletionPrefix);
96     myCompleter->popup()->setCurrentIndex(myCompleter->completionModel()->index(0, 0));
97   }
98   if (myCompleter->completionCount() == 1) {
99     insertCompletion(myCompleter->currentCompletion(), true);
100   } else {
101     QRect aRect = cursorRect();
102     aRect.setWidth(myCompleter->popup()->sizeHintForColumn(0)
103                   + myCompleter->popup()->verticalScrollBar()->sizeHint().width());
104     myCompleter->complete(aRect);
105   }
106 }
107
108 void ExpressionEditor::keyPressEvent(QKeyEvent* theEvent)
109 {
110   if (myCompletedAndSelected && handledCompletedAndSelected(theEvent))
111     return;
112   myCompletedAndSelected = false;
113   if (myCompleter->popup()->isVisible()) {
114     switch (theEvent->key()) {
115       case Qt::Key_Up:
116       case Qt::Key_Down:
117       case Qt::Key_Enter:
118       case Qt::Key_Return:
119       case Qt::Key_Escape:
120         theEvent->ignore();
121         return;
122       default:
123         myCompleter->popup()->hide();
124         break;
125     }
126   }
127   QPlainTextEdit::keyPressEvent(theEvent);
128 }
129
130 bool ExpressionEditor::handledCompletedAndSelected(QKeyEvent* theEvent)
131 {
132   myCompletedAndSelected = false;
133   QTextCursor aCursor = textCursor();
134   switch (theEvent->key()) {
135     case Qt::Key_Enter:
136     case Qt::Key_Return: aCursor.clearSelection(); break;
137     case Qt::Key_Escape: aCursor.removeSelectedText(); break;
138     default: return false;
139   }
140   setTextCursor(aCursor);
141   theEvent->accept();
142   return true;
143 }
144
145 ModuleBase_WidgetExprEditor::ModuleBase_WidgetExprEditor(QWidget* theParent,
146                                                      const Config_WidgetAPI* theData,
147                                                      const std::string& theParentId)
148     : ModuleBase_ModelWidget(theParent, theData, theParentId)
149 {
150   QVBoxLayout* aMainLay = new QVBoxLayout(this);
151   ModuleBase_Tools::adjustMargins(aMainLay);
152
153   myResultLabel = new QLabel(this);
154   myResultLabel->setWordWrap(true);
155   aMainLay->addWidget(myResultLabel);
156   myEditor = new ExpressionEditor(this);
157   myEditor->setMinimumHeight(20);
158   aMainLay->addWidget(myEditor);
159   this->setLayout(aMainLay);
160
161   connect(myEditor, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
162 }
163
164 ModuleBase_WidgetExprEditor::~ModuleBase_WidgetExprEditor()
165 {
166 }
167
168 bool ModuleBase_WidgetExprEditor::storeValueCustom() const
169 {
170   // A rare case when plugin was not loaded. 
171   if(!myFeature)
172     return false;
173   DataPtr aData = myFeature->data();
174   AttributeStringPtr aStringAttr = aData->string(attributeID());
175   QString aWidgetValue = myEditor->toPlainText();
176   aStringAttr->setValue(aWidgetValue.toStdString());
177   updateObject(myFeature);
178
179   if(!myFeature->firstResult().get())
180     return true;
181   
182   ResultParameterPtr aResult =
183       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(myFeature->firstResult());
184   AttributeStringPtr aErrorAttr = aResult->data()->string(ModelAPI_ResultParameter::STATE());
185   myResultLabel->setText(QString::fromStdString(aErrorAttr->value()));
186   return true;
187 }
188
189 bool ModuleBase_WidgetExprEditor::restoreValue()
190 {
191   // A rare case when plugin was not loaded. 
192   if(!myFeature)
193     return false;
194   DataPtr aData = myFeature->data();
195   AttributeStringPtr aStringAttr = aData->string(attributeID());
196
197   bool isBlocked = myEditor->blockSignals(true);
198   QTextCursor aCursor = myEditor->textCursor();
199   int pos = aCursor.position();
200   std::string aRestoredStr = aStringAttr->value();
201   myEditor->setPlainText(QString::fromStdString(aRestoredStr));
202   aCursor.setPosition(pos);
203   myEditor->setTextCursor(aCursor);
204   myEditor->blockSignals(isBlocked);
205
206   return true;
207 }
208
209 QList<QWidget*> ModuleBase_WidgetExprEditor::getControls() const
210 {
211   QList<QWidget*> result;
212   result << myEditor;
213   return result;
214 }
215
216 void ModuleBase_WidgetExprEditor::onTextChanged()
217 {
218   storeValue();
219 }