![]() |
TinkerCell Core 1.0
TinkerCell's Core library providing all basic functionalities
|
00001 /**************************************************************************** 00002 This file is a combination of two example programs included in the Qt Toolkit. 00003 A few modifications have been added, but the majority of the code is from Qt's 00004 demo programs 00005 ****************************************************************************/ 00006 00007 #include <QtGui> 00008 #include <QPushButton> 00009 #include <QVBoxLayout> 00010 #include <QHBoxLayout> 00011 00012 #include "CodeEditor.h" 00013 00014 namespace Tinkercell 00015 { 00016 00017 void CodeEditor::setCompleter(QCompleter *completer) 00018 { 00019 if (c) 00020 QObject::disconnect(c, 0, this, 0); 00021 00022 c = completer; 00023 00024 if (!c) 00025 return; 00026 00027 c->setWidget(this); 00028 c->setCompletionMode(QCompleter::PopupCompletion); 00029 c->setCaseSensitivity(Qt::CaseInsensitive); 00030 QObject::connect(c, SIGNAL(activated(const QString&)), 00031 this, SLOT(insertCompletion(const QString&))); 00032 } 00033 00034 QCompleter *CodeEditor::completer() const 00035 { 00036 return c; 00037 } 00038 00039 void CodeEditor::insertCompletion(const QString& completion) 00040 { 00041 if (c->widget() != this) 00042 return; 00043 QTextCursor tc = textCursor(); 00044 int extra = completion.length() - c->completionPrefix().length(); 00045 tc.movePosition(QTextCursor::Left); 00046 tc.movePosition(QTextCursor::EndOfWord); 00047 tc.insertText(completion.right(extra)); 00048 setTextCursor(tc); 00049 } 00050 00051 QString CodeEditor::textUnderCursor() const 00052 { 00053 QTextCursor tc = textCursor(); 00054 tc.select(QTextCursor::WordUnderCursor); 00055 return tc.selectedText(); 00056 } 00057 00058 void CodeEditor::focusInEvent(QFocusEvent *e) 00059 { 00060 if (c) 00061 c->setWidget(this); 00062 QPlainTextEdit::focusInEvent(e); 00063 } 00064 00065 void CodeEditor::keyPressEvent(QKeyEvent *e) 00066 { 00067 if (c && c->popup()->isVisible()) { 00068 // The following keys are forwarded by the completer to the widget 00069 switch (e->key()) { 00070 case Qt::Key_Enter: 00071 case Qt::Key_Return: 00072 case Qt::Key_Escape: 00073 case Qt::Key_Tab: 00074 case Qt::Key_Backtab: 00075 e->ignore(); 00076 return; // let the completer do default behavior 00077 default: 00078 break; 00079 } 00080 } 00081 00082 if (e->modifiers() & Qt::ControlModifier) 00083 { 00084 if (e->key() == Qt::Key_Equal || e->key() == Qt::Key_Plus) //zoom in 00085 { 00086 zoomIn(); 00087 return; 00088 } 00089 if (e->key() == Qt::Key_Minus || e->key() == Qt::Key_Underscore) //zoom out 00090 { 00091 zoomOut(); 00092 return; 00093 } 00094 } 00095 00096 QString space; 00097 if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) 00098 { 00099 QString s = textCursor().block().text(); 00100 int i = 0; 00101 for (i=0; i < s.length() && s[i].isSpace(); ++i) { } 00102 if (i > 0) 00103 space = s.left(i); 00104 } 00105 00106 bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E 00107 if (!c || !isShortcut) // dont process the shortcut when we have a completer 00108 QPlainTextEdit::keyPressEvent(e); 00109 00110 if (!space.isEmpty()) 00111 { 00112 QTextCursor cursor = textCursor(); 00113 cursor.insertText(space); 00114 } 00115 00116 const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); 00117 if (!c || (ctrlOrShift && e->text().isEmpty())) 00118 return; 00119 00120 static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word 00121 bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; 00122 QString completionPrefix = textUnderCursor(); 00123 00124 if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3 00125 || eow.contains(e->text().right(1)))) { 00126 c->popup()->hide(); 00127 return; 00128 } 00129 00130 if (completionPrefix != c->completionPrefix()) { 00131 c->setCompletionPrefix(completionPrefix); 00132 c->popup()->setCurrentIndex(c->completionModel()->index(0, 0)); 00133 } 00134 QRect cr = cursorRect(); 00135 cr.setWidth(c->popup()->sizeHintForColumn(0) 00136 + c->popup()->verticalScrollBar()->sizeHint().width()); 00137 c->complete(cr); // popup it up! 00138 } 00139 00140 00141 CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent), c(0) 00142 { 00143 lineNumberArea = new LineNumberArea(this); 00144 setTabStopWidth ( 8 ); 00145 lineHighlightColor = QColor(tr("#FCFFAE")); 00146 lineNumberBackground = QColor(tr("#E1E1E1")); 00147 lineNumberText = QColor(tr("#002446")); 00148 00149 connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); 00150 connect(this, SIGNAL(updateRequest(const QRect &, int)), this, SLOT(updateLineNumberArea(const QRect &, int))); 00151 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); 00152 00153 updateLineNumberAreaWidth(0); 00154 highlightCurrentLine(); 00155 00156 /* create find replace dialog */ 00157 findReplaceDialog = new QDialog(this); 00158 findLineEdit = new QLineEdit; 00159 replaceLineEdit = new QLineEdit; 00160 QPushButton * findButton = new QPushButton("&Find"); 00161 QPushButton * replaceButton = new QPushButton("&Replace"); 00162 QPushButton * closeButton = new QPushButton("&Cancel"); 00163 00164 QHBoxLayout * hlayout; 00165 QVBoxLayout * vlayout = new QVBoxLayout; 00166 00167 hlayout = new QHBoxLayout; 00168 hlayout->addWidget(new QLabel(" find: ")); 00169 hlayout->addWidget(findLineEdit); 00170 vlayout->addLayout(hlayout,0); 00171 00172 hlayout = new QHBoxLayout; 00173 hlayout->addWidget(new QLabel("replace: ")); 00174 hlayout->addWidget(replaceLineEdit); 00175 vlayout->addLayout(hlayout,0); 00176 00177 hlayout = new QHBoxLayout; 00178 hlayout->addSpacing(1); 00179 hlayout->addWidget(findButton); 00180 hlayout->addWidget(replaceButton); 00181 hlayout->addWidget(closeButton); 00182 hlayout->addSpacing(1); 00183 vlayout->addLayout(hlayout,0); 00184 00185 findReplaceDialog->setLayout(vlayout); 00186 00187 connect(findButton, SIGNAL(pressed()), this, SLOT(find())); 00188 connect(replaceButton, SIGNAL(pressed()), this, SLOT(replace())); 00189 connect(closeButton, SIGNAL(pressed()), findReplaceDialog, SLOT(reject())); 00190 } 00191 00192 int CodeEditor::lineNumberAreaWidth() 00193 { 00194 int digits = 1; 00195 int max = qMax(1, blockCount()); 00196 while (max >= 10) { 00197 max /= 10; 00198 ++digits; 00199 } 00200 00201 int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; 00202 00203 return space; 00204 } 00205 00206 00207 00208 void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) 00209 { 00210 setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); 00211 } 00212 00213 00214 00215 void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) 00216 { 00217 if (dy) 00218 lineNumberArea->scroll(0, dy); 00219 else 00220 lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height()); 00221 00222 if (rect.contains(viewport()->rect())) 00223 updateLineNumberAreaWidth(0); 00224 } 00225 00226 00227 00228 void CodeEditor::resizeEvent(QResizeEvent *e) 00229 { 00230 QPlainTextEdit::resizeEvent(e); 00231 00232 QRect cr = contentsRect(); 00233 lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); 00234 } 00235 00236 00237 00238 void CodeEditor::highlightCurrentLine() 00239 { 00240 QList<QTextEdit::ExtraSelection> extraSelections; 00241 00242 if (!isReadOnly()) { 00243 QTextEdit::ExtraSelection selection; 00244 00245 selection.format.setBackground(lineHighlightColor); 00246 selection.format.setProperty(QTextFormat::FullWidthSelection, true); 00247 selection.cursor = textCursor(); 00248 selection.cursor.clearSelection(); 00249 extraSelections.append(selection); 00250 } 00251 00252 setExtraSelections(extraSelections); 00253 } 00254 00255 00256 00257 void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) 00258 { 00259 QPainter painter(lineNumberArea); 00260 painter.fillRect(event->rect(), lineNumberBackground); 00261 00262 00263 QTextBlock block = firstVisibleBlock(); 00264 int blockNumber = block.blockNumber(); 00265 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); 00266 int bottom = top + (int) blockBoundingRect(block).height(); 00267 00268 while (block.isValid() && top <= event->rect().bottom()) { 00269 if (block.isVisible() && bottom >= event->rect().top()) { 00270 QString number = QString::number(blockNumber + 1); 00271 painter.setPen(lineNumberText); 00272 painter.setFont(this->font()); 00273 painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(), 00274 Qt::AlignRight, number); 00275 } 00276 00277 block = block.next(); 00278 top = bottom; 00279 bottom = top + (int) blockBoundingRect(block).height(); 00280 ++blockNumber; 00281 } 00282 } 00283 00293 void CodeEditor::zoomIn(int range) 00294 { 00295 QFont f = font(); 00296 const int newSize = f.pointSize() + range; 00297 if (newSize <= 0) 00298 return; 00299 f.setPointSize(newSize); 00300 setFont(f); 00301 } 00302 00314 void CodeEditor::zoomOut(int range) 00315 { 00316 zoomIn(-range); 00317 } 00318 00319 void CodeEditor::wheelEvent ( QWheelEvent * wheelEvent ) 00320 { 00321 if (wheelEvent == 0) return; 00322 00323 if (wheelEvent->modifiers() == Qt::ControlModifier) 00324 { 00325 if (wheelEvent->delta() > 0) 00326 zoomIn(); 00327 else 00328 zoomOut(); 00329 } 00330 else 00331 { 00332 QPlainTextEdit::wheelEvent(wheelEvent); 00333 } 00334 } 00335 00336 QString CodeEditor::text() const 00337 { 00338 return toPlainText(); 00339 } 00340 00341 void CodeEditor::setText(const QString & s) 00342 { 00343 setPlainText(s); 00344 } 00345 00346 bool CodeEditor::find(const QString & s) 00347 { 00348 return QPlainTextEdit::find(s); 00349 } 00350 00351 bool CodeEditor::replace(const QString& old_string, const QString& new_string) 00352 { 00353 QString text = toPlainText(); 00354 QString oldText = text; 00355 if (!text.contains(old_string)) return false; 00356 text.replace(old_string,new_string); 00357 setPlainText(text); 00358 return true; 00359 } 00360 00361 void CodeEditor::find() 00362 { 00363 if (findLineEdit) 00364 find(findLineEdit->text()); 00365 } 00366 00367 void CodeEditor::replace() 00368 { 00369 if (findLineEdit && replaceLineEdit) 00370 replace(findLineEdit->text(),replaceLineEdit->text()); 00371 } 00372 00373 void CodeEditor::showFindReplaceDialog() 00374 { 00375 if (findReplaceDialog) 00376 findReplaceDialog->show(); 00377 } 00378 } 00379