TinkerCell Core 1.0
TinkerCell's Core library providing all basic functionalities
CodeEditor.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines