00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "PreCompiled.h"
00025
00026 #ifndef _PreComp_
00027 # include <QKeyEvent>
00028 # include <QPainter>
00029 # include <QShortcut>
00030 # include <QTextCursor>
00031 #endif
00032
00033 #include "TextEdit.h"
00034 #include "SyntaxHighlighter.h"
00035
00036 using namespace Gui;
00037
00041 TextEdit::TextEdit(QWidget* parent)
00042 : QPlainTextEdit(parent), listBox(0)
00043 {
00044
00045
00046 QShortcut* shortcut = new QShortcut(this);
00047 shortcut->setKey(Qt::CTRL+Qt::Key_Space);
00048 shortcut->setContext(Qt::WidgetShortcut);
00049 connect(shortcut, SIGNAL(activated()), this, SLOT(complete()));
00050 }
00051
00053 TextEdit::~TextEdit()
00054 {
00055 }
00056
00060 void TextEdit::keyPressEvent(QKeyEvent* e)
00061 {
00062 QPlainTextEdit::keyPressEvent(e);
00063
00064
00065 if (listBox && listBox->isVisible()) {
00066
00067 QTextCursor cursor = textCursor();
00068 cursor.movePosition(QTextCursor::StartOfWord);
00069
00070 if (cursor.position() < cursorPosition-wordPrefix.length() ||
00071 cursor.position() > cursorPosition) {
00072 listBox->hide();
00073 return;
00074 }
00075 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00076 listBox->keyboardSearch(cursor.selectedText());
00077 cursor.clearSelection();
00078 }
00079 }
00080
00084 void TextEdit::complete()
00085 {
00086 QTextBlock block = textCursor().block();
00087 if (!block.isValid())
00088 return;
00089 int cursorPos = textCursor().position()-block.position();
00090 QString para = block.text();
00091 int wordStart = cursorPos;
00092 while (wordStart > 0 && para[wordStart - 1].isLetterOrNumber())
00093 --wordStart;
00094 wordPrefix = para.mid(wordStart, cursorPos - wordStart);
00095 if (wordPrefix.isEmpty())
00096 return;
00097
00098 QStringList list = toPlainText().split(QRegExp(QLatin1String("\\W+")));
00099 QMap<QString, QString> map;
00100 QStringList::Iterator it = list.begin();
00101 while (it != list.end()) {
00102 if ((*it).startsWith(wordPrefix) && (*it).length() > wordPrefix.length())
00103 map[(*it).toLower()] = *it;
00104 ++it;
00105 }
00106
00107 if (map.count() == 1) {
00108 insertPlainText((*map.begin()).mid(wordPrefix.length()));
00109 } else if (map.count() > 1) {
00110 if (!listBox)
00111 createListBox();
00112 listBox->clear();
00113 listBox->addItems(map.values());
00114 listBox->setFont(QFont(font().family(), 8));
00115
00116 this->cursorPosition = textCursor().position();
00117
00118
00119 int h = 0;
00120 int w = 0;
00121 for (int i = 0; i < listBox->count(); ++i) {
00122 QRect r = listBox->visualItemRect(listBox->item(i));
00123 w = qMax(w, r.width());
00124 h += r.height();
00125 }
00126
00127
00128 w += 2*listBox->frameWidth();
00129 h += 2*listBox->frameWidth();
00130
00131
00132 QTextCursor cursor = textCursor();
00133 cursor.movePosition(QTextCursor::StartOfWord);
00134 QRect rect = cursorRect(cursor);
00135 int posX = rect.x();
00136 int posY = rect.y();
00137 int boxH = h;
00138
00139
00140 if (posY > viewport()->height()/2) {
00141 h = qMin(qMin(h,posY), 250);
00142 if (h < boxH)
00143 w += style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00144 listBox->setGeometry(posX,posY-h, w, h);
00145 } else {
00146 h = qMin(qMin(h,viewport()->height()-fontMetrics().height()-posY), 250);
00147 if (h < boxH)
00148 w += style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00149 listBox->setGeometry(posX, posY+fontMetrics().height(), w, h);
00150 }
00151
00152 listBox->setCurrentRow(0);
00153 listBox->show();
00154 }
00155 }
00156
00162 void TextEdit::createListBox()
00163 {
00164 listBox = new CompletionList(this);
00165 listBox->setFrameStyle(QFrame::Box|QFrame::Raised);
00166 listBox->setLineWidth(2);
00167 installEventFilter(listBox);
00168 viewport()->installEventFilter(listBox);
00169 listBox->setSelectionMode( QAbstractItemView::SingleSelection );
00170 listBox->hide();
00171 }
00172
00173
00174
00175 namespace Gui {
00176 struct TextEditorP
00177 {
00178 QMap<QString, QColor> colormap;
00179 TextEditorP()
00180 {
00181 colormap[QLatin1String("Text")] = Qt::black;
00182 colormap[QLatin1String("Bookmark")] = Qt::cyan;
00183 colormap[QLatin1String("Breakpoint")] = Qt::red;
00184 colormap[QLatin1String("Keyword")] = Qt::blue;
00185 colormap[QLatin1String("Comment")] = QColor(0, 170, 0);
00186 colormap[QLatin1String("Block comment")] = QColor(160, 160, 164);
00187 colormap[QLatin1String("Number")] = Qt::blue;
00188 colormap[QLatin1String("String")] = Qt::red;
00189 colormap[QLatin1String("Character")] = Qt::red;
00190 colormap[QLatin1String("Class name")] = QColor(255, 170, 0);
00191 colormap[QLatin1String("Define name")] = QColor(255, 170, 0);
00192 colormap[QLatin1String("Operator")] = QColor(160, 160, 164);
00193 colormap[QLatin1String("Python output")] = QColor(170, 170, 127);
00194 colormap[QLatin1String("Python error")] = Qt::red;
00195 colormap[QLatin1String("Line")] = QColor(224,224,224);
00196 }
00197 };
00198 }
00199
00204 TextEditor::TextEditor(QWidget* parent)
00205 : TextEdit(parent), WindowParameter("Editor"), highlighter(0)
00206 {
00207 d = new TextEditorP();
00208 lineNumberArea = new LineMarker(this);
00209
00210 QFont serifFont(QLatin1String("Courier"), 10, QFont::Normal);
00211 setFont(serifFont);
00212
00213 ParameterGrp::handle hPrefGrp = getWindowParameter();
00214
00215 hPrefGrp->SetInt( "TabSize", 4 );
00216 hPrefGrp->Attach( this );
00217
00218
00219 hPrefGrp->NotifyAll();
00220
00221 connect(this, SIGNAL(cursorPositionChanged()),
00222 this, SLOT(highlightCurrentLine()));
00223 connect(this, SIGNAL(blockCountChanged(int)),
00224 this, SLOT(updateLineNumberAreaWidth(int)));
00225 connect(this, SIGNAL(updateRequest(const QRect &, int)),
00226 this, SLOT(updateLineNumberArea(const QRect &, int)));
00227
00228 updateLineNumberAreaWidth(0);
00229 highlightCurrentLine();
00230 }
00231
00233 TextEditor::~TextEditor()
00234 {
00235 getWindowParameter()->Detach(this);
00236 delete highlighter;
00237 delete d;
00238 }
00239
00240 int TextEditor::lineNumberAreaWidth()
00241 {
00242 return fontMetrics().width(QLatin1String("0000"))+10;
00243 }
00244
00245 void TextEditor::updateLineNumberAreaWidth(int )
00246 {
00247 setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
00248 }
00249
00250 void TextEditor::updateLineNumberArea(const QRect &rect, int dy)
00251 {
00252 if (dy)
00253 lineNumberArea->scroll(0, dy);
00254 else
00255 lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
00256
00257 if (rect.contains(viewport()->rect()))
00258 updateLineNumberAreaWidth(0);
00259 }
00260
00261 void TextEditor::resizeEvent(QResizeEvent *e)
00262 {
00263 QPlainTextEdit::resizeEvent(e);
00264
00265 QRect cr = contentsRect();
00266 lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
00267 }
00268
00269 void TextEditor::highlightCurrentLine()
00270 {
00271 QList<QTextEdit::ExtraSelection> extraSelections;
00272
00273 if (!isReadOnly()) {
00274 QTextEdit::ExtraSelection selection;
00275 QColor lineColor = d->colormap[QLatin1String("Line")];
00276
00277 selection.format.setBackground(lineColor);
00278 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
00279 selection.cursor = textCursor();
00280 selection.cursor.clearSelection();
00281 extraSelections.append(selection);
00282 }
00283
00284 setExtraSelections(extraSelections);
00285 }
00286
00287 void TextEditor::drawMarker(int line, int x, int y, QPainter* p)
00288 {
00289 }
00290
00291 void TextEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
00292 {
00293 QPainter painter(lineNumberArea);
00294
00295
00296 QTextBlock block = firstVisibleBlock();
00297 int blockNumber = block.blockNumber();
00298 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
00299 int bottom = top + (int) blockBoundingRect(block).height();
00300
00301 while (block.isValid() && top <= event->rect().bottom()) {
00302 if (block.isVisible() && bottom >= event->rect().top()) {
00303 QString number = QString::number(blockNumber + 1);
00304 painter.setPen(Qt::black);
00305 painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
00306 Qt::AlignRight, number);
00307 drawMarker(blockNumber + 1, 1, top, &painter);
00308 }
00309
00310 block = block.next();
00311 top = bottom;
00312 bottom = top + (int) blockBoundingRect(block).height();
00313 ++blockNumber;
00314 }
00315 }
00316
00317 void TextEditor::setSyntaxHighlighter(SyntaxHighlighter* sh)
00318 {
00319 sh->setDocument(this->document());
00320 this->highlighter = sh;
00321 }
00322
00323 void TextEditor::keyPressEvent (QKeyEvent * e)
00324 {
00325 if ( e->key() == Qt::Key_Tab ) {
00326 ParameterGrp::handle hPrefGrp = getWindowParameter();
00327 int indent = hPrefGrp->GetInt( "IndentSize", 4 );
00328 bool space = hPrefGrp->GetBool( "Spaces", false );
00329 QString ch = space ? QString(indent, QLatin1Char(' '))
00330 : QString::fromAscii("\t");
00331
00332 QTextCursor cursor = textCursor();
00333 if (!cursor.hasSelection()) {
00334
00335 cursor.beginEditBlock();
00336 cursor.insertText(ch);
00337 cursor.endEditBlock();
00338 } else {
00339
00340 int selStart = cursor.selectionStart();
00341 int selEnd = cursor.selectionEnd();
00342 QTextBlock block;
00343 cursor.beginEditBlock();
00344 for (block = document()->begin(); block.isValid(); block = block.next()) {
00345 int pos = block.position();
00346 int off = block.length()-1;
00347
00348 if ( pos >= selStart || pos+off >= selStart) {
00349 if ( pos+1 > selEnd )
00350 break;
00351 cursor.setPosition(block.position());
00352 cursor.insertText(ch);
00353 selEnd += ch.length();
00354 }
00355 }
00356
00357 cursor.endEditBlock();
00358 }
00359
00360 return;
00361 }
00362 else if (e->key() == Qt::Key_Backtab) {
00363 QTextCursor cursor = textCursor();
00364 if (!cursor.hasSelection())
00365 return;
00366
00367
00368 ParameterGrp::handle hPrefGrp = getWindowParameter();
00369 int indent = hPrefGrp->GetInt( "IndentSize", 4 );
00370
00371 int selStart = cursor.selectionStart();
00372 int selEnd = cursor.selectionEnd();
00373 QTextBlock block;
00374 cursor.beginEditBlock();
00375 for (block = document()->begin(); block.isValid(); block = block.next()) {
00376 int pos = block.position();
00377 int off = block.length()-1;
00378
00379 if ( pos >= selStart || pos+off >= selStart) {
00380 if ( pos+1 > selEnd )
00381 break;
00382
00383 QString text = block.text();
00384 if (text.startsWith(QLatin1String("\t"))) {
00385 cursor.setPosition(block.position());
00386 cursor.deleteChar();
00387 selEnd--;
00388 }
00389 else {
00390 cursor.setPosition(block.position());
00391 for (int i=0; i<indent; i++) {
00392 if (!text.startsWith(QLatin1String(" ")))
00393 break;
00394 text = text.mid(1);
00395 cursor.deleteChar();
00396 selEnd--;
00397 }
00398 }
00399 }
00400 }
00401
00402 cursor.endEditBlock();
00403 return;
00404 }
00405
00406 TextEdit::keyPressEvent( e );
00407 }
00408
00410 void TextEditor::OnChange(Base::Subject<const char*> &rCaller,const char* sReason)
00411 {
00412 ParameterGrp::handle hPrefGrp = getWindowParameter();
00413 if (strcmp(sReason, "FontSize") == 0 || strcmp(sReason, "Font") == 0) {
00414 #ifdef FC_OS_LINUX
00415 int fontSize = hPrefGrp->GetInt("FontSize", 15);
00416 #else
00417 int fontSize = hPrefGrp->GetInt("FontSize", 10);
00418 #endif
00419 QString fontFamily = QString::fromAscii(hPrefGrp->GetASCII( "Font", "Courier" ).c_str());
00420
00421 QFont font(fontFamily, fontSize);
00422 setFont(font);
00423 } else {
00424 QMap<QString, QColor>::ConstIterator it = d->colormap.find(QString::fromAscii(sReason));
00425 if (it != d->colormap.end()) {
00426 QColor color = it.value();
00427 unsigned long col = (color.red() << 24) | (color.green() << 16) | (color.blue() << 8);
00428 col = hPrefGrp->GetUnsigned( sReason, col);
00429 color.setRgb((col>>24)&0xff, (col>>16)&0xff, (col>>8)&0xff);
00430 if (this->highlighter)
00431 this->highlighter->setColor(QLatin1String(sReason), color);
00432 }
00433 }
00434
00435 if (strcmp(sReason, "TabSize") == 0 || strcmp(sReason, "FontSize") == 0) {
00436 int tabWidth = hPrefGrp->GetInt("TabSize", 4);
00437 QFontMetrics metric(font());
00438 int fontSize = metric.width(QLatin1String("0"));
00439 setTabStopWidth(tabWidth * fontSize);
00440 }
00441 }
00442
00443 void TextEditor::paintEvent (QPaintEvent * e)
00444 {
00445 TextEdit::paintEvent( e );
00446 }
00447
00448
00449
00450 LineMarker::LineMarker(TextEditor* editor)
00451 : QWidget(editor), textEditor(editor)
00452 {
00453 }
00454
00455 LineMarker::~LineMarker()
00456 {
00457 }
00458
00459 QSize LineMarker::sizeHint() const
00460 {
00461 return QSize(textEditor->lineNumberAreaWidth(), 0);
00462 }
00463
00464 void LineMarker::paintEvent(QPaintEvent* e)
00465 {
00466 textEditor->lineNumberAreaPaintEvent(e);
00467 }
00468
00469
00470
00471 CompletionList::CompletionList(QPlainTextEdit* parent)
00472 : QListWidget(parent), textEdit(parent)
00473 {
00474
00475 QPalette pal = parent->palette();
00476 pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
00477 pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
00478 parent->setPalette( pal );
00479
00480 connect(this, SIGNAL(itemActivated(QListWidgetItem *)),
00481 this, SLOT(completionItem(QListWidgetItem *)));
00482 }
00483
00484 CompletionList::~CompletionList()
00485 {
00486 }
00487
00488 void CompletionList::findCurrentWord(const QString& wordPrefix)
00489 {
00490 for (int i=0; i<count(); ++i) {
00491 QString text = item(i)->text();
00492 if (text.startsWith(wordPrefix)) {
00493 setCurrentRow(i);
00494 return;
00495 }
00496 }
00497
00498 setItemSelected(currentItem(), false);
00499 }
00500
00505 bool CompletionList::eventFilter(QObject * watched, QEvent * event)
00506 {
00507 if (isVisible() && watched == textEdit->viewport()) {
00508 if (event->type() == QEvent::MouseButtonPress)
00509 hide();
00510 } else if (isVisible() && watched == textEdit) {
00511 if (event->type() == QEvent::KeyPress) {
00512 QKeyEvent* ke = (QKeyEvent*)event;
00513 if (ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down) {
00514 keyPressEvent(ke);
00515 return true;
00516 } else if (ke->key() == Qt::Key_PageUp || ke->key() == Qt::Key_PageDown) {
00517 keyPressEvent(ke);
00518 return true;
00519 } else if (ke->key() == Qt::Key_Escape) {
00520 hide();
00521 return true;
00522 } else if (ke->key() == Qt::Key_Space) {
00523 hide();
00524 return false;
00525 } else if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
00526 itemActivated(currentItem());
00527 return true;
00528 }
00529 } else if (event->type() == QEvent::FocusOut) {
00530 if (!hasFocus())
00531 hide();
00532 }
00533 }
00534
00535 return QListWidget::eventFilter(watched, event);
00536 }
00537
00542 void CompletionList::completionItem(QListWidgetItem *item)
00543 {
00544 hide();
00545 QString text = item->text();
00546 QTextCursor cursor = textEdit->textCursor();
00547 cursor.movePosition(QTextCursor::StartOfWord);
00548 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00549 cursor.insertText( text );
00550 textEdit->ensureCursorVisible();
00551 }
00552
00553 #include "moc_TextEdit.cpp"