00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <QtGui/QLineEdit>
00015 #include <QtGui/QPushButton>
00016 #include <QtCore/QRegExp>
00017 #include <QtGui/QSplitter>
00018 #include <QtGui/QLabel>
00019 #include <QtGui/QMouseEvent>
00020 #include <QtGui/QKeyEvent>
00021 #include <QtGui/QHBoxLayout>
00022 #include <QtGui/QVBoxLayout>
00023 #include <QtGui/QPaintEvent>
00024 #include <QtGui/QPainter>
00025 #include <QtGui/QScrollArea>
00026 #include <QtGui/QSpacerItem>
00027 #include <QtGui/QSizePolicy>
00028 #include <QtGui/QTextCharFormat>
00029 #include <QtGui/QCompleter>
00030 #include <QtGui/QAbstractItemView>
00031 #include <QtGui/QStandardItemModel>
00032 #include <QtGui/QStringListModel>
00033 #include <QtGui/QScrollBar>
00034 #include <QtGui/QToolTip>
00035 #include <QtGui/QListWidget>
00036 #include <QtGui/QTreeView>
00037 #include <QtGui/QAction>
00038 #include <QtGui/QMenu>
00039
00040 #include <SeExpression.h>
00041 #include <SeExprNode.h>
00042 #include <SeExprFunc.h>
00043 #include <SeExprBuiltins.h>
00044
00045 #include "SeExprEditor.h"
00046 #include "SeExprEdHighlighter.h"
00047 #include "SeExprEdCompletionModel.h"
00048 #include "SeExprEdControlCollection.h"
00049 #include "SeExprEdCurve.h"
00050 #include "SeExprEdColorCurve.h"
00051 #include "SeExprEdControl.h"
00052 #include "SeExprEdPopupDocumentation.h"
00053
00054 SeExprEdLineEdit::SeExprEdLineEdit(int id, QWidget* parent)
00055 : QLineEdit(parent), _id(id), _signaling(0)
00056 {
00057 connect(this, SIGNAL(textChanged(const QString &)), SLOT(textChangedCB(const QString &)));
00058 }
00059
00060
00061 void SeExprEdLineEdit::textChangedCB(const QString& text)
00062 {
00063 _signaling = 1;
00064 emit textChanged(_id, text);
00065 _signaling = 0;
00066 }
00067
00068 void SeExprEditor::controlChanged(int id)
00069 {
00070 QString newText=exprTe->toPlainText();
00071 controls->updateText(id,newText);
00072 _updatingText=1;
00073 exprTe->selectAll();
00074 exprTe->insertPlainText(newText);
00075
00076 _updatingText=0;
00077
00078
00079 previewTimer->setSingleShot(true);
00080 previewTimer->start(0);
00081 }
00082
00083 SeExprEditor::~SeExprEditor()
00084 {
00085 delete controlRebuildTimer;
00086 delete previewTimer;
00087 }
00088
00089 SeExprEdExpressionTextEdit::~SeExprEdExpressionTextEdit()
00090 {}
00091
00092 SeExprEditor::SeExprEditor(QWidget* parent,SeExprEdControlCollection* controls)
00093 : QWidget(parent),_updatingText(0),errorHeight(0)
00094 {
00095
00096 controlRebuildTimer=new QTimer();
00097 previewTimer=new QTimer();
00098
00099
00100 setWindowTitle("Expression Editor");
00101 setMinimumHeight(100);
00102
00103
00104 this->controls = controls;
00105
00106
00107 QVBoxLayout* exprAndErrors=new QVBoxLayout;
00108 exprAndErrors->setMargin(0);
00109 setLayout(exprAndErrors);
00110
00111
00112 exprTe = new SeExprEdExpressionTextEdit(this);
00113 exprTe->setMinimumHeight(50);
00114
00115
00116 int fontsize = 12;
00117 while (QFontMetrics(QFont("Liberation Sans",fontsize)).width("abcdef")<38 && fontsize<20)
00118 fontsize++;
00119 while (QFontMetrics(QFont("Liberation Sans",fontsize)).width("abcdef")>44 && fontsize>3)
00120 fontsize--;
00121
00122 exprTe->setFont(QFont("Liberation Sans",fontsize));
00123
00124 exprAndErrors->addWidget(exprTe);
00125
00126
00127 errorWidget=new QListWidget();
00128 errorWidget->setSelectionMode(QAbstractItemView::SingleSelection);
00129 errorWidget->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Maximum));
00130 connect(errorWidget,SIGNAL(itemSelectionChanged()),SLOT(selectError()));
00131 clearErrors();
00132 exprAndErrors->addWidget(errorWidget);
00133
00134
00135 connect(exprTe, SIGNAL(applyShortcut()), SLOT(sendApply()));
00136 connect(exprTe, SIGNAL(nextError()), SLOT(nextError()));
00137 connect(exprTe, SIGNAL(textChanged()), SLOT(exprChanged()));
00138 connect(controls,SIGNAL(controlChanged(int)),SLOT(controlChanged(int)));
00139 connect(controls,SIGNAL(insertString(const std::string&)),SLOT(insertStr(const std::string&)));
00140 connect(controlRebuildTimer, SIGNAL(timeout()), SLOT(rebuildControls()));
00141 connect(previewTimer, SIGNAL(timeout()), SLOT(sendPreview()));
00142 }
00143
00144 void SeExprEditor::selectError()
00145 {
00146 int selected=errorWidget->currentRow();
00147 QListWidgetItem* item=errorWidget->item(selected);
00148 int start=item->data(Qt::UserRole).toInt();
00149 int end=item->data(Qt::UserRole+1).toInt();
00150 QTextCursor cursor=exprTe->textCursor();
00151 cursor.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor);
00152 cursor.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor,start);
00153 cursor.movePosition(QTextCursor::Right,QTextCursor::KeepAnchor,end-start+1);
00154 exprTe->setTextCursor(cursor);
00155 }
00156
00157 void SeExprEditor::sendApply()
00158 {
00159 emit apply();
00160 }
00161
00162 void SeExprEditor::sendPreview()
00163 {
00164 emit preview();
00165 }
00166
00167 void SeExprEditor::exprChanged()
00168 {
00169 if (_updatingText) return;
00170
00171
00172 controlRebuildTimer->setSingleShot(true);
00173 controlRebuildTimer->start(0);
00174 }
00175
00176 void SeExprEditor::rebuildControls()
00177 {
00178 bool wasShown=!exprTe->completer->popup()->isHidden();
00179 bool newVariables=controls->rebuildControls(exprTe->toPlainText(),exprTe->completionModel->local_variables);
00180 if(newVariables) exprTe->completer->setModel(exprTe->completionModel);
00181 if(wasShown) exprTe->completer->popup()->show();
00182 }
00183
00184 void SeExprEdExpressionTextEdit::updateStyle()
00185 {
00186 lastStyleForHighlighter=0;
00187 highlighter->fixStyle(palette());
00188 highlighter->rehighlight();
00189 repaint();
00190 }
00191
00192 SeExprEdExpressionTextEdit::SeExprEdExpressionTextEdit(QWidget* parent)
00193 :QTextEdit(parent),lastStyleForHighlighter(0),_tip(0)
00194 {
00195 highlighter=new SeExprEdHighlighter(document());
00196
00197
00198 completer=new QCompleter();
00199 completionModel=new SeExprEdCompletionModel(this);
00200 completer->setModel(completionModel);
00201 QTreeView* treePopup=new QTreeView;
00202 completer->setPopup(treePopup);
00203 treePopup->setRootIsDecorated(false);
00204 treePopup->setMinimumWidth(300);
00205 treePopup->setMinimumHeight(50);
00206 treePopup->setItemsExpandable(true);
00207
00208 completer->setWidget(this);
00209 completer->setCompletionMode(QCompleter::PopupCompletion);
00210 completer->setCaseSensitivity(Qt::CaseInsensitive);
00211 QObject::connect(completer,SIGNAL(activated(const QString&)),this,
00212 SLOT(insertCompletion(const QString&)));
00213
00214 _popupEnabledAction = new QAction("Pop-up Help", this);
00215 _popupEnabledAction->setCheckable(true);
00216 _popupEnabledAction->setChecked(true);
00217 }
00218
00219 void SeExprEdExpressionTextEdit::focusInEvent(QFocusEvent* e)
00220 {
00221 if(completer) completer->setWidget(this);
00222 QTextEdit::focusInEvent(e);
00223 }
00224
00225 void SeExprEdExpressionTextEdit::focusOutEvent(QFocusEvent* e)
00226 {
00227 hideTip();
00228 QTextEdit::focusInEvent(e);
00229 }
00230
00231 void SeExprEdExpressionTextEdit::mousePressEvent(QMouseEvent* event)
00232 {
00233 hideTip();
00234 QTextEdit::mousePressEvent(event);
00235 }
00236
00237 void SeExprEdExpressionTextEdit::mouseDoubleClickEvent(QMouseEvent* event)
00238 {
00239 hideTip();
00240 QTextEdit::mouseDoubleClickEvent(event);
00241 }
00242
00243 void SeExprEdExpressionTextEdit::paintEvent(QPaintEvent* event)
00244 {
00245 if(lastStyleForHighlighter!=style()){
00246 lastStyleForHighlighter=style();
00247 highlighter->fixStyle(palette());
00248 highlighter->rehighlight();
00249 }
00250 QTextEdit::paintEvent(event);
00251 }
00252
00253 void SeExprEdExpressionTextEdit::wheelEvent(QWheelEvent* event)
00254 {
00255 if(event->modifiers() == Qt::ControlModifier){
00256 if(event->delta()>0) zoomIn();
00257 else if(event->delta()<0) zoomOut();
00258 }
00259 return QTextEdit::wheelEvent(event);
00260 }
00261
00262 void SeExprEdExpressionTextEdit::keyPressEvent( QKeyEvent* e )
00263 {
00264
00265 if (e->key()==Qt::Key_Return && e->modifiers()==Qt::ControlModifier){
00266 emit applyShortcut();
00267 return ;
00268 }else if(e->key()==Qt::Key_F4){
00269 emit nextError();
00270 return;
00271 }
00272
00273
00274 if(completer && completer->popup()->isVisible()){
00275 switch(e->key()){
00276 case Qt::Key_Enter:case Qt::Key_Return:case Qt::Key_Escape:
00277 case Qt::Key_Tab:case Qt::Key_Backtab:
00278 e->ignore();return;
00279 default:
00280 break;
00281 }
00282 }
00283
00284
00285 bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E);
00286 if (!isShortcut)
00287 QTextEdit::keyPressEvent(e);
00288
00289 const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
00290 if (!completer || (ctrlOrShift && e->text().isEmpty()))
00291 return;
00292
00293 bool hasModifier=(e->modifiers()!=Qt::NoModifier) && !ctrlOrShift;
00294
00295
00296 QTextCursor tc=textCursor();
00297 tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
00298 QString line=tc.selectedText();
00299
00300
00301 static QRegExp completion("^(?:.*[^A-Za-z0-9_$])?((?:\\$[A-Za-z0-9_]*)|[A-Za-z]+[A-Za-z0-9_]*)$");
00302 int index=completion.indexIn(line);
00303 QString completionPrefix;
00304 if(index != -1 && !line.contains('#')){
00305 completionPrefix=completion.cap(1);
00306
00307 }
00308
00309
00310 if(!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length()<1 || index==-1)){
00311 completer->popup()->hide();
00312 }else if (_popupEnabledAction->isChecked()) {
00313
00314
00315 if(completionPrefix!=completer->completionPrefix()){
00316 completer->setCompletionPrefix(completionPrefix);
00317 completer->popup()->setCurrentIndex(completer->completionModel()->index(0,0));
00318 }
00319
00320
00321 QRect cr=cursorRect();
00322 cr.setWidth(completer->popup()->sizeHintForColumn(0)+completer->popup()->sizeHintForColumn(1)
00323 + completer->popup()->verticalScrollBar()->sizeHint().width());
00324 cr.translate(0,6);
00325 completer->complete(cr);
00326 hideTip();
00327 return;
00328 }
00329
00330
00331 static QRegExp inFunction("^(?:.*[^A-Za-z0-9_$])?([A-Za-z0-9_]+)\\([^()]*$");
00332 int index2=inFunction.indexIn(line);
00333 if(index2!=-1){
00334 QString functionName=inFunction.cap(1);
00335 QStringList tips=completionModel->getDocString(functionName).split("\n");
00336 QString tip="<b>"+tips[0]+"</b>";
00337 for(int i=1;i<tips.size();i++){
00338 tip+="<br>"+tips[i];
00339 }
00340 if (_popupEnabledAction->isChecked()) showTip(tip);
00341
00342 }else{
00343 hideTip();
00344 }
00345 }
00346
00347 void SeExprEdExpressionTextEdit::contextMenuEvent(QContextMenuEvent *event)
00348 {
00349 QMenu *menu = createStandardContextMenu();
00350
00351 if (!menu->actions().empty())
00352 {
00353 QAction* f = menu->actions().first();
00354 menu->insertAction(f, _popupEnabledAction);
00355 menu->insertSeparator(f);
00356 }
00357
00358 menu->exec(event->globalPos());
00359 delete menu;
00360 }
00361
00362 void SeExprEdExpressionTextEdit::showTip(const QString& string)
00363 {
00364
00365 if(string=="") return;
00366
00367 if(_tip && !_tip->isHidden() && _tip->label->text() == string) return;
00368
00369 QRect cr=cursorRect();
00370 cr.setX(0);
00371 cr.setWidth(cr.width()*3);
00372 if(_tip){delete _tip;_tip=0;}
00373 _tip=new SeExprEdPopupDocumentation(this,mapToGlobal(cr.bottomLeft())+QPoint(0,6),string);
00374 }
00375
00376 void SeExprEdExpressionTextEdit::hideTip()
00377 {
00378 if(_tip) _tip->hide();
00379 }
00380
00381 void SeExprEdExpressionTextEdit::insertCompletion(const QString &completion)
00382 {
00383 if (completer->widget() != this) return;
00384 QTextCursor tc = textCursor();
00385 int extra = completion.length() - completer->completionPrefix().length();
00386 tc.movePosition(QTextCursor::Left);
00387 tc.movePosition(QTextCursor::EndOfWord);
00388 tc.insertText(completion.right(extra));
00389 if(completion[0]!='$') tc.insertText("(");
00390 setTextCursor(tc);
00391 }
00392
00393 std::string SeExprEditor::getExpr()
00394 {
00395 return exprTe->toPlainText().toStdString();
00396 }
00397
00398 void SeExprEditor::setExpr(const std::string& expression,const bool doApply)
00399 {
00400
00401 exprTe->selectAll();
00402 exprTe->insertPlainText(QString::fromStdString(expression));
00403 clearErrors();
00404 exprTe->moveCursor(QTextCursor::Start);
00405 if(doApply) emit apply();
00406 }
00407
00408 void SeExprEditor::insertStr(const std::string& str)
00409 {
00410 exprTe->insertPlainText(QString::fromStdString(str));
00411 }
00412
00413 void SeExprEditor::appendStr(const std::string& str)
00414 {
00415 exprTe->append(QString::fromStdString(str));
00416 }
00417
00418 void SeExprEditor::addError(const int startPos,const int endPos,const std::string& error)
00419 {
00420 QListWidgetItem* item=new QListWidgetItem(("Error: "+error).c_str(),errorWidget);
00421 item->setData(Qt::UserRole,startPos);
00422 item->setData(Qt::UserRole+1,endPos);
00423 errorWidget->setHidden(false);
00424
00425 const char* c=error.c_str();
00426 int lines=1;
00427 while(*c != '\0'){
00428 if(*c == '\n') lines++;
00429 c++;
00430 }
00431 errorHeight+=25*lines;
00432
00433 errorWidget->setMaximumHeight(errorHeight);
00434 }
00435
00436 void SeExprEditor::nextError()
00437 {
00438 int newRow=errorWidget->currentRow()+1;
00439 if(newRow>=errorWidget->count()) newRow=0;
00440 errorWidget->setCurrentRow(newRow);
00441 }
00442
00443 void SeExprEditor::clearErrors()
00444 {
00445 errorWidget->clear();
00446 errorWidget->setHidden(true);
00447 errorHeight=0;
00448 }
00449
00450 void SeExprEditor::clearExtraCompleters()
00451 {
00452 exprTe->completionModel->clearFunctions();
00453 exprTe->completionModel->clearVariables();
00454 }
00455
00456 void SeExprEditor::registerExtraFunction(const std::string& name,const std::string& docString)
00457 {
00458 exprTe->completionModel->addFunction(name.c_str(),docString.c_str());
00459 }
00460
00461 void SeExprEditor::registerExtraVariable(const std::string& name,const std::string& docString)
00462 {
00463 exprTe->completionModel->addVariable(name.c_str(),docString.c_str());
00464 }
00465
00466 void SeExprEditor::replaceExtras(const SeExprEdCompletionModel& completer)
00467 {
00468 exprTe->completionModel->syncExtras(completer);
00469 }
00470
00471 void SeExprEditor::updateCompleter()
00472 {
00473 exprTe->completer->setModel(exprTe->completionModel);
00474 }
00475
00476 void SeExprEditor::updateStyle()
00477 {
00478 exprTe->updateStyle();
00479 }
00480