SeExpr
ExprShortEdit.cpp
Go to the documentation of this file.
1 /*
2 * Copyright Disney Enterprises, Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License
6 * and the following modification to it: Section 6 Trademarks.
7 * deleted and replaced with:
8 *
9 * 6. Trademarks. This License does not grant permission to use the
10 * trade names, trademarks, service marks, or product names of the
11 * Licensor and its affiliates, except as required for reproducing
12 * the content of the NOTICE file.
13 *
14 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * @file ExprShortEdit.cpp
18 * @brief This provides an expression editor for SeExpr syntax with auto ui features
19 * @author aselle
20 */
21 #include <QtGui/QLineEdit>
22 #include <QtGui/QPushButton>
23 #include <QtGui/QToolButton>
24 #include <QtGui/QHBoxLayout>
25 #include <QtGui/QIcon>
26 #include <QtGui/QCompleter>
27 #include <QtGui/QTreeView>
28 #include <QtGui/QScrollBar>
29 #include <QtGui/QToolTip>
30 #include <QtGui/QLabel>
31 
32 
33 #include "ExprShortEdit.h"
34 #include "ExprDialog.h"
35 #include "ExprBrowser.h"
36 #include "ExprHighlighter.h"
37 #include "ExprCompletionModel.h"
38 #include "ExprControlCollection.h"
39 #include "ExprPopupDoc.h"
40 #include "BasicExpression.h"
41 
42 
43 /* XPM */
44 static const char *sum_xpm[]={
45 "16 16 6 1",
46 "# c None",
47 ". c None",
48 "b c #808080",
49 "d c #010000",
50 "c c #aaaaaa",
51 "a c #303030",
52 "................",
53 ".#aaaaaaaaaa##..",
54 ".abbbbbbcbbba...",
55 ".#abbaaaaabba...",
56 "..#aabba..aba...",
57 "....#abba..a#...",
58 ".....#abba......",
59 ".....#abba......",
60 "...##abba...#...",
61 "...#abba...aa...",
62 "..#abba...aca...",
63 ".#abbaaaaabba...",
64 ".abbbbbbbbbba...",
65 ".aaaaaaaaaaa#...",
66 "................",
67 "................"};
68 
69 /* XPM */
70 static const char *stop_xpm[] = {
71 "16 16 4 1",
72 " c None",
73 ". c #FF0000",
74 "+ c #FF8080",
75 "@ c #FFFFFF",
76 " ",
77 " ",
78 " ...... ",
79 " ...+++.. ",
80 " ....@@@... ",
81 " .....@@@.... ",
82 " .....@@@.... ",
83 " .....@@@.... ",
84 " .....@@@.... ",
85 " ............ ",
86 " ....@@@.... ",
87 " ...@@@... ",
88 " ....... ",
89 " ..... ",
90 " ",
91 " "};
92 
93 
94 
95 ExprShortEdit::ExprShortEdit(QWidget* parent, bool expanded, bool applyOnSelect)
96  :QWidget(parent)
97  , _dialog( 0 )
98  , _context( "" )
99  , _searchPath( "" )
100  , _applyOnSelect(applyOnSelect)
101 {
102  controlRebuildTimer = new QTimer(this);
103 
104  vboxlayout = new QVBoxLayout();
105  vboxlayout->setSpacing( 2 );
106  vboxlayout->setContentsMargins( 0, 0, 0, 0 );
107  hboxlayout = new QHBoxLayout();
108  hboxlayout->setSpacing( 2 );
109  hboxlayout->setContentsMargins( 0, 0, 0, 0 );
110  edit = new ExprShortTextEdit( parent );
111 
112  error = new QLabel();
113  error->setPixmap( QPixmap(stop_xpm) );
114  error->setHidden( true );
115 
116  expandButton = new QToolButton();
117  expandButton->setFixedSize( 20, 20 );
118  expandButton->setFocusPolicy( Qt::NoFocus );
119  if (expanded) expandButton->setArrowType( Qt::DownArrow );
120  else expandButton->setArrowType( Qt::RightArrow );
121  connect( expandButton, SIGNAL(clicked()), SLOT(expandPressed()) );
122 
123  QToolButton* button = new QToolButton();
124  editDetail = button;
125  button->setIcon( QIcon(QPixmap(sum_xpm)) );
126  hboxlayout->addWidget( expandButton );
127  hboxlayout->addWidget( edit );
128  hboxlayout->addWidget( error );
129  hboxlayout->addWidget( editDetail );
130 
131  editDetail->setFixedSize( 20, 20 );
132  connect( editDetail, SIGNAL(clicked()), SLOT(detailPressed()) );
133  connect( edit, SIGNAL(editingFinished()), SLOT(textFinished()) );
134 
135  vboxlayout->addLayout( hboxlayout );
136 
137  controls = 0;
138  if ( expanded )
139  expandPressed();
140 
141  setLayout( vboxlayout );
142  connect( controlRebuildTimer, SIGNAL(timeout()), SLOT(rebuildControls()) );
143 }
144 
146 {
147 }
148 
149 void ExprShortEdit::setSearchPath(const QString& context, const QString& path)
150 {
151  _context = context.toStdString();
152  _searchPath = path.toStdString();
153 }
154 
156 {
157  showDetails(-1);
158 }
159 
161 {
162  _dialog = new ExprDialog(0);
164 
166  _dialog->browser->setSearchPath(_context.c_str(), _searchPath.c_str());
169  if (idx >= 0) {
170  _dialog->showEditor(idx);
171  }
172 
173  connect(_dialog,SIGNAL(expressionApplied()),SLOT(expressionApplied()));
174  connect(_dialog,SIGNAL(dialogClosed()),SLOT(dialogClosed()));
175  _dialog->show();
176  setEnabled(false);
177 }
178 
180 {
182 }
183 
185 {
186  setEnabled(true);
187 }
188 
190 {
191  if(controls){
192  bool wasShown=!edit->completer->popup()->isHidden();
194  if (controls->numControls()==0)
195  {
196  controls->deleteLater();
197  controls=0;
198  expandButton->setArrowType(Qt::RightArrow);
199  }
200  else
201  vboxlayout->addWidget(controls);
202 
203  if(newVariables)
204  edit->completer->setModel(edit->completionModel);
205  if(wasShown)
206  edit->completer->popup()->show();
207  }
208 }
209 
211 {
212  if(controls){
213  //vboxlayout->removeWidget(controls);
214  controls->deleteLater();
215  controls=0;
216  expandButton->setArrowType(Qt::RightArrow);
217  }else{
218  controls=new ExprControlCollection(0,false);
219  //vboxlayout->addWidget(controls);
220  connect(controls,SIGNAL(controlChanged(int)),SLOT(controlChanged(int)));
221  controlRebuildTimer->setSingleShot(true);
222  controlRebuildTimer->start(0);
223  expandButton->setArrowType(Qt::DownArrow);
224  }
225 }
226 
228 {}
229 
231 {
232  controlRebuildTimer->setSingleShot(true);
233  controlRebuildTimer->start(0);
234  checkErrors();
235  emit exprChanged();
236 }
237 
239 {
240  edit->setText(QString(expression.c_str()));
241  controlRebuildTimer->setSingleShot(true);
242  controlRebuildTimer->start(0);
243  checkErrors();
244  emit exprChanged();
245 }
246 
248 {
249  return edit->toPlainText();
250 }
251 
253 {
254  return getExpression().toStdString();
255 }
256 
258 {
259  if(controls){
260  QString newText=getExpression();
261  controls->updateText(id,newText);
262  edit->setText(newText);
263  checkErrors();
264  emit exprChanged();
265  }
266 }
267 
268 void ExprShortEdit::
270 {
273 }
274 
275 void ExprShortEdit::
276 registerExtraFunction(const std::string& name,const std::string& docString)
277 {
278  edit->completionModel->addFunction(name.c_str(),docString.c_str());
279 }
280 
281 void ExprShortEdit::
282 registerExtraVariable(const std::string& name,const std::string& docString)
283 {
284  edit->completionModel->addVariable(name.c_str(),docString.c_str());
285 }
286 
287 void ExprShortEdit::
289 {
290  edit->completer->setModel(edit->completionModel);
291 }
292 
293 void ExprShortEdit::
295 {
297  bool valid=expr.isValid();
298  std::string err;
299  if (!valid)
300  err = expr.parseError();
301 
302  hideErrors(valid, err);
303 }
304 
305 void ExprShortEdit::
306 hideErrors(bool hidden, const std::string &err)
307 {
308  error->setHidden(hidden);
309  if (!hidden) {
310  error->setToolTip(QString::fromStdString(err));
311  }
312 }
313 
314 void ExprShortEdit::
315 setSimple(bool enabled)
316 {
317  edit->setHidden(enabled);
318  editDetail->setHidden(enabled);
319  expandButton->setHidden(enabled);
320 }
321 
323 {
324  editDetail->setMenu(menu);
325 }
326 
327 void ExprShortEdit::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
328 {
329  edit->setVerticalScrollBarPolicy(policy);
330 }
331 
332 void ExprShortEdit::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy )
333 {
334  edit->setHorizontalScrollBarPolicy( policy );
335 }
336 
337 void ExprShortEdit::setLineWrapMode( QTextEdit::LineWrapMode mode )
338 {
339  edit->setLineWrapMode( mode );
340 }
341 
343 :QTextEdit(parent),editing(false),_tip(0)
344 {
346  setMaximumHeight(25);
347  highlighter=new ExprHighlighter(document());
348  highlighter->fixStyle(palette());
349  highlighter->rehighlight();
350  repaint();
351 
352  // setup auto completion
353  completer=new QCompleter();
355  completer->setModel(completionModel);
356  QTreeView* treePopup=new QTreeView;
357  completer->setPopup(treePopup);
358  treePopup->setRootIsDecorated(false);
359  treePopup->setMinimumWidth(300);
360  treePopup->setMinimumHeight(50);
361  treePopup->setItemsExpandable(true);
362 
363  completer->setWidget(this);
364  completer->setCompletionMode(QCompleter::PopupCompletion);
365  completer->setCaseSensitivity(Qt::CaseInsensitive);
366  QObject::connect(completer,SIGNAL(activated(const QString&)),this,
367  SLOT(insertCompletion(const QString&)));
368 
369 }
370 
372 {
373  //setTextCursor(QTextCursor(document()));
374  if(completer) completer->setWidget(this);
375  QTextEdit::focusInEvent(e);
376 }
377 
379 {
380  //setTextCursor(QTextCursor());
381  finishEdit();
382  QTextCursor newCursor=textCursor();
383  newCursor.clearSelection();
384  setTextCursor(newCursor);
385  setColor(false);
386  hideTip();
387  QTextEdit::focusOutEvent(e);
388 }
389 
390 void ExprShortTextEdit::mousePressEvent(QMouseEvent* event)
391 {
392  hideTip();
393  QTextEdit::mousePressEvent(event);
394 }
395 
397 {
398  hideTip();
399  QTextEdit::mouseDoubleClickEvent(event);
400 }
401 
402 void ExprShortTextEdit::paintEvent(QPaintEvent* e){
405  highlighter->fixStyle(palette());
406  highlighter->rehighlight();
407  }
408  QTextEdit::paintEvent(e);
409 }
410 
411 
413 {
414 
415 
416  // If the completer is active pass keys it needs down
417  if(completer && completer->popup()->isVisible()){
418  switch(e->key()){
419  case Qt::Key_Enter:case Qt::Key_Return:case Qt::Key_Escape:
420  case Qt::Key_Tab:case Qt::Key_Backtab:
421  e->ignore();return;
422  default:
423  break;
424  }
425  }
426 
427  // Accept expression
428  if (e->key()==Qt::Key_Return || e->key()==Qt::Key_Enter){
429  selectAll();
430  finishEdit();
431  return;
432  }else if (e->key()==Qt::Key_Escape){
433  setText(savedText);
434  selectAll();
435  finishEdit();
436  return;
437  }else if(e->key()==Qt::Key_Tab){
438  QWidget::keyPressEvent(e);
439  return;
440  }else if(!editing){
441  editing=true;
442  setColor(true);
443  savedText=toPlainText();
444  }
445 
446 
447 
448  // use the values here as long as we are not using the shortcut to bring up the editor
449  bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
450  if (!isShortcut) // dont process the shortcut when we have a completer
451  QTextEdit::keyPressEvent(e);
452 
453 
454  const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
455  if (!completer || (ctrlOrShift && e->text().isEmpty()))
456  return;
457 
458  bool hasModifier=(e->modifiers()!=Qt::NoModifier) && !ctrlOrShift;
459 
460  // grab the line we're on
461  QTextCursor tc=textCursor();
462  tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
463  QString line=tc.selectedText();
464 
465  // matches the last prefix of a completable variable or function and extract as completionPrefix
466  static QRegExp completion("^(?:.*[^A-Za-z0-9_$])?((?:\\$[A-Za-z0-9_]*)|[A-Za-z]+[A-Za-z0-9_]*)$");
467  int index=completion.indexIn(line);
468  QString completionPrefix;
469  if(index != -1 && !line.contains('#')){
470  completionPrefix=completion.cap(1);
471  //std::cout<<"we have completer prefix '"<<completionPrefix.toStdString()<<"'"<<std::endl;
472  }
473 
474  // hide the completer if we have too few characters, we are at end of word
475  if(!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length()<1 || index==-1)){
476  completer->popup()->hide();
477  }else{
478 
479  // copy the completion prefix in if we don't already have it in the completer
480  if(completionPrefix!=completer->completionPrefix()){
481  completer->setCompletionPrefix(completionPrefix);
482  completer->popup()->setCurrentIndex(completer->completionModel()->index(0,0));
483  }
484 
485  // display the completer
486  QRect cr=cursorRect();
487  cr.setWidth(2*(completer->popup()->sizeHintForColumn(0)+completer->popup()->sizeHintForColumn(1)
488  + completer->popup()->verticalScrollBar()->sizeHint().width()));
489  completer->complete(cr);
490  hideTip();
491  return;
492  }
493 
494  // documentation completion
495  static QRegExp inFunction("^(?:.*[^A-Za-z0-9_$])?([A-Za-z0-9_]+)\\([^()]*$");
496  int index2=inFunction.indexIn(line);
497  if(index2!=-1){
498  QString functionName=inFunction.cap(1);
499  QStringList tips=completionModel->getDocString(functionName).split("\n");
500  QString tip="<b>"+tips[0]+"</b>";
501  for(int i=1;i<tips.size();i++){
502  tip+="<br>"+tips[i];
503  }
504  showTip(tip);
505  }else{
506  hideTip();
507  }
508 
509 }
510 
511 void ExprShortTextEdit::showTip(const QString& string)
512 {
513  // skip empty strings
514  if(string=="") return;
515  // skip already shwon stuff
516  //if(_tip && !_tip->isHidden() && _tip->label->text() == string) return;
517 
518  QRect cr=cursorRect();
519  cr.setX(0);
520  cr.setWidth(cr.width()*3);
521  if(_tip){delete _tip;_tip=0;}
522  _tip=new ExprPopupDoc(this,mapToGlobal(cr.bottomLeft()),string);
523 }
524 
526 {
527  if(_tip) _tip->hide();
528 }
529 
530 void ExprShortTextEdit::insertCompletion(const QString &completion)
531 {
532  if (completer->widget() != this) return;
533  QTextCursor tc = textCursor();
534  int extra = completion.length() - completer->completionPrefix().length();
535  tc.movePosition(QTextCursor::Left);
536  tc.movePosition(QTextCursor::EndOfWord);
537  tc.insertText(completion.right(extra));
538  if(completion[0]!='$') tc.insertText("(");
539  setTextCursor(tc);
540 
541 }
542 
544 {
545  editing=false;
546  setColor(false);
547  emit editingFinished();
548 }
549 
550 
551 
553 setColor(bool editing)
554 {
555  Q_UNUSED(editing);
556  // todo: decorate when editing
557 }
QCompleter * completer
void setSearchPath(const QString &context, const QString &path)
void exprChanged()
virtual void expandPressed()
virtual void hideErrors(bool hidden, const std::string &err)
void setApplyOnSelect(bool on)
Definition: ExprBrowser.h:73
</pre >< h2 > Evaluating expressions</h2 > Evaluating an expression is pretty easy But before we can do that we need to make an instance< pre > GrapherExpr expr("x+x^2")
void addVariable(const QString &str, const QString &comment)
std::string _searchPath
Definition: ExprShortEdit.h:59
void showDetails(int idx)
void showTip(const QString &string)
QTimer * controlRebuildTimer
Definition: ExprShortEdit.h:51
int numControls()
Number of controls.
std::vector< QString > local_variables
void paintEvent(QPaintEvent *e)
void updateCompleter()
void clearExtraCompleters()
virtual void textFinished()
void setExpressionString(const std::string &str)
Definition: ExprDialog.h:75
ExprShortTextEdit(QWidget *parent)
virtual void rebuildControls()
const std::string & parseError() const
Definition: Expression.h:140
virtual void detailPressed()
void addFunction(const QString &function, const QString &docString)
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
void setSimple(bool enabled)
void registerExtraFunction(const std::string &name, const std::string &docString)
QHBoxLayout * hboxlayout
Definition: ExprShortEdit.h:56
void expandAll()
ExprHighlighter * highlighter
std::string getExpressionString()
Definition: ExprDialog.h:70
void replaceExtras(const ExprCompletionModel &completer)
Definition: ExprEditor.cpp:473
void setDetailsMenu(QMenu *menu)
For a multi line expression
Definition: userdoc.txt:571
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
static const char * sum_xpm[]
std::string _context
Definition: ExprShortEdit.h:58
void insertCompletion(const QString &completion)
QVBoxLayout * vboxlayout
Definition: ExprShortEdit.h:55
The result is computed int int< br >< divstyle="margin-left:40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed).&nbsp
void showEditor(int idx)
Definition: ExprDialog.cpp:144
ExprPopupDoc * _tip
bool rebuildControls(const QString &expressionText, std::vector< QString > &variables)
Rebuild the controls given the new expressionText. Return any local variables found.
ExprControlCollection * controls
Definition: ExprShortEdit.h:53
void setLineWrapMode(QTextEdit::LineWrapMode mode)
ExprEditor * editor
Definition: ExprDialog.h:50
virtual void mousePressEvent(QMouseEvent *event)
void updateText(const int id, QString &text)
Request new text, given taking into account control id&#39;s new values.
void registerExtraVariable(const std::string &name, const std::string &docString)
virtual void focusInEvent(QFocusEvent *e)
void setColor(bool editing)
virtual void mouseDoubleClickEvent(QMouseEvent *event)
ExprCompletionModel * completionModel
If a scalar is used in a vector context
Definition: userdoc.txt:456
virtual void dialogClosed()
void setExpressionString(const std::string &expression)
bool isValid() const
Definition: Expression.h:133
static const char * stop_xpm[]
void show()
Definition: ExprDialog.cpp:156
std::string getExpressionString() const
virtual void keyPressEvent(QKeyEvent *e)
ExprDialog * _dialog
Definition: ExprShortEdit.h:54
QToolButton * expandButton
ExprShortEdit(QWidget *parent, bool expanded=true, bool applyOnSelect=true)
QString getExpression() const
ExprBrowser * browser
Definition: ExprDialog.h:51
virtual void handleTextEdited()
void fixStyle(const QPalette &palette)
virtual ~ExprShortEdit()
QLabel * error
Definition: ExprShortEdit.h:57
virtual void controlChanged(int id)
"margin-left: 40px style
Definition: userdoc.txt:70
virtual void expressionApplied()
QToolButton * editDetail
Definition: ExprShortEdit.h:52
QString getDocString(const QString &s)
QStyle * lastStyleForHighlighter
virtual void focusOutEvent(QFocusEvent *e)
ExprShortTextEdit * edit
void setSearchPath(const QString &context, const QString &path)