00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "AeslEditor.h"
00022 #include "MainWindow.h"
00023 #include "CustomWidgets.h"
00024 #include "../utils/utils.h"
00025 #include <QtGui>
00026 #include <QtGlobal>
00027
00028 #include <AeslEditor.moc>
00029
00030 namespace Aseba
00031 {
00034
00035 AeslHighlighter::AeslHighlighter(AeslEditor *editor, QTextDocument *parent) :
00036 QSyntaxHighlighter(parent),
00037 editor(editor)
00038 {
00039 HighlightingRule rule;
00040
00041
00042 QTextCharFormat keywordFormat;
00043 keywordFormat.setForeground(Qt::darkRed);
00044 QStringList keywordPatterns;
00045 keywordPatterns << "\\bemit\\b" << "\\bwhile\\b" << "\\bdo\\b"
00046 << "\\bfor\\b" << "\\bin\\b" << "\\bstep\\b"
00047 << "\\bif\\b" << "\\bthen\\b" << "\\belse\\b" << "\\belseif\\b"
00048 << "\\bend\\b" << "\\bvar\\b" << "\\bcall\\b"
00049 << "\\bonevent\\b" << "\\bontimer\\b" << "\\bwhen\\b"
00050 << "\\band\\b" << "\\bor\\b" << "\\bnot\\b" << "\\babs\\b"
00051 << "\\bsub\\b" << "\\bcallsub\\b" << "\\breturn\\b";
00052 foreach (QString pattern, keywordPatterns)
00053 {
00054 rule.pattern = QRegExp(pattern);
00055 rule.format = keywordFormat;
00056 highlightingRules.append(rule);
00057 }
00058
00059
00060 QTextCharFormat literalsFormat;
00061 literalsFormat.setForeground(Qt::darkBlue);
00062 rule.pattern = QRegExp("\\b(-{0,1}\\d+|0x([0-9]|[a-f]|[A-F])+|0b[0-1]+)\\b");
00063 rule.format = literalsFormat;
00064 highlightingRules.append(rule);
00065
00066
00067 QTextCharFormat commentFormat;
00068 commentFormat.setForeground(Qt::gray);
00069 rule.pattern = QRegExp("(?!\\*)#(?!\\*).*");
00070 rule.format = commentFormat;
00071 highlightingRules.append(rule);
00072
00073 rule.pattern = QRegExp("^#(?!\\*).*");
00074 rule.format = commentFormat;
00075 highlightingRules.append(rule);
00076
00077
00078 rule.pattern = QRegExp("#\\*.*\\*#");
00079 rule.format = commentFormat;
00080 highlightingRules.append(rule);
00081
00082
00083 commentBlockRules.begin = QRegExp("#\\*(?!.*\\*#)");
00084 commentBlockRules.end = QRegExp(".*\\*#");
00085 commentBlockRules.format = commentFormat;
00086
00087
00088 QTextCharFormat todoFormat;
00089 todoFormat.setForeground(Qt::black);
00090 todoFormat.setBackground(QColor(255, 192, 192));
00091 rule.pattern = QRegExp("#.*(\\bTODO\\b|\\bFIXME\\b).*");
00092 rule.format = todoFormat;
00093 highlightingRules.append(rule);
00094 }
00095
00096 void AeslHighlighter::highlightBlock(const QString &text)
00097 {
00098 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(currentBlockUserData());
00099
00100
00101 bool isActive = uData && uData->properties.contains("active");
00102 bool isExecutionError = uData && uData->properties.contains("executionError");
00103 bool isBreakpointPending = uData && uData->properties.contains("breakpointPending");
00104 bool isBreakpoint = uData && uData->properties.contains("breakpoint");
00105
00106 QColor breakpointPendingColor(255, 240, 178);
00107 QColor breakpointColor(255, 211, 178);
00108 QColor activeColor(220, 220, 255);
00109 QColor errorColor(240, 100, 100);
00110
00111
00112
00113
00114
00115 QList<QTextEdit::ExtraSelection> extraSelections;
00116 if (currentBlock().blockNumber() != 0)
00117
00118 extraSelections = editor->extraSelections();
00119
00120
00121 QTextEdit::ExtraSelection selection;
00122 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
00123 selection.cursor = QTextCursor(currentBlock());
00124
00125 if (isBreakpointPending)
00126 selection.format.setBackground(breakpointPendingColor);
00127 if (isBreakpoint)
00128 selection.format.setBackground(breakpointColor);
00129 if (editor->debugging)
00130 {
00131 if (isActive)
00132 selection.format.setBackground(activeColor);
00133 if (isExecutionError)
00134 selection.format.setBackground(errorColor);
00135 }
00136
00137
00138 extraSelections.append(selection);
00139 editor->setExtraSelections(extraSelections);
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166 foreach (HighlightingRule rule, highlightingRules)
00167 {
00168 QRegExp expression(rule.pattern);
00169 int index = text.indexOf(expression);
00170 while (index >= 0)
00171 {
00172 int length = expression.matchedLength();
00173 QTextCharFormat format = rule.format;
00174
00175
00176
00177
00178
00179
00180 setFormat(index, length, format);
00181 index = text.indexOf(expression, index + length);
00182 }
00183 }
00184
00185
00186 int index;
00187 QTextCharFormat format = commentBlockRules.format;
00188
00189
00190 if (isBreakpointPending)
00191 format.setBackground(breakpointPendingColor);
00192
00193
00194 setCurrentBlockState(NO_COMMENT);
00195 if (previousBlockState() != COMMENT)
00196 {
00197
00198 if ((index = text.indexOf(commentBlockRules.begin)) != -1)
00199 {
00200
00201 setFormat(index, text.length() - index, format);
00202 setCurrentBlockState(COMMENT);
00203 }
00204 }
00205 else
00206 {
00207
00208 if ((index = text.indexOf(commentBlockRules.end)) != -1)
00209 {
00210
00211 int length = commentBlockRules.end.matchedLength();
00212 setFormat(0, length, format);
00213 setCurrentBlockState(NO_COMMENT);
00214 }
00215 else
00216 {
00217
00218 setFormat(0, text.length(), format);
00219 setCurrentBlockState(COMMENT);
00220 }
00221 }
00222
00223
00224 if (uData && uData->properties.contains("errorPos"))
00225 {
00226 int pos = uData->properties["errorPos"].toInt();
00227 int len = 0;
00228
00229 if (pos + len < text.length())
00230 {
00231
00232 while (pos + len < text.length())
00233 if (
00234 (!text[pos + len].isDigit()) &&
00235 (!text[pos + len].isLetter()) &&
00236 (text[pos + len] != '_') &&
00237 (text[pos + len] != '.')
00238 )
00239 break;
00240 else
00241 len++;
00242 }
00243 len = len > 0 ? len : 1;
00244 setFormat(pos, len, Qt::red);
00245 }
00246 }
00247
00248 AeslEditorSidebar::AeslEditorSidebar(AeslEditor* editor) :
00249 QWidget(editor),
00250 editor(editor),
00251 currentSizeHint(0,0),
00252 verticalScroll(0)
00253 {
00254 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
00255 connect(editor->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(scroll(int)));
00256 connect(editor, SIGNAL(textChanged()), SLOT(update()));
00257 }
00258
00259 void AeslEditorSidebar::scroll(int dy)
00260 {
00261 verticalScroll = dy;
00262 update();
00263 }
00264
00265 QSize AeslEditorSidebar::sizeHint() const
00266 {
00267 return QSize(idealWidth(), 0);
00268 }
00269
00270 void AeslEditorSidebar::paintEvent(QPaintEvent *event)
00271 {
00272 QSize newSizeHint = sizeHint();
00273
00274 if (currentSizeHint != newSizeHint)
00275 {
00276
00277 updateGeometry();
00278 currentSizeHint = newSizeHint;
00279 }
00280 }
00281
00282 int AeslEditorSidebar::posToLineNumber(int y)
00283 {
00284
00285
00286 QTextBlock block = editor->document()->firstBlock();
00287 int offset = editor->contentsRect().top();
00288
00289 while (block.isValid())
00290 {
00291 QRectF bounds = block.layout()->boundingRect();
00292 bounds.translate(0, offset + block.layout()->position().y() - verticalScroll);
00293 if (y > bounds.top() && y < bounds.bottom())
00294 {
00295
00296 return block.blockNumber();
00297 }
00298
00299 block = block.next();
00300 }
00301
00302
00303 return -1;
00304 }
00305
00306 AeslLineNumberSidebar::AeslLineNumberSidebar(AeslEditor *editor) :
00307 AeslEditorSidebar(editor)
00308 {
00309 }
00310
00311 void AeslLineNumberSidebar::showLineNumbers(bool state)
00312 {
00313 setVisible(state);
00314 }
00315
00316 void AeslLineNumberSidebar::paintEvent(QPaintEvent *event)
00317 {
00318 AeslEditorSidebar::paintEvent(event);
00319
00320
00321 QPainter painter(this);
00322
00323
00324 painter.fillRect(event->rect(), QColor(210, 210, 210));
00325
00326
00327 QRect editorRect = editor->contentsRect();
00328
00329
00330 painter.setClipRect(QRect(0, editorRect.top(), width(), editorRect.bottom()), Qt::ReplaceClip );
00331 painter.setClipping(true);
00332
00333
00334 QRect region = editorRect;
00335 region.setLeft(0);
00336 region.setRight(idealWidth());
00337
00338
00339 QTextBlock block = editor->document()->firstBlock();
00340
00341 painter.setPen(Qt::darkGray);
00342
00343
00344 while(block.isValid())
00345 {
00346 if (block.isVisible())
00347 {
00348 QString number = QString::number(block.blockNumber() + 1);
00349
00350 int y = block.layout()->position().y() + region.top() - verticalScroll;
00351 painter.drawText(0, y, width(), fontMetrics().height(), Qt::AlignRight, number);
00352 }
00353
00354 block = block.next();
00355 }
00356 }
00357
00358 int AeslLineNumberSidebar::idealWidth() const
00359 {
00360
00361
00362 int digits = 1;
00363 int linenumber = editor->document()->blockCount();
00364 while (linenumber >= 10) {
00365 linenumber /= 10;
00366 digits++;
00367 }
00368
00369 int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
00370
00371 return space;
00372 }
00373
00374 AeslBreakpointSidebar::AeslBreakpointSidebar(AeslEditor *editor) :
00375 AeslEditorSidebar(editor),
00376 borderSize(1)
00377 {
00378
00379 QFontMetrics metrics = fontMetrics();
00380 int lineSpacing = metrics.lineSpacing();
00381 breakpoint = QRect(borderSize, borderSize, lineSpacing - 2*borderSize, lineSpacing - 2*borderSize);
00382 }
00383
00384 void AeslBreakpointSidebar::paintEvent(QPaintEvent *event)
00385 {
00386 AeslEditorSidebar::paintEvent(event);
00387
00388
00389 QPainter painter(this);
00390
00391
00392 painter.fillRect(event->rect(), QColor(210, 210, 210));
00393
00394
00395 QRect editorRect = editor->contentsRect();
00396
00397
00398 painter.setClipRect(QRect(0, editorRect.top(), width(), editorRect.bottom()), Qt::ReplaceClip );
00399 painter.setClipping(true);
00400
00401
00402 QRect region = editorRect;
00403 region.setLeft(0);
00404 region.setRight(idealWidth());
00405
00406
00407 QTextBlock block = editor->document()->firstBlock();
00408
00409 painter.setPen(Qt::red);
00410 painter.setBrush(Qt::red);
00411
00412
00413 while(block.isValid())
00414 {
00415 if (block.isVisible())
00416 {
00417
00418 if (editor->isBreakpoint(block))
00419 {
00420
00421 int y = block.layout()->position().y() + region.top() - verticalScroll;
00422
00423 painter.drawRect(breakpoint.translated(0, y));
00424 }
00425
00426 }
00427
00428 block = block.next();
00429 }
00430 }
00431
00432 void AeslBreakpointSidebar::mousePressEvent(QMouseEvent *event)
00433 {
00434
00435 int line = posToLineNumber(event->pos().y());
00436 editor->toggleBreakpoint(editor->document()->findBlockByNumber(line));
00437 }
00438
00439 int AeslBreakpointSidebar::idealWidth() const
00440 {
00441 return breakpoint.width() + 2*borderSize;
00442 }
00443
00444 AeslEditor::AeslEditor(const ScriptTab* tab) :
00445 tab(tab),
00446 debugging(false),
00447 dropSourceWidget(0),
00448 completer(0),
00449 vardefRegexp("^var .*"),
00450 leftValueRegexp("^\\w+\\s*=.*"),
00451 previousContext(UnknownContext),
00452 editingLeftValue(false)
00453 {
00454 QFont font;
00455 font.setFamily("");
00456 font.setStyleHint(QFont::TypeWriter);
00457 font.setFixedPitch(true);
00458
00459 setFont(font);
00460 setAcceptDrops(true);
00461 setAcceptRichText(false);
00462 setTabStopWidth( QFontMetrics(font).width(' ') * 4);
00463
00464 #ifdef Q_WS_WIN
00465
00466
00467
00468 QPalette p = palette();
00469 p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight));
00470 p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText));
00471 setPalette(p);
00472 #endif // Q_WS_WIN
00473
00474
00475 completer = new QCompleter(this);
00476
00477 completer->setCaseSensitivity(Qt::CaseInsensitive);
00478 completer->setWrapAround(false);
00479 completer->setWidget(this);
00480 completer->setCompletionMode(QCompleter::PopupCompletion);
00481 QObject::connect(completer, SIGNAL(activated(QString)), SLOT(insertCompletion(QString)));
00482 }
00483
00484 void AeslEditor::dropEvent(QDropEvent *event)
00485 {
00486 dropSourceWidget = event->source();
00487 QTextEdit::dropEvent(event);
00488 dropSourceWidget = 0;
00489 setFocus(Qt::MouseFocusReason);
00490 }
00491
00492 void AeslEditor::insertFromMimeData ( const QMimeData * source )
00493 {
00494 QTextCursor cursor(this->textCursor());
00495
00496
00497 bool startOfLine(cursor.atBlockStart());
00498 const int posInBlock(cursor.position() - cursor.block().position());
00499 if (!startOfLine && posInBlock)
00500 {
00501 const QString startText(cursor.block().text().left(posInBlock));
00502 startOfLine = !startText.contains(QRegExp("\\S"));
00503 }
00504
00505
00506 if (dropSourceWidget && startOfLine)
00507 {
00508 const NodeTab* nodeTab(dynamic_cast<const NodeTab*>(tab));
00509 QString prefix("");
00510 QString midfix("");
00511 QString postfix("");
00512 if (dropSourceWidget == nodeTab->vmFunctionsView)
00513 {
00514
00515 prefix = "call ";
00516 midfix = "(";
00517
00518 const TargetDescription *desc = nodeTab->vmFunctionsModel->descriptionRead;
00519 const std::wstring funcName = source->text().toStdWString();
00520 for (size_t i = 0; i < desc->nativeFunctions.size(); i++)
00521 {
00522 const TargetDescription::NativeFunction native(desc->nativeFunctions[i]);
00523 if (native.name == funcName)
00524 {
00525 for (size_t j = 0; j < native.parameters.size(); ++j)
00526 {
00527 postfix += QString::fromStdWString(native.parameters[j].name);
00528 if (j + 1 < native.parameters.size())
00529 postfix += ", ";
00530 }
00531 break;
00532 }
00533 }
00534 postfix += ")\n";
00535 }
00536 else if (dropSourceWidget == nodeTab->vmMemoryView)
00537 {
00538 const std::wstring varName = source->text().toStdWString();
00539 if (nodeTab->vmMemoryModel->getVariableSize(QString::fromStdWString(varName)) > 1)
00540 {
00541 midfix = "[";
00542 postfix = "] ";
00543 }
00544 else
00545 midfix = " ";
00546 }
00547 else if (dropSourceWidget == nodeTab->vmLocalEvents)
00548 {
00549
00550 prefix = "onevent ";
00551 midfix = "\n";
00552 }
00553 else if (dropSourceWidget == nodeTab->mainWindow->eventsDescriptionsView)
00554 {
00555
00556 prefix = "onevent ";
00557 midfix = "\n";
00558 }
00559
00560 cursor.beginEditBlock();
00561 cursor.insertText(prefix + source->text() + midfix);
00562 const int pos = cursor.position();
00563 cursor.insertText(postfix);
00564 cursor.setPosition(pos);
00565 cursor.endEditBlock();
00566
00567 this->setTextCursor(cursor);
00568 }
00569 else
00570 cursor.insertText(source->text());
00571
00572 }
00573
00574 void AeslEditor::contextMenuEvent ( QContextMenuEvent * e )
00575 {
00576
00577 QMenu *menu = createStandardContextMenu();
00578 if (!isReadOnly())
00579 {
00580 QAction *breakpointAction;
00581 menu->addSeparator();
00582
00583
00584 QTextBlock block = cursorForPosition(e->pos()).block();
00585
00586 bool breakpointPresent = isBreakpoint(block);
00587
00588
00589 if (breakpointPresent)
00590 breakpointAction = menu->addAction(tr("Clear breakpoint"));
00591 else
00592 breakpointAction = menu->addAction(tr("Set breakpoint"));
00593 QAction *breakpointClearAllAction = menu->addAction(tr("Clear all breakpoints"));
00594
00595
00596 QAction* selectedAction = menu->exec(e->globalPos());
00597
00598
00599 if (selectedAction == breakpointAction)
00600 {
00601
00602 if (breakpointPresent)
00603 {
00604
00605 clearBreakpoint(block);
00606 }
00607 else
00608 {
00609
00610 setBreakpoint(block);
00611 }
00612 }
00613 if (selectedAction == breakpointClearAllAction)
00614 {
00615 clearAllBreakpoints();
00616 }
00617 }
00618 else
00619 menu->exec(e->globalPos());
00620 delete menu;
00621 }
00622
00623 bool AeslEditor::isBreakpoint()
00624 {
00625 return isBreakpoint(textCursor().block());
00626 }
00627
00628 bool AeslEditor::isBreakpoint(QTextBlock block)
00629 {
00630 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData());
00631 return (uData && (uData->properties.contains("breakpoint") || uData->properties.contains("breakpointPending") )) ;
00632 }
00633
00634 bool AeslEditor::isBreakpoint(int line)
00635 {
00636 QTextBlock block = document()->findBlockByNumber(line);
00637 return isBreakpoint(block);
00638 }
00639
00640 void AeslEditor::toggleBreakpoint()
00641 {
00642 toggleBreakpoint(textCursor().block());
00643 }
00644
00645 void AeslEditor::toggleBreakpoint(QTextBlock block)
00646 {
00647 if (isBreakpoint(block))
00648 clearBreakpoint(block);
00649 else
00650 setBreakpoint(block);
00651 }
00652
00653 void AeslEditor::setBreakpoint()
00654 {
00655 setBreakpoint(textCursor().block());
00656 }
00657
00658 void AeslEditor::setBreakpoint(QTextBlock block)
00659 {
00660 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData());
00661 if (!uData)
00662 {
00663
00664 uData = new AeslEditorUserData("breakpointPending");
00665 block.setUserData(uData);
00666 }
00667 else
00668 uData->properties.insert("breakpointPending", QVariant());
00669 emit breakpointSet(block.blockNumber());
00670 }
00671
00672 void AeslEditor::clearBreakpoint()
00673 {
00674 clearBreakpoint(textCursor().block());
00675 }
00676
00677 void AeslEditor::clearBreakpoint(QTextBlock block)
00678 {
00679 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData());
00680 uData->properties.remove("breakpointPending");
00681 uData->properties.remove("breakpoint");
00682 if (uData->properties.isEmpty())
00683 {
00684
00685 block.setUserData(0);
00686 }
00687 emit breakpointCleared(block.blockNumber());
00688 }
00689
00690 void AeslEditor::clearAllBreakpoints()
00691 {
00692 for (QTextBlock it = document()->begin(); it != document()->end(); it = it.next())
00693 {
00694 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(it.userData());
00695 if (uData)
00696 {
00697 uData->properties.remove("breakpoint");
00698 uData->properties.remove("breakpointPending");
00699 }
00700 }
00701 emit breakpointClearedAll();
00702 }
00703
00704 void AeslEditor::commentAndUncommentSelection(CommentOperation commentOperation)
00705 {
00706 QTextCursor cursor = textCursor();
00707 bool moveFailed = false;
00708
00709
00710 QTextBlock endBlock = document()->findBlock(cursor.selectionEnd());
00711 int lineEnd = endBlock.blockNumber();
00712 int positionInEndBlock = cursor.selectionEnd() - endBlock.position();
00713
00714
00715
00716 if (cursor.hasSelection() && (positionInEndBlock == 0))
00717 lineEnd--;
00718
00719
00720 cursor.beginEditBlock();
00721
00722
00723 cursor.setPosition(cursor.selectionStart());
00724 cursor.movePosition(QTextCursor::StartOfBlock);
00725 QTextCursor cursorRestore = cursor;
00726
00727 while (cursor.block().blockNumber() <= lineEnd)
00728 {
00729
00730 setTextCursor(cursor);
00731
00732 if (commentOperation == CommentSelection)
00733 {
00734
00735 cursor.insertText("#");
00736 }
00737 else if (commentOperation == UncommentSelection)
00738 {
00739
00740 if (cursor.block().text().at(0) == QChar('#'))
00741 cursor.deleteChar();
00742 }
00743
00744
00745 if (!cursor.movePosition(QTextCursor::NextBlock))
00746 {
00747
00748 moveFailed = true;
00749 break;
00750 }
00751 }
00752
00753
00754 cursorRestore.movePosition(QTextCursor::StartOfBlock);
00755 if (!moveFailed)
00756 cursor.movePosition(QTextCursor::StartOfBlock);
00757 else
00758 cursor.movePosition(QTextCursor::EndOfBlock);
00759 cursorRestore.setPosition(cursor.position(), QTextCursor::KeepAnchor);
00760 setTextCursor(cursorRestore);
00761
00762
00763 cursor.endEditBlock();
00764 }
00765
00766 void AeslEditor::keyPressEvent(QKeyEvent * event)
00767 {
00768 if (handleCompleter(event))
00769 return;
00770 if (handleTab(event))
00771 return;
00772 if (handleNewLine(event))
00773 return;
00774
00775
00776 QKeyEvent eventCopy(*event);
00777 QTextEdit::keyPressEvent(event);
00778 detectLocalContextChange(&eventCopy);
00779 doCompletion(&eventCopy);
00780 }
00781
00782 bool AeslEditor::handleCompleter(QKeyEvent *event)
00783 {
00784
00785 if (completer && completer->popup()->isVisible()) {
00786 switch (event->key()) {
00787 case Qt::Key_Enter:
00788 case Qt::Key_Return:
00789 case Qt::Key_Escape:
00790 case Qt::Key_Tab:
00791 case Qt::Key_Backtab:
00792 event->ignore();
00793 return true;
00794 default:
00795 break;
00796 }
00797 }
00798
00799
00800 return false;
00801 }
00802
00803 bool AeslEditor::handleTab(QKeyEvent *event)
00804 {
00805
00806 if (!(event->key() == Qt::Key_Tab) || !(textCursor().hasSelection()))
00807
00808 return false;
00809
00810 QTextCursor cursor(document()->findBlock(textCursor().selectionStart()));
00811
00812 cursor.beginEditBlock();
00813
00814 while (cursor.position() < textCursor().selectionEnd())
00815 {
00816 cursor.movePosition(QTextCursor::StartOfLine);
00817 if (event->modifiers() & Qt::ControlModifier)
00818 {
00819 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
00820 if ((cursor.selectedText() == "\t") ||
00821 ( (cursor.selectedText() == " ") &&
00822 (cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 3)) &&
00823 (cursor.selectedText() == " ")
00824 )
00825 )
00826 cursor.removeSelectedText();
00827 }
00828 else
00829 cursor.insertText("\t");
00830 cursor.movePosition(QTextCursor::Down);
00831 cursor.movePosition(QTextCursor::EndOfLine);
00832 }
00833
00834 cursor.movePosition(QTextCursor::StartOfLine);
00835 if (event->modifiers() & Qt::ControlModifier)
00836 {
00837 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
00838 if ((cursor.selectedText() == "\t") ||
00839 ( (cursor.selectedText() == " ") &&
00840 (cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 3)) &&
00841 (cursor.selectedText() == " ")
00842 )
00843 )
00844 cursor.removeSelectedText();
00845 }
00846 else
00847 cursor.insertText("\t");
00848
00849 cursor.endEditBlock();
00850
00851
00852 return true;
00853 }
00854
00855 bool AeslEditor::handleNewLine(QKeyEvent *event)
00856 {
00857
00858 if (!(event->key() == Qt::Key_Return ) && !(event->key() == Qt::Key_Enter))
00859
00860 return false;
00861
00862 QString headingSpace("\n");
00863 const QString &line = textCursor().block().text();
00864 for (size_t i = 0; i < (size_t)line.length(); i++)
00865 {
00866 const QChar c(line[(unsigned)i]);
00867 if (c.isSpace())
00868 headingSpace += c;
00869 else
00870 break;
00871
00872 }
00873 insertPlainText(headingSpace);
00874
00875
00876 return true;
00877 }
00878
00879 void AeslEditor::detectLocalContextChange(QKeyEvent *event)
00880 {
00881
00882 LocalContext currentContext = UnknownContext;
00883 QString previous = previousWord();
00884 QString line = currentLine();
00885
00886 if (previous == "call")
00887 currentContext = FunctionContext;
00888 else if (previous == "onevent" || previous == "emit")
00889 currentContext = EventContext;
00890 else if (vardefRegexp.indexIn(line) != -1)
00891 currentContext = VarDefContext;
00892 else if (leftValueRegexp.indexIn(line) == -1)
00893 currentContext = LeftValueContext;
00894 else
00895 currentContext = GeneralContext;
00896
00897 if (currentContext != previousContext)
00898 {
00899
00900 previousContext = currentContext;
00901 emit refreshModelRequest(currentContext);
00902 }
00903 }
00904
00905 void AeslEditor::doCompletion(QKeyEvent *event)
00906 {
00907 static QString eow("~!@#$%^&*()+{}|:\"<>?,/;'[]\\-=");
00908 QString completionPrefix = textUnderCursor();
00909
00910
00911
00912 if (event->text().isEmpty()|| completionPrefix.length() < 1 || eow.contains(event->text().right(1))) {
00913 completer->popup()->hide();
00914 return;
00915 }
00916
00917
00918 if (completionPrefix != completer->completionPrefix()) {
00919 completer->setCompletionPrefix(completionPrefix);
00920 completer->popup()->setCurrentIndex(completer->completionModel()->index(0, 0));
00921 }
00922
00923
00924
00925 if (completer->completionCount() == 1)
00926 if (completer->currentCompletion() == completionPrefix)
00927 {
00928 completer->popup()->hide();
00929 return;
00930 }
00931
00932
00933 QRect cr = cursorRect();
00934 cr.setWidth(completer->popup()->sizeHintForColumn(0) + completer->popup()->verticalScrollBar()->sizeHint().width());
00935 completer->complete(cr);
00936 }
00937
00938 void AeslEditor::insertCompletion(const QString& completion)
00939 {
00940 QTextCursor tc = textCursor();
00941
00942 tc.movePosition(QTextCursor::Left);
00943 tc.movePosition(QTextCursor::EndOfWord);
00944
00945 tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, completer->completionPrefix().length());
00946 tc.removeSelectedText();
00947
00948 tc.insertText(completion);
00949 setTextCursor(tc);
00950 }
00951
00952 QString AeslEditor::textUnderCursor() const
00953 {
00954 QTextCursor tc = textCursor();
00955
00956 tc.movePosition(QTextCursor::EndOfWord);
00957 int endPosition = tc.position();
00958
00959 tc.movePosition(QTextCursor::StartOfWord);
00960 while( (document()->characterAt(tc.position() - 1)) == QChar('.') )
00961 {
00962 tc.movePosition(QTextCursor::Left);
00963 tc.movePosition(QTextCursor::WordLeft);
00964 }
00965
00966 tc.setPosition(endPosition, QTextCursor::KeepAnchor);
00967 return tc.selectedText();
00968 }
00969
00970 QString AeslEditor::previousWord() const
00971 {
00972 QTextCursor tc = textCursor();
00973 tc.movePosition(QTextCursor::WordLeft);
00974 tc.movePosition(QTextCursor::PreviousWord);
00975 tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00976 return tc.selectedText();
00977 }
00978
00979 QString AeslEditor::currentLine() const
00980 {
00981
00982 QTextCursor tc = textCursor();
00983 tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
00984 return tc.selectedText();
00985 }
00986
00987 void AeslEditor::setCompleterModel(QAbstractItemModel *model)
00988 {
00989 completer->setModel(model);
00990 completer->setCompletionRole(Qt::DisplayRole);
00991 }
00992
00993
00995 };