OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / texteditor / basetexteditor.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "texteditor_global.h"
34
35 #include "basetextdocument.h"
36 #include "basetextdocumentlayout.h"
37 #include "basetexteditor_p.h"
38 #include "behaviorsettings.h"
39 #include "codecselector.h"
40 #include "completionsettings.h"
41 #include "completionsupport.h"
42 #include "tabsettings.h"
43 #include "texteditorconstants.h"
44 #include "texteditorplugin.h"
45 #include "syntaxhighlighter.h"
46 #include "tooltip.h"
47 #include "tipcontents.h"
48 #include "indenter.h"
49 #include "autocompleter.h"
50 #include "snippet.h"
51
52 #include <aggregation/aggregate.h>
53 #include <coreplugin/actionmanager/actionmanager.h>
54 #include <coreplugin/actionmanager/actioncontainer.h>
55 #include <coreplugin/actionmanager/command.h>
56 #include <coreplugin/coreconstants.h>
57 #include <coreplugin/editormanager/editormanager.h>
58 #include <coreplugin/icore.h>
59 #include <coreplugin/manhattanstyle.h>
60 #include <coreplugin/uniqueidmanager.h>
61 #include <extensionsystem/pluginmanager.h>
62 #include <find/basetextfind.h>
63 #include <utils/linecolumnlabel.h>
64 #include <utils/qtcassert.h>
65 #include <utils/stylehelper.h>
66
67 #include <QtCore/QCoreApplication>
68 #include <QtCore/QTextCodec>
69 #include <QtCore/QFile>
70 #include <QtCore/QDebug>
71 #include <QtCore/QTimer>
72 #include <QtCore/QTimeLine>
73 #include <QtCore/QTime>
74 #include <QtGui/QAbstractTextDocumentLayout>
75 #include <QtGui/QApplication>
76 #include <QtGui/QKeyEvent>
77 #include <QtGui/QLabel>
78 #include <QtGui/QLayout>
79 #include <QtGui/QPainter>
80 #include <QtGui/QPrinter>
81 #include <QtGui/QPrintDialog>
82 #include <QtGui/QScrollBar>
83 #include <QtGui/QShortcut>
84 #include <QtGui/QStyle>
85 #include <QtGui/QSyntaxHighlighter>
86 #include <QtGui/QTextCursor>
87 #include <QtGui/QTextDocumentFragment>
88 #include <QtGui/QTextBlock>
89 #include <QtGui/QTextLayout>
90 #include <QtGui/QToolBar>
91 #include <QtGui/QInputDialog>
92 #include <QtGui/QMenu>
93
94 //#define DO_FOO
95
96 using namespace TextEditor;
97 using namespace TextEditor::Internal;
98
99 namespace TextEditor {
100 namespace Internal {
101
102 class TextEditExtraArea : public QWidget {
103     BaseTextEditorWidget *textEdit;
104 public:
105     TextEditExtraArea(BaseTextEditorWidget *edit):QWidget(edit) {
106         textEdit = edit;
107         setAutoFillBackground(true);
108     }
109 public:
110
111     QSize sizeHint() const {
112         return QSize(textEdit->extraAreaWidth(), 0);
113     }
114 protected:
115     void paintEvent(QPaintEvent *event){
116         textEdit->extraAreaPaintEvent(event);
117     }
118     void mousePressEvent(QMouseEvent *event){
119         textEdit->extraAreaMouseEvent(event);
120     }
121     void mouseMoveEvent(QMouseEvent *event){
122         textEdit->extraAreaMouseEvent(event);
123     }
124     void mouseReleaseEvent(QMouseEvent *event){
125         textEdit->extraAreaMouseEvent(event);
126     }
127     void leaveEvent(QEvent *event){
128         textEdit->extraAreaLeaveEvent(event);
129     }
130
131     void wheelEvent(QWheelEvent *event) {
132         QCoreApplication::sendEvent(textEdit->viewport(), event);
133     }
134 };
135
136 } // namespace Internal
137 } // namespace TextEditor
138
139 ITextEditor *BaseTextEditorWidget::openEditorAt(const QString &fileName, int line, int column,
140                                  const QString &editorKind,
141                                  Core::EditorManager::OpenEditorFlags flags,
142                                  bool *newEditor)
143 {
144     Core::EditorManager *editorManager = Core::EditorManager::instance();
145     editorManager->cutForwardNavigationHistory();
146     editorManager->addCurrentPositionToNavigationHistory();
147     Core::IEditor *editor = editorManager->openEditor(fileName, editorKind,
148             flags, newEditor);
149     TextEditor::ITextEditor *texteditor = qobject_cast<TextEditor::ITextEditor *>(editor);
150     if (texteditor) {
151         texteditor->gotoLine(line, column);
152         return texteditor;
153     }
154
155     return 0;
156 }
157
158 static void convertToPlainText(QString &txt)
159 {
160     QChar *uc = txt.data();
161     QChar *e = uc + txt.size();
162
163     for (; uc != e; ++uc) {
164         switch (uc->unicode()) {
165         case 0xfdd0: // QTextBeginningOfFrame
166         case 0xfdd1: // QTextEndOfFrame
167         case QChar::ParagraphSeparator:
168         case QChar::LineSeparator:
169             *uc = QLatin1Char('\n');
170             break;
171         case QChar::Nbsp:
172             *uc = QLatin1Char(' ');
173             break;
174         default:
175             ;
176         }
177     }
178 }
179
180 BaseTextEditorWidget::BaseTextEditorWidget(QWidget *parent)
181     : QPlainTextEdit(parent)
182 {
183     d = new BaseTextEditorPrivate;
184     d->q = this;
185     d->m_extraArea = new TextEditExtraArea(this);
186     d->m_extraArea->setMouseTracking(true);
187     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
188
189     d->m_overlay = new TextEditorOverlay(this);
190     d->m_snippetOverlay = new TextEditorOverlay(this);
191     d->m_searchResultOverlay = new TextEditorOverlay(this);
192     d->m_refactorOverlay = new RefactorOverlay(this);
193
194     d->setupDocumentSignals(d->m_document);
195
196     d->m_lastScrollPos = -1;
197
198     // from RESEARCH
199
200     setLayoutDirection(Qt::LeftToRight);
201     viewport()->setMouseTracking(true);
202     d->extraAreaSelectionAnchorBlockNumber
203         = d->extraAreaToggleMarkBlockNumber
204         = d->extraAreaHighlightFoldedBlockNumber
205         = -1;
206
207     d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber = -1;
208
209     connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateExtraAreaWidth()));
210     connect(this, SIGNAL(modificationChanged(bool)), this, SLOT(slotModificationChanged(bool)));
211     connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged()));
212     connect(this, SIGNAL(updateRequest(QRect, int)), this, SLOT(slotUpdateRequest(QRect, int)));
213     connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
214
215 //     (void) new QShortcut(tr("CTRL+L"), this, SLOT(centerCursor()), 0, Qt::WidgetShortcut);
216 //     (void) new QShortcut(tr("F9"), this, SLOT(slotToggleMark()), 0, Qt::WidgetShortcut);
217 //     (void) new QShortcut(tr("F11"), this, SLOT(slotToggleBlockVisible()));
218
219 #ifdef DO_FOO
220     (void) new QShortcut(tr("CTRL+D"), this, SLOT(doFoo()));
221 #endif
222
223
224     // parentheses matcher
225     d->m_formatRange = true;
226     d->m_matchFormat.setForeground(Qt::red);
227     d->m_rangeFormat.setBackground(QColor(0xb4, 0xee, 0xb4));
228     d->m_mismatchFormat.setBackground(Qt::magenta);
229     d->m_parenthesesMatchingTimer = new QTimer(this);
230     d->m_parenthesesMatchingTimer->setSingleShot(true);
231     connect(d->m_parenthesesMatchingTimer, SIGNAL(timeout()), this, SLOT(_q_matchParentheses()));
232
233     d->m_highlightBlocksTimer = new QTimer(this);
234     d->m_highlightBlocksTimer->setSingleShot(true);
235     connect(d->m_highlightBlocksTimer, SIGNAL(timeout()), this, SLOT(_q_highlightBlocks()));
236
237     d->m_requestAutoCompletionTimer = new QTimer(this);
238     d->m_requestAutoCompletionTimer->setSingleShot(true);
239     d->m_requestAutoCompletionTimer->setInterval(500);
240     connect(d->m_requestAutoCompletionTimer, SIGNAL(timeout()), this, SLOT(_q_requestAutoCompletion()));
241
242     d->m_animator = 0;
243
244     d->m_searchResultFormat.setBackground(QColor(0xffef0b));
245
246     slotUpdateExtraAreaWidth();
247     updateHighlights();
248     setFrameStyle(QFrame::NoFrame);
249
250     d->m_delayedUpdateTimer = new QTimer(this);
251     d->m_delayedUpdateTimer->setSingleShot(true);
252     connect(d->m_delayedUpdateTimer, SIGNAL(timeout()), viewport(), SLOT(update()));
253
254     connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
255             this, SLOT(currentEditorChanged(Core::IEditor*)));
256
257     d->m_moveLineUndoHack = false;
258 }
259
260 BaseTextEditorWidget::~BaseTextEditorWidget()
261 {
262     delete d;
263     d = 0;
264 }
265
266 QString BaseTextEditorWidget::mimeType() const
267 {
268     return d->m_document->mimeType();
269 }
270
271 void BaseTextEditorWidget::setMimeType(const QString &mt)
272 {
273     d->m_document->setMimeType(mt);
274 }
275
276 void BaseTextEditorWidget::print(QPrinter *printer)
277 {
278     const bool oldFullPage =  printer->fullPage();
279     printer->setFullPage(true);
280     QPrintDialog *dlg = new QPrintDialog(printer, this);
281     dlg->setWindowTitle(tr("Print Document"));
282     if (dlg->exec() == QDialog::Accepted) {
283         d->print(printer);
284     }
285     printer->setFullPage(oldFullPage);
286     delete dlg;
287 }
288
289 static int foldBoxWidth(const QFontMetrics &fm)
290 {
291     const int lineSpacing = fm.lineSpacing();
292     return lineSpacing + lineSpacing%2 + 1;
293 }
294
295 static void printPage(int index, QPainter *painter, const QTextDocument *doc,
296                       const QRectF &body, const QRectF &titleBox,
297                       const QString &title)
298 {
299     painter->save();
300
301     painter->translate(body.left(), body.top() - (index - 1) * body.height());
302     QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
303
304     QAbstractTextDocumentLayout *layout = doc->documentLayout();
305     QAbstractTextDocumentLayout::PaintContext ctx;
306
307     painter->setFont(QFont(doc->defaultFont()));
308     QRectF box = titleBox.translated(0, view.top());
309     int dpix = painter->device()->logicalDpiX();
310     int dpiy = painter->device()->logicalDpiY();
311     int mx = 5 * dpix / 72.0;
312     int my = 2 * dpiy / 72.0;
313     painter->fillRect(box.adjusted(-mx, -my, mx, my), QColor(210, 210, 210));
314     if (!title.isEmpty())
315         painter->drawText(box, Qt::AlignCenter, title);
316     const QString pageString = QString::number(index);
317     painter->drawText(box, Qt::AlignRight, pageString);
318
319     painter->setClipRect(view);
320     ctx.clip = view;
321     // don't use the system palette text as default text color, on HP/UX
322     // for example that's white, and white text on white paper doesn't
323     // look that nice
324     ctx.palette.setColor(QPalette::Text, Qt::black);
325
326     layout->draw(painter, ctx);
327
328     painter->restore();
329 }
330
331 void BaseTextEditorPrivate::print(QPrinter *printer)
332 {
333     QTextDocument *doc = q->document();
334
335     QString title = q->displayName();
336     if (title.isEmpty())
337         printer->setDocName(title);
338
339
340     QPainter p(printer);
341
342     // Check that there is a valid device to print to.
343     if (!p.isActive())
344         return;
345
346     doc = doc->clone(doc);
347
348     QTextOption opt = doc->defaultTextOption();
349     opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
350     doc->setDefaultTextOption(opt);
351
352     (void)doc->documentLayout(); // make sure that there is a layout
353
354
355     QColor background = q->palette().color(QPalette::Base);
356     bool backgroundIsDark = background.value() < 128;
357
358     for (QTextBlock srcBlock = q->document()->firstBlock(), dstBlock = doc->firstBlock();
359          srcBlock.isValid() && dstBlock.isValid();
360          srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
361
362
363         QList<QTextLayout::FormatRange> formatList = srcBlock.layout()->additionalFormats();
364         if (backgroundIsDark) {
365             // adjust syntax highlighting colors for better contrast
366             for (int i = formatList.count() - 1; i >=0; --i) {
367                 QTextCharFormat &format = formatList[i].format;
368                 if (format.background().color() == background) {
369                     QBrush brush = format.foreground();
370                     QColor color = brush.color();
371                     int h,s,v,a;
372                     color.getHsv(&h, &s, &v, &a);
373                     color.setHsv(h, s, qMin(128, v), a);
374                     brush.setColor(color);
375                     format.setForeground(brush);
376                 }
377                 format.setBackground(Qt::white);
378             }
379         }
380
381         dstBlock.layout()->setAdditionalFormats(formatList);
382     }
383
384     QAbstractTextDocumentLayout *layout = doc->documentLayout();
385     layout->setPaintDevice(p.device());
386
387     int dpiy = p.device()->logicalDpiY();
388     int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
389
390     QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
391     fmt.setMargin(margin);
392     doc->rootFrame()->setFrameFormat(fmt);
393
394     QRectF pageRect(printer->pageRect());
395     QRectF body = QRectF(0, 0, pageRect.width(), pageRect.height());
396     QFontMetrics fontMetrics(doc->defaultFont(), p.device());
397
398     QRectF titleBox(margin,
399                     body.top() + margin
400                     - fontMetrics.height()
401                     - 6 * dpiy / 72.0,
402                     body.width() - 2*margin,
403                     fontMetrics.height());
404     doc->setPageSize(body.size());
405
406     int docCopies;
407     int pageCopies;
408     if (printer->collateCopies() == true){
409         docCopies = 1;
410         pageCopies = printer->numCopies();
411     } else {
412         docCopies = printer->numCopies();
413         pageCopies = 1;
414     }
415
416     int fromPage = printer->fromPage();
417     int toPage = printer->toPage();
418     bool ascending = true;
419
420     if (fromPage == 0 && toPage == 0) {
421         fromPage = 1;
422         toPage = doc->pageCount();
423     }
424     // paranoia check
425     fromPage = qMax(1, fromPage);
426     toPage = qMin(doc->pageCount(), toPage);
427
428     if (printer->pageOrder() == QPrinter::LastPageFirst) {
429         int tmp = fromPage;
430         fromPage = toPage;
431         toPage = tmp;
432         ascending = false;
433     }
434
435     for (int i = 0; i < docCopies; ++i) {
436
437         int page = fromPage;
438         while (true) {
439             for (int j = 0; j < pageCopies; ++j) {
440                 if (printer->printerState() == QPrinter::Aborted
441                     || printer->printerState() == QPrinter::Error)
442                     goto UserCanceled;
443                 printPage(page, &p, doc, body, titleBox, title);
444                 if (j < pageCopies - 1)
445                     printer->newPage();
446             }
447
448             if (page == toPage)
449                 break;
450
451             if (ascending)
452                 ++page;
453             else
454                 --page;
455
456             printer->newPage();
457         }
458
459         if ( i < docCopies - 1)
460             printer->newPage();
461     }
462
463 UserCanceled:
464     delete doc;
465 }
466
467
468 int BaseTextEditorPrivate::visualIndent(const QTextBlock &block) const
469 {
470     if (!block.isValid())
471         return 0;
472     const QTextDocument *document = block.document();
473     int i = 0;
474     while (i < block.length()) {
475         if (!document->characterAt(block.position() + i).isSpace()) {
476             QTextCursor cursor(block);
477             cursor.setPosition(block.position() + i);
478             return q->cursorRect(cursor).x();
479         }
480         ++i;
481     }
482
483     return 0;
484 }
485
486 ITextMarkable *BaseTextEditorWidget::markableInterface() const
487 {
488     return baseTextDocument()->documentMarker();
489 }
490
491 BaseTextEditor *BaseTextEditorWidget::editor() const
492 {
493     if (!d->m_editor) {
494         d->m_editor = const_cast<BaseTextEditorWidget*>(this)->createEditor();
495         connect(this, SIGNAL(textChanged()),
496                 d->m_editor, SIGNAL(contentsChanged()));
497         connect(this, SIGNAL(changed()),
498                 d->m_editor, SIGNAL(changed()));
499     }
500     return d->m_editor;
501 }
502
503
504 void BaseTextEditorWidget::currentEditorChanged(Core::IEditor *ed)
505 {
506     if (ed == editor()) {
507         if (d->m_document->hasDecodingError()) {
508             Core::EditorManager::instance()->showEditorInfoBar(QLatin1String(Constants::SELECT_ENCODING),
509                 tr("<b>Error:</b> Could not decode \"%1\" with \"%2\"-encoding. Editing not possible.")
510                     .arg(displayName()).arg(QString::fromLatin1(d->m_document->codec()->name())),
511                 tr("Select Encoding"),
512                 this, SLOT(selectEncoding()));
513         }
514     }
515 }
516
517 void BaseTextEditorWidget::selectEncoding()
518 {
519     BaseTextDocument *doc = d->m_document;
520     CodecSelector codecSelector(this, doc);
521
522     switch (codecSelector.exec()) {
523     case CodecSelector::Reload:
524         doc->reload(codecSelector.selectedCodec());
525         setReadOnly(d->m_document->hasDecodingError());
526         if (doc->hasDecodingError())
527             currentEditorChanged(Core::EditorManager::instance()->currentEditor());
528         else
529             Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String(Constants::SELECT_ENCODING));
530         break;
531     case CodecSelector::Save:
532         doc->setCodec(codecSelector.selectedCodec());
533         Core::EditorManager::instance()->saveEditor(editor());
534         break;
535     case CodecSelector::Cancel:
536         break;
537     }
538 }
539
540 QString BaseTextEditorWidget::msgTextTooLarge(quint64 size)
541 {
542     return tr("The text is too large to be displayed (%1 MB).").
543            arg(size >> 20);
544 }
545
546 bool BaseTextEditorWidget::createNew(const QString &contents)
547 {
548     if (contents.size() > Core::EditorManager::maxTextFileSize()) {
549         setPlainText(msgTextTooLarge(contents.size()));
550         document()->setModified(false);
551         return false;
552     }
553     setPlainText(contents);
554     document()->setModified(false);
555     return true;
556 }
557
558 bool BaseTextEditorWidget::open(const QString &fileName)
559 {
560     if (d->m_document->open(fileName)) {
561         moveCursor(QTextCursor::Start);
562         setReadOnly(d->m_document->hasDecodingError());
563         return true;
564     }
565     return false;
566 }
567
568 /*
569   Collapses the first comment in a file, if there is only whitespace above
570   */
571 void BaseTextEditorPrivate::foldLicenseHeader()
572 {
573     QTextDocument *doc = q->document();
574     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
575     QTC_ASSERT(documentLayout, return);
576     QTextBlock block = doc->firstBlock();
577     const TabSettings &ts = m_document->tabSettings();
578     while (block.isValid() && block.isVisible()) {
579         QString text = block.text();
580         if (BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) {
581             if (text.trimmed().startsWith(QLatin1String("/*"))) {
582                 BaseTextDocumentLayout::doFoldOrUnfold(block, false);
583                 moveCursorVisible();
584                 documentLayout->requestUpdate();
585                 documentLayout->emitDocumentSizeChanged();
586                 break;
587             }
588         }
589         if (ts.firstNonSpace(text) < text.size())
590             break;
591         block = block.next();
592     }
593 }
594
595 const Utils::ChangeSet &BaseTextEditorWidget::changeSet() const
596 {
597     return d->m_changeSet;
598 }
599
600 void BaseTextEditorWidget::setChangeSet(const Utils::ChangeSet &changeSet)
601 {
602     using namespace Utils;
603
604     d->m_changeSet = changeSet;
605
606     foreach (const ChangeSet::EditOp &op, changeSet.operationList()) {
607         // ### TODO: process the edit operation
608
609         switch (op.type) {
610         case ChangeSet::EditOp::Replace:
611             break;
612
613         case ChangeSet::EditOp::Move:
614             break;
615
616         case ChangeSet::EditOp::Insert:
617             break;
618
619         case ChangeSet::EditOp::Remove:
620             break;
621
622         case ChangeSet::EditOp::Flip:
623             break;
624
625         case ChangeSet::EditOp::Copy:
626             break;
627
628         default:
629             break;
630         } // switch
631     }
632 }
633
634 Core::IFile *BaseTextEditorWidget::file()
635 {
636     return d->m_document;
637 }
638
639 void BaseTextEditorWidget::editorContentsChange(int position, int charsRemoved, int charsAdded)
640 {
641     if (d->m_animator)
642         d->m_animator->finish();
643
644     d->m_contentsChanged = true;
645     QTextDocument *doc = document();
646
647     // Keep the line numbers and the block information for the text marks updated
648     if (charsRemoved != 0) {
649         d->updateMarksLineNumber();
650         d->updateMarksBlock(document()->findBlock(position));
651     } else {
652         const QTextBlock posBlock = doc->findBlock(position);
653         const QTextBlock nextBlock = doc->findBlock(position + charsAdded);
654         if (posBlock != nextBlock) {
655             d->updateMarksLineNumber();
656             d->updateMarksBlock(posBlock);
657             d->updateMarksBlock(nextBlock);
658         } else {
659             d->updateMarksBlock(posBlock);
660         }
661     }
662
663     if (d->m_snippetOverlay->isVisible()) {
664         QTextCursor cursor = textCursor();
665         cursor.setPosition(position);
666         d->snippetCheckCursor(cursor);
667     }
668
669     if (doc->isRedoAvailable())
670         emit editor()->contentsChangedBecauseOfUndo();
671 }
672
673 void BaseTextEditorWidget::slotSelectionChanged()
674 {
675     if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) {
676         d->m_inBlockSelectionMode = false;
677         d->m_blockSelection.clear();
678         viewport()->update();
679     }
680
681     if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection())
682         d->m_selectBlockAnchor = QTextCursor();
683
684     // Clear any link which might be showing when the selection changes
685     clearLink();
686 }
687
688 void BaseTextEditorWidget::gotoBlockStart()
689 {
690     QTextCursor cursor = textCursor();
691     if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, false)) {
692         setTextCursor(cursor);
693         _q_matchParentheses();
694     }
695 }
696
697 void BaseTextEditorWidget::gotoBlockEnd()
698 {
699     QTextCursor cursor = textCursor();
700     if (TextBlockUserData::findNextClosingParenthesis(&cursor, false)) {
701         setTextCursor(cursor);
702         _q_matchParentheses();
703     }
704 }
705
706 void BaseTextEditorWidget::gotoBlockStartWithSelection()
707 {
708     QTextCursor cursor = textCursor();
709     if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, true)) {
710         setTextCursor(cursor);
711         _q_matchParentheses();
712     }
713 }
714
715 void BaseTextEditorWidget::gotoBlockEndWithSelection()
716 {
717     QTextCursor cursor = textCursor();
718     if (TextBlockUserData::findNextClosingParenthesis(&cursor, true)) {
719         setTextCursor(cursor);
720         _q_matchParentheses();
721     }
722 }
723
724
725 void BaseTextEditorWidget::gotoLineStart()
726 {
727     handleHomeKey(false);
728 }
729
730 void BaseTextEditorWidget::gotoLineStartWithSelection()
731 {
732     handleHomeKey(true);
733 }
734
735 void BaseTextEditorWidget::gotoLineEnd()
736 {
737     moveCursor(QTextCursor::EndOfLine);
738 }
739
740 void BaseTextEditorWidget::gotoLineEndWithSelection()
741 {
742     moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
743 }
744
745 void BaseTextEditorWidget::gotoNextLine()
746 {
747     moveCursor(QTextCursor::Down);
748 }
749
750 void BaseTextEditorWidget::gotoNextLineWithSelection()
751 {
752     moveCursor(QTextCursor::Down, QTextCursor::KeepAnchor);
753 }
754
755 void BaseTextEditorWidget::gotoPreviousLine()
756 {
757     moveCursor(QTextCursor::Up);
758 }
759
760 void BaseTextEditorWidget::gotoPreviousLineWithSelection()
761 {
762     moveCursor(QTextCursor::Up, QTextCursor::KeepAnchor);
763 }
764
765 void BaseTextEditorWidget::gotoPreviousCharacter()
766 {
767     moveCursor(QTextCursor::PreviousCharacter);
768 }
769
770 void BaseTextEditorWidget::gotoPreviousCharacterWithSelection()
771 {
772     moveCursor(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
773 }
774
775 void BaseTextEditorWidget::gotoNextCharacter()
776 {
777     moveCursor(QTextCursor::NextCharacter);
778 }
779
780 void BaseTextEditorWidget::gotoNextCharacterWithSelection()
781 {
782     moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
783 }
784
785 void BaseTextEditorWidget::gotoPreviousWord()
786 {
787     moveCursor(QTextCursor::PreviousWord);
788 }
789
790 void BaseTextEditorWidget::gotoPreviousWordWithSelection()
791 {
792     moveCursor(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
793 }
794
795 void BaseTextEditorWidget::gotoNextWord()
796 {
797     moveCursor(QTextCursor::NextWord);
798 }
799
800 void BaseTextEditorWidget::gotoNextWordWithSelection()
801 {
802     moveCursor(QTextCursor::NextWord, QTextCursor::KeepAnchor);
803 }
804
805 void BaseTextEditorWidget::gotoPreviousWordCamelCase()
806 {
807     QTextCursor c = textCursor();
808     camelCaseLeft(c, QTextCursor::MoveAnchor);
809     setTextCursor(c);
810 }
811
812 void BaseTextEditorWidget::gotoPreviousWordCamelCaseWithSelection()
813 {
814     QTextCursor c = textCursor();
815     camelCaseLeft(c, QTextCursor::KeepAnchor);
816     setTextCursor(c);
817 }
818
819 void BaseTextEditorWidget::gotoNextWordCamelCase()
820 {
821     qDebug() << Q_FUNC_INFO;
822     QTextCursor c = textCursor();
823     camelCaseRight(c, QTextCursor::MoveAnchor);
824     setTextCursor(c);
825 }
826
827 void BaseTextEditorWidget::gotoNextWordCamelCaseWithSelection()
828 {
829     QTextCursor c = textCursor();
830     camelCaseRight(c, QTextCursor::KeepAnchor);
831     setTextCursor(c);
832 }
833
834
835
836 static QTextCursor flippedCursor(const QTextCursor &cursor)
837 {
838     QTextCursor flipped = cursor;
839     flipped.clearSelection();
840     flipped.setPosition(cursor.anchor(), QTextCursor::KeepAnchor);
841     return flipped;
842 }
843
844 void BaseTextEditorWidget::selectBlockUp()
845 {
846     QTextCursor cursor = textCursor();
847     if (!cursor.hasSelection())
848         d->m_selectBlockAnchor = cursor;
849     else
850         cursor.setPosition(cursor.selectionStart());
851
852
853     if (!TextBlockUserData::findPreviousOpenParenthesis(&cursor, false))
854         return;
855     if (!TextBlockUserData::findNextClosingParenthesis(&cursor, true))
856         return;
857     setTextCursor(flippedCursor(cursor));
858     _q_matchParentheses();
859 }
860
861 void BaseTextEditorWidget::selectBlockDown()
862 {
863     QTextCursor tc = textCursor();
864     QTextCursor cursor = d->m_selectBlockAnchor;
865
866     if (!tc.hasSelection() || cursor.isNull())
867         return;
868     tc.setPosition(tc.selectionStart());
869
870     forever {
871         QTextCursor ahead = cursor;
872         if (!TextBlockUserData::findPreviousOpenParenthesis(&ahead, false))
873             break;
874         if (ahead.position() <= tc.position())
875             break;
876         cursor = ahead;
877     }
878     if ( cursor != d->m_selectBlockAnchor)
879         TextBlockUserData::findNextClosingParenthesis(&cursor, true);
880
881     setTextCursor(flippedCursor(cursor));
882     _q_matchParentheses();
883 }
884
885 void BaseTextEditorWidget::copyLineUp()
886 {
887     copyLineUpDown(true);
888 }
889
890 void BaseTextEditorWidget::copyLineDown()
891 {
892     copyLineUpDown(false);
893 }
894
895 // @todo: Potential reuse of some code around the following functions...
896 void BaseTextEditorWidget::copyLineUpDown(bool up)
897 {
898     QTextCursor cursor = textCursor();
899     QTextCursor move = cursor;
900     move.beginEditBlock();
901
902     bool hasSelection = cursor.hasSelection();
903
904     if (hasSelection) {
905         move.setPosition(cursor.selectionStart());
906         move.movePosition(QTextCursor::StartOfBlock);
907         move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
908         move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
909                           QTextCursor::KeepAnchor);
910     } else {
911         move.movePosition(QTextCursor::StartOfBlock);
912         move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
913     }
914
915     QString text = move.selectedText();
916
917     if (up) {
918         move.setPosition(cursor.selectionStart());
919         move.movePosition(QTextCursor::StartOfBlock);
920         move.insertBlock();
921         move.movePosition(QTextCursor::Left);
922     } else {
923         move.movePosition(QTextCursor::EndOfBlock);
924         if (move.atBlockStart()) {
925             move.movePosition(QTextCursor::NextBlock);
926             move.insertBlock();
927             move.movePosition(QTextCursor::Left);
928         } else {
929             move.insertBlock();
930         }
931     }
932
933     int start = move.position();
934     move.clearSelection();
935     move.insertText(text);
936     int end = move.position();
937
938     move.setPosition(start);
939     move.setPosition(end, QTextCursor::KeepAnchor);
940
941     indent(document(), move, QChar::Null);
942     move.endEditBlock();
943
944     setTextCursor(move);
945 }
946
947 void BaseTextEditorWidget::joinLines()
948 {
949     QTextCursor cursor = textCursor();
950     QTextCursor start = cursor;
951     QTextCursor end = cursor;
952
953     start.setPosition(cursor.selectionStart());
954     end.setPosition(cursor.selectionEnd() - 1);
955
956     int lineCount = qMax(1, end.blockNumber() - start.blockNumber());
957
958     cursor.beginEditBlock();
959     cursor.setPosition(cursor.selectionStart());
960     while (lineCount--) {
961         cursor.movePosition(QTextCursor::NextBlock);
962         cursor.movePosition(QTextCursor::StartOfBlock);
963         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
964         QString cutLine = cursor.selectedText();
965
966         // Collapse leading whitespaces to one or insert whitespace
967         cutLine.replace(QRegExp("^\\s*"), " ");
968         cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
969         cursor.removeSelectedText();
970
971         cursor.movePosition(QTextCursor::PreviousBlock);
972         cursor.movePosition(QTextCursor::EndOfBlock);
973
974         cursor.insertText(cutLine);
975     }
976     cursor.endEditBlock();
977
978     setTextCursor(cursor);
979 }
980
981 void BaseTextEditorWidget::insertLineAbove()
982 {
983     QTextCursor cursor = textCursor();
984     cursor.beginEditBlock();
985     cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
986     cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
987     cursor.insertBlock();
988     indent(document(), cursor, QChar::Null);
989     cursor.endEditBlock();
990     setTextCursor(cursor);
991 }
992
993 void BaseTextEditorWidget::insertLineBelow()
994 {
995     QTextCursor cursor = textCursor();
996     cursor.beginEditBlock();
997     cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
998     cursor.insertBlock();
999     indent(document(), cursor, QChar::Null);
1000     cursor.endEditBlock();
1001     setTextCursor(cursor);
1002 }
1003
1004 void BaseTextEditorWidget::moveLineUp()
1005 {
1006     moveLineUpDown(true);
1007 }
1008
1009 void BaseTextEditorWidget::moveLineDown()
1010 {
1011     moveLineUpDown(false);
1012 }
1013
1014 void BaseTextEditorWidget::uppercaseSelection()
1015 {
1016     transformSelection(&QString::toUpper);
1017 }
1018
1019
1020 void BaseTextEditorWidget::lowercaseSelection()
1021 {
1022     transformSelection(&QString::toLower);
1023 }
1024
1025 void BaseTextEditorWidget::moveLineUpDown(bool up)
1026 {
1027     QTextCursor cursor = textCursor();
1028     QTextCursor move = cursor;
1029
1030     move.setVisualNavigation(false); // this opens folded items instead of destroying them
1031
1032     if (d->m_moveLineUndoHack)
1033         move.joinPreviousEditBlock();
1034     else
1035         move.beginEditBlock();
1036
1037     bool hasSelection = cursor.hasSelection();
1038
1039     if (cursor.hasSelection()) {
1040         move.setPosition(cursor.selectionStart());
1041         move.movePosition(QTextCursor::StartOfBlock);
1042         move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
1043         move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
1044                           QTextCursor::KeepAnchor);
1045     } else {
1046         move.movePosition(QTextCursor::StartOfBlock);
1047         move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1048     }
1049     QString text = move.selectedText();
1050
1051     RefactorMarkers affectedMarkers;
1052     RefactorMarkers nonAffectedMarkers;
1053     QList<int> markerOffsets;
1054
1055     foreach (const RefactorMarker &marker, d->m_refactorOverlay->markers()) {
1056         //test if marker is part of the selection to be moved
1057         if ((move.selectionStart() <= marker.cursor.position())
1058                 && (move.selectionEnd() >= marker.cursor.position())) {
1059             affectedMarkers.append(marker);
1060             //remember the offset of markers in text
1061             int offset = marker.cursor.position() - move.selectionStart();
1062             markerOffsets.append(offset);
1063         } else {
1064             nonAffectedMarkers.append(marker);
1065         }
1066     }
1067
1068     move.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
1069     move.removeSelectedText();
1070
1071     if (up) {
1072         move.movePosition(QTextCursor::PreviousBlock);
1073         move.insertBlock();
1074         move.movePosition(QTextCursor::Left);
1075     } else {
1076         move.movePosition(QTextCursor::EndOfBlock);
1077         if (move.atBlockStart()) { // empty block
1078             move.movePosition(QTextCursor::NextBlock);
1079             move.insertBlock();
1080             move.movePosition(QTextCursor::Left);
1081         } else {
1082             move.insertBlock();
1083         }
1084     }
1085
1086     int start = move.position();
1087     move.clearSelection();
1088     move.insertText(text);
1089     int end = move.position();
1090
1091     if (hasSelection) {
1092         move.setPosition(start);
1093         move.setPosition(end, QTextCursor::KeepAnchor);
1094     }
1095
1096     //update positions of affectedMarkers
1097     for (int i=0;i < affectedMarkers.count(); i++) {
1098         int newPosition = start + markerOffsets.at(i);
1099         affectedMarkers[i].cursor.setPosition(newPosition);
1100     }
1101     d->m_refactorOverlay->setMarkers(nonAffectedMarkers + affectedMarkers);
1102
1103     reindent(document(), move);
1104     move.endEditBlock();
1105
1106     setTextCursor(move);
1107     d->m_moveLineUndoHack = true;
1108 }
1109
1110 void BaseTextEditorWidget::cleanWhitespace()
1111 {
1112     d->m_document->cleanWhitespace(textCursor());
1113 }
1114
1115
1116 // could go into QTextCursor...
1117 static QTextLine currentTextLine(const QTextCursor &cursor)
1118 {
1119     const QTextBlock block = cursor.block();
1120     if (!block.isValid())
1121         return QTextLine();
1122
1123     const QTextLayout *layout = block.layout();
1124     if (!layout)
1125         return QTextLine();
1126
1127     const int relativePos = cursor.position() - block.position();
1128     return layout->lineForTextPosition(relativePos);
1129 }
1130
1131 bool BaseTextEditorWidget::camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode)
1132 {
1133     int state = 0;
1134     enum Input {
1135         Input_U,
1136         Input_l,
1137         Input_underscore,
1138         Input_space,
1139         Input_other
1140     };
1141
1142     if (!cursor.movePosition(QTextCursor::Left, mode))
1143         return false;
1144
1145     forever {
1146         QChar c = characterAt(cursor.position());
1147         Input input = Input_other;
1148         if (c.isUpper())
1149             input = Input_U;
1150         else if (c.isLower() || c.isDigit())
1151             input = Input_l;
1152         else if (c == QLatin1Char('_'))
1153             input = Input_underscore;
1154         else if (c.isSpace() && c != QChar::ParagraphSeparator)
1155             input = Input_space;
1156         else
1157             input = Input_other;
1158
1159         switch (state) {
1160         case 0:
1161             switch (input) {
1162             case Input_U:
1163                 state = 1;
1164                 break;
1165             case Input_l:
1166                 state = 2;
1167                 break;
1168             case Input_underscore:
1169                 state = 3;
1170                 break;
1171             case Input_space:
1172                 state = 4;
1173                 break;
1174             default:
1175                 cursor.movePosition(QTextCursor::Right, mode);
1176                 return cursor.movePosition(QTextCursor::WordLeft, mode);
1177             }
1178             break;
1179         case 1:
1180             switch (input) {
1181             case Input_U:
1182                 break;
1183             default:
1184                 cursor.movePosition(QTextCursor::Right, mode);
1185                 return true;
1186             }
1187             break;
1188
1189         case 2:
1190             switch (input) {
1191             case Input_U:
1192                 return true;
1193             case Input_l:
1194                 break;
1195             default:
1196                 cursor.movePosition(QTextCursor::Right, mode);
1197                 return true;
1198             }
1199             break;
1200         case 3:
1201             switch (input) {
1202             case Input_underscore:
1203                 break;
1204             case Input_U:
1205                 state = 1;
1206                 break;
1207             case Input_l:
1208                 state = 2;
1209                 break;
1210             default:
1211                 cursor.movePosition(QTextCursor::Right, mode);
1212                 return true;
1213             }
1214             break;
1215         case 4:
1216             switch (input) {
1217             case Input_space:
1218                 break;
1219             case Input_U:
1220                 state = 1;
1221                 break;
1222             case Input_l:
1223                 state = 2;
1224                 break;
1225             case Input_underscore:
1226                 state = 3;
1227                 break;
1228             default:
1229                 cursor.movePosition(QTextCursor::Right, mode);
1230                 if (cursor.positionInBlock() == 0)
1231                     return true;
1232                 return cursor.movePosition(QTextCursor::WordLeft, mode);
1233             }
1234         }
1235
1236         if (!cursor.movePosition(QTextCursor::Left, mode))
1237             return true;
1238     }
1239 }
1240
1241 bool BaseTextEditorWidget::camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode)
1242 {
1243     int state = 0;
1244     enum Input {
1245         Input_U,
1246         Input_l,
1247         Input_underscore,
1248         Input_space,
1249         Input_other
1250     };
1251
1252     forever {
1253         QChar c = characterAt(cursor.position());
1254         Input input = Input_other;
1255         if (c.isUpper())
1256             input = Input_U;
1257         else if (c.isLower() || c.isDigit())
1258             input = Input_l;
1259         else if (c == QLatin1Char('_'))
1260             input = Input_underscore;
1261         else if (c.isSpace() && c != QChar::ParagraphSeparator)
1262             input = Input_space;
1263         else
1264             input = Input_other;
1265
1266         switch (state) {
1267         case 0:
1268             switch (input) {
1269             case Input_U:
1270                 state = 4;
1271                 break;
1272             case Input_l:
1273                 state = 1;
1274                 break;
1275             case Input_underscore:
1276                 state = 6;
1277                 break;
1278             default:
1279                 return cursor.movePosition(QTextCursor::WordRight, mode);
1280             }
1281             break;
1282         case 1:
1283             switch (input) {
1284             case Input_U:
1285                 return true;
1286             case Input_l:
1287                 break;
1288             case Input_underscore:
1289                 state = 6;
1290                 break;
1291             case Input_space:
1292                 state = 7;
1293                 break;
1294             default:
1295                 return true;
1296             }
1297             break;
1298         case 2:
1299             switch (input) {
1300             case Input_U:
1301                 break;
1302             case Input_l:
1303                 cursor.movePosition(QTextCursor::Left, mode);
1304                 return true;
1305             case Input_underscore:
1306                 state = 6;
1307                 break;
1308             case Input_space:
1309                 state = 7;
1310                 break;
1311             default:
1312                 return true;
1313             }
1314             break;
1315         case 4:
1316             switch (input) {
1317             case Input_U:
1318                 state = 2;
1319                 break;
1320             case Input_l:
1321                 state = 1;
1322                 break;
1323             case Input_underscore:
1324                 state = 6;
1325                 break;
1326             case Input_space:
1327                 state = 7;
1328                 break;
1329             default:
1330                 return true;
1331             }
1332             break;
1333         case 6:
1334             switch (input) {
1335             case Input_underscore:
1336                 break;
1337             case Input_space:
1338                 state = 7;
1339                 break;
1340             default:
1341                 return true;
1342             }
1343             break;
1344         case 7:
1345             switch (input) {
1346             case Input_space:
1347                 break;
1348             default:
1349                 return true;
1350             }
1351             break;
1352         }
1353         cursor.movePosition(QTextCursor::Right, mode);
1354     }
1355 }
1356
1357 bool BaseTextEditorWidget::cursorMoveKeyEvent(QKeyEvent *e)
1358 {
1359     QTextCursor cursor = textCursor();
1360
1361     QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
1362     QTextCursor::MoveOperation op = QTextCursor::NoMove;
1363
1364     if (e == QKeySequence::MoveToNextChar) {
1365             op = QTextCursor::Right;
1366     }
1367     else if (e == QKeySequence::MoveToPreviousChar) {
1368             op = QTextCursor::Left;
1369     }
1370     else if (e == QKeySequence::SelectNextChar) {
1371            op = QTextCursor::Right;
1372            mode = QTextCursor::KeepAnchor;
1373     }
1374     else if (e == QKeySequence::SelectPreviousChar) {
1375             op = QTextCursor::Left;
1376             mode = QTextCursor::KeepAnchor;
1377     }
1378     else if (e == QKeySequence::SelectNextWord) {
1379             op = QTextCursor::WordRight;
1380             mode = QTextCursor::KeepAnchor;
1381     }
1382     else if (e == QKeySequence::SelectPreviousWord) {
1383             op = QTextCursor::WordLeft;
1384             mode = QTextCursor::KeepAnchor;
1385     }
1386     else if (e == QKeySequence::SelectStartOfLine) {
1387             op = QTextCursor::StartOfLine;
1388             mode = QTextCursor::KeepAnchor;
1389     }
1390     else if (e == QKeySequence::SelectEndOfLine) {
1391             op = QTextCursor::EndOfLine;
1392             mode = QTextCursor::KeepAnchor;
1393     }
1394     else if (e == QKeySequence::SelectStartOfBlock) {
1395             op = QTextCursor::StartOfBlock;
1396             mode = QTextCursor::KeepAnchor;
1397     }
1398     else if (e == QKeySequence::SelectEndOfBlock) {
1399             op = QTextCursor::EndOfBlock;
1400             mode = QTextCursor::KeepAnchor;
1401     }
1402     else if (e == QKeySequence::SelectStartOfDocument) {
1403             op = QTextCursor::Start;
1404             mode = QTextCursor::KeepAnchor;
1405     }
1406     else if (e == QKeySequence::SelectEndOfDocument) {
1407             op = QTextCursor::End;
1408             mode = QTextCursor::KeepAnchor;
1409     }
1410     else if (e == QKeySequence::SelectPreviousLine) {
1411             op = QTextCursor::Up;
1412             mode = QTextCursor::KeepAnchor;
1413     }
1414     else if (e == QKeySequence::SelectNextLine) {
1415             op = QTextCursor::Down;
1416             mode = QTextCursor::KeepAnchor;
1417             {
1418                 QTextBlock block = cursor.block();
1419                 QTextLine line = currentTextLine(cursor);
1420                 if (!block.next().isValid()
1421                     && line.isValid()
1422                     && line.lineNumber() == block.layout()->lineCount() - 1)
1423                     op = QTextCursor::End;
1424             }
1425     }
1426     else if (e == QKeySequence::MoveToNextWord) {
1427             op = QTextCursor::WordRight;
1428     }
1429     else if (e == QKeySequence::MoveToPreviousWord) {
1430             op = QTextCursor::WordLeft;
1431     }
1432     else if (e == QKeySequence::MoveToEndOfBlock) {
1433             op = QTextCursor::EndOfBlock;
1434     }
1435     else if (e == QKeySequence::MoveToStartOfBlock) {
1436             op = QTextCursor::StartOfBlock;
1437     }
1438     else if (e == QKeySequence::MoveToNextLine) {
1439             op = QTextCursor::Down;
1440     }
1441     else if (e == QKeySequence::MoveToPreviousLine) {
1442             op = QTextCursor::Up;
1443     }
1444     else if (e == QKeySequence::MoveToPreviousLine) {
1445             op = QTextCursor::Up;
1446     }
1447     else if (e == QKeySequence::MoveToStartOfLine) {
1448             op = QTextCursor::StartOfLine;
1449     }
1450     else if (e == QKeySequence::MoveToEndOfLine) {
1451             op = QTextCursor::EndOfLine;
1452     }
1453     else if (e == QKeySequence::MoveToStartOfDocument) {
1454             op = QTextCursor::Start;
1455     }
1456     else if (e == QKeySequence::MoveToEndOfDocument) {
1457             op = QTextCursor::End;
1458     }
1459     else {
1460         return false;
1461     }
1462
1463
1464 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
1465 // here's the breakdown:
1466 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
1467 // Alt (Option), or Meta (Control).
1468 // Command/Control + Left/Right -- Move to left or right of the line
1469 //                 + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
1470 // Option + Left/Right -- Move one word Left/right.
1471 //        + Up/Down  -- Begin/End of Paragraph.
1472 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
1473
1474     bool visualNavigation = cursor.visualNavigation();
1475     cursor.setVisualNavigation(true);
1476
1477     if (op == QTextCursor::WordRight) {
1478         camelCaseRight(cursor, mode);
1479     } else if (op == QTextCursor::WordLeft) {
1480         camelCaseLeft(cursor, mode);
1481     } else {
1482         cursor.movePosition(op, mode);
1483     }
1484     cursor.setVisualNavigation(visualNavigation);
1485
1486     setTextCursor(cursor);
1487     ensureCursorVisible();
1488     return true;
1489 }
1490
1491
1492 void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e)
1493 {
1494     viewport()->setCursor(Qt::BlankCursor);
1495     ToolTip::instance()->hide();
1496
1497     d->m_moveLineUndoHack = false;
1498     d->clearVisibleFoldedBlock();
1499
1500     if (e->key() == Qt::Key_Escape) {
1501         if (d->m_snippetOverlay->isVisible()) {
1502             e->accept();
1503             d->m_snippetOverlay->hide();
1504             d->m_snippetOverlay->clear();
1505             QTextCursor cursor = textCursor();
1506             cursor.clearSelection();
1507             setTextCursor(cursor);
1508             return;
1509         }
1510     }
1511
1512     bool ro = isReadOnly();
1513
1514     if (d->m_inBlockSelectionMode) {
1515         if (e == QKeySequence::Cut) {
1516             if (!ro) {
1517                 cut();
1518                 e->accept();
1519                 return;
1520             }
1521         } else if (e == QKeySequence::Delete || e->key() == Qt::Key_Backspace) {
1522             if (!ro) {
1523                 d->removeBlockSelection();
1524                 e->accept();
1525                 return;
1526             }
1527         } else if (e == QKeySequence::Paste) {
1528             if (!ro) {
1529                 d->removeBlockSelection();
1530                 // continue
1531             }
1532         }
1533     }
1534
1535
1536     if (!ro
1537         && (e == QKeySequence::InsertParagraphSeparator
1538             || (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator))
1539         ) {
1540
1541         if (d->m_snippetOverlay->isVisible()) {
1542             e->accept();
1543             d->m_snippetOverlay->hide();
1544             d->m_snippetOverlay->clear();
1545             QTextCursor cursor = textCursor();
1546             cursor.movePosition(QTextCursor::EndOfBlock);
1547             setTextCursor(cursor);
1548             return;
1549         }
1550
1551
1552         QTextCursor cursor = textCursor();
1553         if (d->m_inBlockSelectionMode)
1554             cursor.clearSelection();
1555         const TabSettings &ts = d->m_document->tabSettings();
1556         cursor.beginEditBlock();
1557
1558         int extraBlocks =
1559             d->m_autoCompleter->paragraphSeparatorAboutToBeInserted(cursor, tabSettings());
1560
1561         QString previousIndentationString;
1562         if (ts.m_autoIndent) {
1563             cursor.insertBlock();
1564             indent(document(), cursor, QChar::Null);
1565         } else {
1566             cursor.insertBlock();
1567
1568             // After inserting the block, to avoid duplicating whitespace on the same line
1569             const QString &previousBlockText = cursor.block().previous().text();
1570             previousIndentationString = ts.indentationString(previousBlockText);
1571             if (!previousIndentationString.isEmpty())
1572                 cursor.insertText(previousIndentationString);
1573         }
1574         cursor.endEditBlock();
1575         e->accept();
1576
1577         if (extraBlocks > 0) {
1578             QTextCursor ensureVisible = cursor;
1579             while (extraBlocks > 0) {
1580                 --extraBlocks;
1581                 ensureVisible.movePosition(QTextCursor::NextBlock);
1582                 if (ts.m_autoIndent)
1583                     indent(document(), ensureVisible, QChar::Null);
1584                 else if (!previousIndentationString.isEmpty())
1585                     ensureVisible.insertText(previousIndentationString);
1586             }
1587             setTextCursor(ensureVisible);
1588         }
1589
1590         setTextCursor(cursor);
1591         return;
1592     } else if (!ro
1593                && (e == QKeySequence::MoveToStartOfBlock
1594                    || e == QKeySequence::SelectStartOfBlock)){
1595         if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
1596             e->accept();
1597             return;
1598         }
1599         handleHomeKey(e == QKeySequence::SelectStartOfBlock);
1600         e->accept();
1601         return;
1602     } else if (!ro
1603                && (e == QKeySequence::MoveToStartOfLine
1604                    || e == QKeySequence::SelectStartOfLine)){
1605         if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
1606                 e->accept();
1607                 return;
1608         }
1609         QTextCursor cursor = textCursor();
1610         if (QTextLayout *layout = cursor.block().layout()) {
1611             if (layout->lineForTextPosition(cursor.position() - cursor.block().position()).lineNumber() == 0) {
1612                 handleHomeKey(e == QKeySequence::SelectStartOfLine);
1613                 e->accept();
1614                 return;
1615             }
1616         }
1617     } else if (!ro
1618                && e == QKeySequence::DeleteStartOfWord
1619                && d->m_document->tabSettings().m_autoIndent
1620                && !textCursor().hasSelection()){
1621         e->accept();
1622         QTextCursor c = textCursor();
1623         int pos = c.position();
1624         camelCaseLeft(c, QTextCursor::MoveAnchor);
1625         int targetpos = c.position();
1626         forever {
1627             handleBackspaceKey();
1628             int cpos = textCursor().position();
1629             if (cpos == pos || cpos <= targetpos)
1630                 break;
1631             pos = cpos;
1632         }
1633         return;
1634     } else if (!ro && e == QKeySequence::DeleteStartOfWord && !textCursor().hasSelection()) {
1635         e->accept();
1636         QTextCursor c = textCursor();
1637         camelCaseLeft(c, QTextCursor::KeepAnchor);
1638         c.removeSelectedText();
1639         return;
1640     } else if (!ro && e == QKeySequence::DeleteEndOfWord && !textCursor().hasSelection()) {
1641         e->accept();
1642         QTextCursor c = textCursor();
1643         camelCaseRight(c, QTextCursor::KeepAnchor);
1644         c.removeSelectedText();
1645         return;
1646     } else switch (e->key()) {
1647
1648
1649 #if 0
1650     case Qt::Key_Dollar: {
1651             d->m_overlay->setVisible(!d->m_overlay->isVisible());
1652             d->m_overlay->setCursor(textCursor());
1653             e->accept();
1654         return;
1655
1656     } break;
1657 #endif
1658     case Qt::Key_Tab:
1659     case Qt::Key_Backtab: {
1660         if (ro) break;
1661         if (d->m_snippetOverlay->isVisible() && !d->m_snippetOverlay->isEmpty()) {
1662             d->snippetTabOrBacktab(e->key() == Qt::Key_Tab);
1663             e->accept();
1664             return;
1665         }
1666         QTextCursor cursor = textCursor();
1667         int newPosition;
1668         if (d->m_document->tabSettings().tabShouldIndent(document(), cursor, &newPosition)) {
1669             if (newPosition != cursor.position() && !cursor.hasSelection()) {
1670                 cursor.setPosition(newPosition);
1671                 setTextCursor(cursor);
1672             }
1673             indent(document(), cursor, QChar::Null);
1674         } else {
1675             indentOrUnindent(e->key() == Qt::Key_Tab);
1676         }
1677         e->accept();
1678         return;
1679     } break;
1680     case Qt::Key_Backspace:
1681         if (ro) break;
1682         if ((e->modifiers() & (Qt::ControlModifier
1683                                | Qt::ShiftModifier
1684                                | Qt::AltModifier
1685                                | Qt::MetaModifier)) == Qt::NoModifier
1686             && !textCursor().hasSelection()) {
1687             handleBackspaceKey();
1688             e->accept();
1689             return;
1690         }
1691         break;
1692     case Qt::Key_Up:
1693     case Qt::Key_Down:
1694         if (e->modifiers() & Qt::ControlModifier) {
1695             verticalScrollBar()->triggerAction(
1696                     e->key() == Qt::Key_Up ? QAbstractSlider::SliderSingleStepSub :
1697                                              QAbstractSlider::SliderSingleStepAdd);
1698             e->accept();
1699             return;
1700         }
1701         // fall through
1702     case Qt::Key_Right:
1703     case Qt::Key_Left:
1704 #ifndef Q_WS_MAC
1705         if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
1706             int diff_row = 0;
1707             int diff_col = 0;
1708             if (e->key() == Qt::Key_Up)
1709                 diff_row = -1;
1710             else if (e->key() == Qt::Key_Down)
1711                 diff_row = 1;
1712             else if (e->key() == Qt::Key_Left)
1713                 diff_col = -1;
1714             else if (e->key() == Qt::Key_Right)
1715                 diff_col = 1;
1716             handleBlockSelection(diff_row, diff_col);
1717             e->accept();
1718             return;
1719         } else {
1720             // leave block selection mode
1721             if (d->m_inBlockSelectionMode) {
1722                 d->m_inBlockSelectionMode = false;
1723                 d->m_blockSelection.clear();
1724                 viewport()->update();
1725             }
1726         }
1727 #endif
1728         break;
1729     case Qt::Key_PageUp:
1730     case Qt::Key_PageDown:
1731         if (e->modifiers() == Qt::ControlModifier) {
1732             verticalScrollBar()->triggerAction(
1733                     e->key() == Qt::Key_PageUp ? QAbstractSlider::SliderPageStepSub :
1734                                                  QAbstractSlider::SliderPageStepAdd);
1735             e->accept();
1736             return;
1737         }
1738         break;
1739
1740     default:
1741         break;
1742     }
1743
1744     if (d->m_inBlockSelectionMode) {
1745         QString text = e->text();
1746         if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
1747             d->removeBlockSelection(text);
1748             goto skip_event;
1749         }
1750     }
1751
1752     if (e->key() == Qt::Key_H && e->modifiers() ==
1753 #ifdef Q_OS_DARWIN
1754         Qt::MetaModifier
1755 #else
1756         Qt::ControlModifier
1757 #endif
1758         ) {
1759         universalHelper();
1760         e->accept();
1761         return;
1762     }
1763
1764     if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
1765         if (cursorMoveKeyEvent(e))
1766             ;
1767         else {
1768             QTextCursor cursor = textCursor();
1769             bool cursorWithinSnippet = false;
1770             if (d->m_snippetOverlay->isVisible()
1771                 && (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) {
1772                 cursorWithinSnippet = d->snippetCheckCursor(cursor);
1773             }
1774             if (cursorWithinSnippet)
1775                 cursor.beginEditBlock();
1776
1777             QPlainTextEdit::keyPressEvent(e);
1778
1779             if (cursorWithinSnippet) {
1780                 cursor.endEditBlock();
1781                 d->m_snippetOverlay->updateEquivalentSelections(textCursor());
1782             }
1783         }
1784     } else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
1785         QTextCursor cursor = textCursor();
1786         QString text = e->text();
1787         const QString &autoText = d->m_autoCompleter->autoComplete(cursor, text);
1788
1789         QChar electricChar;
1790         if (d->m_document->tabSettings().m_autoIndent) {
1791             foreach (QChar c, text) {
1792                 if (d->m_indenter->isElectricCharacter(c)) {
1793                     electricChar = c;
1794                     break;
1795                 }
1796             }
1797         }
1798
1799         bool cursorWithinSnippet = false;
1800         if (d->m_snippetOverlay->isVisible())
1801             cursorWithinSnippet = d->snippetCheckCursor(cursor);
1802
1803         bool doEditBlock = !electricChar.isNull() || !autoText.isEmpty() || cursorWithinSnippet;
1804         if (doEditBlock)
1805             cursor.beginEditBlock();
1806
1807         cursor.insertText(text);
1808
1809         if (!autoText.isEmpty()) {
1810             int pos = cursor.position();
1811             cursor.insertText(autoText);
1812             //Select the inserted text, to be able to re-indent the inserted text
1813             cursor.setPosition(pos, QTextCursor::KeepAnchor);
1814         }
1815         if (!electricChar.isNull() && d->m_autoCompleter->contextAllowsElectricCharacters(cursor))
1816             indent(document(), cursor, electricChar);
1817         if (!autoText.isEmpty()) {
1818             if (d->m_document->tabSettings().m_autoIndent)
1819                 reindent(document(), cursor);
1820             cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
1821         }
1822
1823         if (doEditBlock) {
1824             cursor.endEditBlock();
1825             if (cursorWithinSnippet)
1826                 d->m_snippetOverlay->updateEquivalentSelections(textCursor());
1827         }
1828
1829         setTextCursor(cursor);
1830     }
1831
1832     skip_event:
1833     if (!ro && e->key() == Qt::Key_Delete && d->m_parenthesesMatchingEnabled)
1834         d->m_parenthesesMatchingTimer->start(50);
1835
1836
1837     if (!ro && d->m_contentsChanged && !e->text().isEmpty() && e->text().at(0).isPrint()) {
1838         maybeRequestAutoCompletion(e->text().at(0));
1839     }
1840
1841 }
1842
1843 void BaseTextEditorWidget::maybeRequestAutoCompletion(const QChar &ch)
1844 {
1845     if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) {
1846         if (CompletionSupport::instance()->isActive())
1847             d->m_requestAutoCompletionTimer->stop();
1848         else {
1849             d->m_requestAutoCompletionRevision = document()->revision();
1850             d->m_requestAutoCompletionPosition = position();
1851             d->m_requestAutoCompletionTimer->start();
1852         }
1853     } else {
1854         d->m_requestAutoCompletionTimer->stop();
1855         CompletionSupport::instance()->complete(editor(), SemanticCompletion, false);
1856     }
1857 }
1858
1859 void BaseTextEditorWidget::_q_requestAutoCompletion()
1860 {
1861     d->m_requestAutoCompletionTimer->stop();
1862
1863     if (CompletionSupport::instance()->isActive())
1864         return;
1865
1866     if (d->m_requestAutoCompletionRevision == document()->revision()
1867             && d->m_requestAutoCompletionPosition == position())
1868         CompletionSupport::instance()->complete(editor(), SemanticCompletion, false);
1869 }
1870
1871 void BaseTextEditorWidget::insertCodeSnippet(const QTextCursor &cursor_arg, const QString &snippet)
1872 {
1873     if ((snippet.count(Snippet::kVariableDelimiter) % 2) != 0) {
1874         qWarning() << "invalid snippet";
1875         return;
1876     }
1877
1878     QList<QTextEdit::ExtraSelection> selections;
1879
1880     QTextCursor cursor = cursor_arg;
1881     cursor.beginEditBlock();
1882     cursor.removeSelectedText();
1883     const int startCursorPosition = cursor.position();
1884
1885     int pos = 0;
1886     QMap<int, int> positions;
1887
1888     while (pos < snippet.size()) {
1889         if (snippet.at(pos) != Snippet::kVariableDelimiter) {
1890             const int start = pos;
1891             do { ++pos; }
1892             while (pos < snippet.size() && snippet.at(pos) != Snippet::kVariableDelimiter);
1893             cursor.insertText(snippet.mid(start, pos - start));
1894         } else {
1895             // the start of a place holder.
1896             const int start = ++pos;
1897             for (; pos < snippet.size(); ++pos) {
1898                 if (snippet.at(pos) == Snippet::kVariableDelimiter)
1899                     break;
1900             }
1901
1902             Q_ASSERT(pos < snippet.size());
1903             Q_ASSERT(snippet.at(pos) == Snippet::kVariableDelimiter);
1904
1905             const QString textToInsert = snippet.mid(start, pos - start);
1906
1907             int cursorPosition = cursor.position();
1908             cursor.insertText(textToInsert);
1909
1910             if (textToInsert.isEmpty()) {
1911                 positions.insert(cursorPosition, 0);
1912             } else {
1913                 positions.insert(cursorPosition, textToInsert.length());
1914             }
1915
1916             ++pos;
1917         }
1918     }
1919
1920     QMapIterator<int,int> it(positions);
1921     while (it.hasNext()) {
1922         it.next();
1923         int length = it.value();
1924         int position = it.key();
1925
1926         QTextCursor tc(document());
1927         tc.setPosition(position);
1928         tc.setPosition(position + length, QTextCursor::KeepAnchor);
1929         QTextEdit::ExtraSelection selection;
1930         selection.cursor = tc;
1931         selection.format = (length ? d->m_occurrencesFormat : d->m_occurrenceRenameFormat);
1932         selections.append(selection);
1933     }
1934
1935     cursor.setPosition(startCursorPosition, QTextCursor::KeepAnchor);
1936     indent(cursor.document(), cursor, QChar());
1937     cursor.endEditBlock();
1938
1939     setExtraSelections(BaseTextEditorWidget::SnippetPlaceholderSelection, selections);
1940
1941     if (! selections.isEmpty()) {
1942         const QTextEdit::ExtraSelection &selection = selections.first();
1943
1944         cursor = textCursor();
1945         if (selection.cursor.hasSelection()) {
1946             cursor.setPosition(selection.cursor.selectionStart());
1947             cursor.setPosition(selection.cursor.selectionEnd(), QTextCursor::KeepAnchor);
1948         } else {
1949             cursor.setPosition(selection.cursor.position());
1950         }
1951         setTextCursor(cursor);
1952     }
1953
1954 }
1955
1956 void BaseTextEditorWidget::universalHelper()
1957 {
1958     // Test function for development. Place your new fangled experiment here to
1959     // give it proper scrutiny before pushing it onto others.
1960 }
1961
1962 void BaseTextEditorWidget::setTextCursor(const QTextCursor &cursor)
1963 {
1964     // workaround for QTextControl bug
1965     bool selectionChange = cursor.hasSelection() || textCursor().hasSelection();
1966     QTextCursor c = cursor;
1967     c.setVisualNavigation(true);
1968     QPlainTextEdit::setTextCursor(c);
1969     if (selectionChange)
1970         slotSelectionChanged();
1971 }
1972
1973 void BaseTextEditorWidget::gotoLine(int line, int column)
1974 {
1975     d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history
1976     const int blockNumber = line - 1;
1977     const QTextBlock &block = document()->findBlockByNumber(blockNumber);
1978     if (block.isValid()) {
1979         QTextCursor cursor(block);
1980         if (column > 0) {
1981             cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column);
1982         } else {
1983             int pos = cursor.position();
1984             while (characterAt(pos).category() == QChar::Separator_Space) {
1985                 ++pos;
1986             }
1987             cursor.setPosition(pos);
1988         }
1989         setTextCursor(cursor);
1990         centerCursor();
1991     }
1992     saveCurrentCursorPositionForNavigation();
1993 }
1994
1995 int BaseTextEditorWidget::position(ITextEditor::PositionOperation posOp, int at) const
1996 {
1997     QTextCursor tc = textCursor();
1998
1999     if (at != -1)
2000         tc.setPosition(at);
2001
2002     if (posOp == ITextEditor::Current)
2003         return tc.position();
2004
2005     switch (posOp) {
2006     case ITextEditor::EndOfLine:
2007         tc.movePosition(QTextCursor::EndOfLine);
2008         return tc.position();
2009     case ITextEditor::StartOfLine:
2010         tc.movePosition(QTextCursor::StartOfLine);
2011         return tc.position();
2012     case ITextEditor::Anchor:
2013         if (tc.hasSelection())
2014             return tc.anchor();
2015         break;
2016     case ITextEditor::EndOfDoc:
2017         tc.movePosition(QTextCursor::End);
2018         return tc.position();
2019     default:
2020         break;
2021     }
2022
2023     return -1;
2024 }
2025
2026 void BaseTextEditorWidget::convertPosition(int pos, int *line, int *column) const
2027 {
2028     QTextBlock block = document()->findBlock(pos);
2029     if (!block.isValid()) {
2030         (*line) = -1;
2031         (*column) = -1;
2032     } else {
2033         (*line) = block.blockNumber() + 1;
2034         (*column) = pos - block.position();
2035     }
2036 }
2037
2038 QChar BaseTextEditorWidget::characterAt(int pos) const
2039 {
2040     return document()->characterAt(pos);
2041 }
2042
2043 bool BaseTextEditorWidget::event(QEvent *e)
2044 {
2045     d->m_contentsChanged = false;
2046     switch (e->type()) {
2047     case QEvent::ShortcutOverride:
2048         if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_snippetOverlay->isVisible()) {
2049             e->accept();
2050             return true;
2051         }
2052         e->ignore(); // we are a really nice citizen
2053         return true;
2054         break;
2055     default:
2056         break;
2057     }
2058
2059     return QPlainTextEdit::event(e);
2060 }
2061
2062 void BaseTextEditorWidget::duplicateFrom(BaseTextEditorWidget *widget)
2063 {
2064     if (this == widget)
2065         return;
2066     setDisplayName(widget->displayName());
2067     d->m_revisionsVisible = widget->d->m_revisionsVisible;
2068     if (d->m_document == widget->d->m_document)
2069         return;
2070     d->setupDocumentSignals(widget->d->m_document);
2071     d->m_document = widget->d->m_document;
2072 }
2073
2074 QString BaseTextEditorWidget::displayName() const
2075 {
2076     return d->m_displayName;
2077 }
2078
2079 void BaseTextEditorWidget::setDisplayName(const QString &title)
2080 {
2081     d->m_displayName = title;
2082     emit changed();
2083 }
2084
2085 BaseTextDocument *BaseTextEditorWidget::baseTextDocument() const
2086 {
2087     return d->m_document;
2088 }
2089
2090 void BaseTextEditorWidget::setBaseTextDocument(BaseTextDocument *doc)
2091 {
2092     if (doc) {
2093         d->setupDocumentSignals(doc);
2094         d->m_document = doc;
2095     }
2096 }
2097
2098 void BaseTextEditorWidget::documentAboutToBeReloaded()
2099 {
2100     //memorize cursor position
2101     d->m_tempState = saveState();
2102
2103     // remove extra selections (loads of QTextCursor objects)
2104
2105     for (int i = 0; i < NExtraSelectionKinds; ++i)
2106         d->m_extraSelections[i].clear();
2107     QPlainTextEdit::setExtraSelections(QList<QTextEdit::ExtraSelection>());
2108
2109     // clear all overlays
2110     d->m_overlay->clear();
2111     d->m_snippetOverlay->clear();
2112     d->m_searchResultOverlay->clear();
2113     d->m_refactorOverlay->clear();
2114 }
2115
2116 void BaseTextEditorWidget::documentReloaded()
2117 {
2118     // restore cursor position
2119     restoreState(d->m_tempState);
2120 }
2121
2122 QByteArray BaseTextEditorWidget::saveState() const
2123 {
2124     QByteArray state;
2125     QDataStream stream(&state, QIODevice::WriteOnly);
2126     stream << 1; // version number
2127     stream << verticalScrollBar()->value();
2128     stream << horizontalScrollBar()->value();
2129     int line, column;
2130     convertPosition(textCursor().position(), &line, &column);
2131     stream << line;
2132     stream << column;
2133
2134     // store code folding state
2135     QList<int> foldedBlocks;
2136     QTextBlock block = document()->firstBlock();
2137     while (block.isValid()) {
2138         if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->folded()) {
2139             int number = block.blockNumber();
2140             foldedBlocks += number;
2141         }
2142         block = block.next();
2143     }
2144     stream << foldedBlocks;
2145
2146     return state;
2147 }
2148
2149 bool BaseTextEditorWidget::restoreState(const QByteArray &state)
2150 {
2151     if (state.isEmpty()) {
2152         if (d->m_displaySettings.m_autoFoldFirstComment)
2153             d->foldLicenseHeader();
2154         return false;
2155     }
2156     int version;
2157     int vval;
2158     int hval;
2159     int lval;
2160     int cval;
2161     QDataStream stream(state);
2162     stream >> version;
2163     stream >> vval;
2164     stream >> hval;
2165     stream >> lval;
2166     stream >> cval;
2167
2168     if (version >= 1) {
2169         QList<int> collapsedBlocks;
2170         stream >> collapsedBlocks;
2171         QTextDocument *doc = document();
2172         foreach(int blockNumber, collapsedBlocks) {
2173             QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
2174             if (block.isValid())
2175                 BaseTextDocumentLayout::doFoldOrUnfold(block, false);
2176         }
2177     } else {
2178         if (d->m_displaySettings.m_autoFoldFirstComment)
2179             d->foldLicenseHeader();
2180     }
2181
2182     d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
2183     gotoLine(lval, cval);
2184     verticalScrollBar()->setValue(vval);
2185     horizontalScrollBar()->setValue(hval);
2186     saveCurrentCursorPositionForNavigation();
2187     return true;
2188 }
2189
2190 void BaseTextEditorWidget::setDefaultPath(const QString &defaultPath)
2191 {
2192     baseTextDocument()->setDefaultPath(defaultPath);
2193 }
2194
2195 void BaseTextEditorWidget::setSuggestedFileName(const QString &suggestedFileName)
2196 {
2197     baseTextDocument()->setSuggestedFileName(suggestedFileName);
2198 }
2199
2200 void BaseTextEditorWidget::setParenthesesMatchingEnabled(bool b)
2201 {
2202     d->m_parenthesesMatchingEnabled = b;
2203 }
2204
2205 bool BaseTextEditorWidget::isParenthesesMatchingEnabled() const
2206 {
2207     return d->m_parenthesesMatchingEnabled;
2208 }
2209
2210 void BaseTextEditorWidget::setHighlightCurrentLine(bool b)
2211 {
2212     d->m_highlightCurrentLine = b;
2213     updateCurrentLineHighlight();
2214 }
2215
2216 bool BaseTextEditorWidget::highlightCurrentLine() const
2217 {
2218     return d->m_highlightCurrentLine;
2219 }
2220
2221 void BaseTextEditorWidget::setLineNumbersVisible(bool b)
2222 {
2223     d->m_lineNumbersVisible = b;
2224     slotUpdateExtraAreaWidth();
2225 }
2226
2227 bool BaseTextEditorWidget::lineNumbersVisible() const
2228 {
2229     return d->m_lineNumbersVisible;
2230 }
2231
2232 void BaseTextEditorWidget::setMarksVisible(bool b)
2233 {
2234     d->m_marksVisible = b;
2235     slotUpdateExtraAreaWidth();
2236 }
2237
2238 bool BaseTextEditorWidget::marksVisible() const
2239 {
2240     return d->m_marksVisible;
2241 }
2242
2243 void BaseTextEditorWidget::setRequestMarkEnabled(bool b)
2244 {
2245     d->m_requestMarkEnabled = b;
2246 }
2247
2248 bool BaseTextEditorWidget::requestMarkEnabled() const
2249 {
2250     return d->m_requestMarkEnabled;
2251 }
2252
2253 void BaseTextEditorWidget::setLineSeparatorsAllowed(bool b)
2254 {
2255     d->m_lineSeparatorsAllowed = b;
2256 }
2257
2258 bool BaseTextEditorWidget::lineSeparatorsAllowed() const
2259 {
2260     return d->m_lineSeparatorsAllowed;
2261 }
2262
2263 void BaseTextEditorWidget::updateCodeFoldingVisible()
2264 {
2265     const bool visible = d->m_codeFoldingSupported && d->m_displaySettings.m_displayFoldingMarkers;
2266     if (d->m_codeFoldingVisible != visible) {
2267         d->m_codeFoldingVisible = visible;
2268         slotUpdateExtraAreaWidth();
2269     }
2270 }
2271
2272 bool BaseTextEditorWidget::codeFoldingVisible() const
2273 {
2274     return d->m_codeFoldingVisible;
2275 }
2276
2277 /**
2278  * Sets whether code folding is supported by the syntax highlighter. When not
2279  * supported (the default), this makes sure the code folding is not shown.
2280  *
2281  * Needs to be called before calling setCodeFoldingVisible.
2282  */
2283 void BaseTextEditorWidget::setCodeFoldingSupported(bool b)
2284 {
2285     d->m_codeFoldingSupported = b;
2286     updateCodeFoldingVisible();
2287 }
2288
2289 bool BaseTextEditorWidget::codeFoldingSupported() const
2290 {
2291     return d->m_codeFoldingSupported;
2292 }
2293
2294 void BaseTextEditorWidget::setMouseNavigationEnabled(bool b)
2295 {
2296     d->m_behaviorSettings.m_mouseNavigation = b;
2297 }
2298
2299 bool BaseTextEditorWidget::mouseNavigationEnabled() const
2300 {
2301     return d->m_behaviorSettings.m_mouseNavigation;
2302 }
2303
2304 void BaseTextEditorWidget::setScrollWheelZoomingEnabled(bool b)
2305 {
2306     d->m_behaviorSettings.m_scrollWheelZooming = b;
2307 }
2308
2309 bool BaseTextEditorWidget::scrollWheelZoomingEnabled() const
2310 {
2311     return d->m_behaviorSettings.m_scrollWheelZooming;
2312 }
2313
2314 void BaseTextEditorWidget::setRevisionsVisible(bool b)
2315 {
2316     d->m_revisionsVisible = b;
2317     slotUpdateExtraAreaWidth();
2318 }
2319
2320 bool BaseTextEditorWidget::revisionsVisible() const
2321 {
2322     return d->m_revisionsVisible;
2323 }
2324
2325 void BaseTextEditorWidget::setVisibleWrapColumn(int column)
2326 {
2327     d->m_visibleWrapColumn = column;
2328     viewport()->update();
2329 }
2330
2331 int BaseTextEditorWidget::visibleWrapColumn() const
2332 {
2333     return d->m_visibleWrapColumn;
2334 }
2335
2336 void BaseTextEditorWidget::setIndenter(Indenter *indenter)
2337 {
2338     // clear out existing code formatter data
2339     for (QTextBlock it = document()->begin(); it.isValid(); it = it.next()) {
2340         TextEditor::TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(it);
2341         if (userData)
2342             userData->setCodeFormatterData(0);
2343     }
2344     d->m_indenter.reset(indenter);
2345 }
2346
2347 Indenter *BaseTextEditorWidget::indenter() const
2348 {
2349     return d->m_indenter.data();
2350 }
2351
2352 void BaseTextEditorWidget::setAutoCompleter(AutoCompleter *autoCompleter)
2353 {
2354     d->m_autoCompleter.reset(autoCompleter);
2355 }
2356
2357 AutoCompleter *BaseTextEditorWidget::autoCompleter() const
2358 {
2359     return d->m_autoCompleter.data();
2360 }
2361
2362 //--------- BaseTextEditorPrivate -----------
2363
2364 BaseTextEditorPrivate::BaseTextEditorPrivate()
2365     :
2366     m_lastScrollPos(-1),
2367     m_lineNumber(-1),
2368     q(0),
2369     m_contentsChanged(false),
2370     m_lastCursorChangeWasInteresting(false),
2371     m_document(new BaseTextDocument),
2372     m_parenthesesMatchingEnabled(false),
2373     m_updateTimer(0),
2374     m_formatRange(false),
2375     m_parenthesesMatchingTimer(0),
2376     m_extraArea(0),
2377     extraAreaSelectionAnchorBlockNumber(-1),
2378     extraAreaToggleMarkBlockNumber(-1),
2379     extraAreaHighlightFoldedBlockNumber(-1),
2380     m_overlay(0),
2381     m_snippetOverlay(0),
2382     m_searchResultOverlay(0),
2383     m_refactorOverlay(0),
2384     visibleFoldedBlockNumber(-1),
2385     suggestedVisibleFoldedBlockNumber(-1),
2386     m_mouseOnFoldedMarker(false),
2387     m_marksVisible(false),
2388     m_codeFoldingVisible(false),
2389     m_codeFoldingSupported(false),
2390     m_revisionsVisible(false),
2391     m_lineNumbersVisible(true),
2392     m_highlightCurrentLine(true),
2393     m_requestMarkEnabled(true),
2394     m_lineSeparatorsAllowed(false),
2395     m_visibleWrapColumn(0),
2396     m_linkPressed(false),
2397     m_delayedUpdateTimer(0),
2398     m_editor(0),
2399     m_actionHack(0),
2400     m_inBlockSelectionMode(false),
2401     m_moveLineUndoHack(false),
2402     m_findScopeVerticalBlockSelectionFirstColumn(-1),
2403     m_findScopeVerticalBlockSelectionLastColumn(-1),
2404     m_highlightBlocksTimer(0),
2405     m_requestAutoCompletionRevision(0),
2406     m_requestAutoCompletionPosition(0),
2407     m_requestAutoCompletionTimer(0),
2408     m_cursorBlockNumber(-1),
2409     m_autoCompleter(new AutoCompleter),
2410     m_indenter(new Indenter)
2411 {
2412 }
2413
2414 BaseTextEditorPrivate::~BaseTextEditorPrivate()
2415 {
2416 }
2417
2418 void BaseTextEditorPrivate::setupDocumentSignals(BaseTextDocument *document)
2419 {
2420     BaseTextDocument *oldDocument = q->baseTextDocument();
2421     if (oldDocument) {
2422         q->disconnect(oldDocument->document(), 0, q, 0);
2423         q->disconnect(oldDocument, 0, q, 0);
2424     }
2425
2426     QTextDocument *doc = document->document();
2427     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
2428     if (!documentLayout) {
2429         QTextOption opt = doc->defaultTextOption();
2430         opt.setTextDirection(Qt::LeftToRight);
2431         opt.setFlags(opt.flags() | QTextOption::IncludeTrailingSpaces
2432                 | QTextOption::AddSpaceForLineAndParagraphSeparators
2433                 );
2434         doc->setDefaultTextOption(opt);
2435         documentLayout = new BaseTextDocumentLayout(doc);
2436         doc->setDocumentLayout(documentLayout);
2437     }
2438
2439     q->setDocument(doc);
2440     q->setCursorWidth(2); // Applies to the document layout
2441
2442     QObject::connect(documentLayout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(slotUpdateBlockNotify(QTextBlock)));
2443     QObject::connect(q, SIGNAL(requestBlockUpdate(QTextBlock)), documentLayout, SIGNAL(updateBlock(QTextBlock)));
2444     QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(changed()));
2445     QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q,
2446         SLOT(editorContentsChange(int,int,int)), Qt::DirectConnection);
2447     QObject::connect(document, SIGNAL(changed()), q, SIGNAL(changed()));
2448     QObject::connect(document, SIGNAL(titleChanged(QString)), q, SLOT(setDisplayName(const QString &)));
2449     QObject::connect(document, SIGNAL(aboutToReload()), q, SLOT(documentAboutToBeReloaded()));
2450     QObject::connect(document, SIGNAL(reloaded()), q, SLOT(documentReloaded()));
2451     q->slotUpdateExtraAreaWidth();
2452 }
2453
2454
2455 bool BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
2456 {
2457     if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
2458         return false;
2459
2460     QTextCursor start = cursor;
2461     start.setPosition(cursor.selectionStart());
2462     QTextCursor end = cursor;
2463     end.setPosition(cursor.selectionEnd());
2464     if (!m_snippetOverlay->hasCursorInSelection(start)
2465         || !m_snippetOverlay->hasCursorInSelection(end)
2466         || m_snippetOverlay->hasFirstSelectionBeginMoved()) {
2467         m_snippetOverlay->setVisible(false);
2468         m_snippetOverlay->clear();
2469         return false;
2470     }
2471     return true;
2472 }
2473
2474 void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
2475 {
2476     if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
2477         return;
2478     QTextCursor cursor = q->textCursor();
2479     OverlaySelection final;
2480     if (forward) {
2481         for (int i = 0; i < m_snippetOverlay->selections().count(); ++i){
2482             const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
2483             if (selection.m_cursor_begin.position() >= cursor.position()
2484                 && selection.m_cursor_end.position() > cursor.position()) {
2485                 final = selection;
2486                 break;
2487             }
2488         }
2489     } else {
2490         for (int i = m_snippetOverlay->selections().count()-1; i >= 0; --i){
2491             const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
2492             if (selection.m_cursor_end.position() < cursor.position()) {
2493                 final = selection;
2494                 break;
2495             }
2496         }
2497
2498     }
2499     if (final.m_cursor_begin.isNull())
2500         final = forward ? m_snippetOverlay->selections().first() : m_snippetOverlay->selections().last();
2501
2502     if (final.m_cursor_begin.position() == final.m_cursor_end.position()) { // empty tab stop
2503         cursor.setPosition(final.m_cursor_end.position());
2504     } else {
2505         cursor.setPosition(final.m_cursor_begin.position());
2506         cursor.setPosition(final.m_cursor_end.position(), QTextCursor::KeepAnchor);
2507     }
2508     q->setTextCursor(cursor);
2509 }
2510
2511 // Calculate global position for a tooltip considering the left extra area.
2512 QPoint BaseTextEditorWidget::toolTipPosition(const QTextCursor &c) const
2513 {
2514     const QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
2515     return cursorPos + QPoint(d->m_extraArea->width(),
2516 #ifdef Q_WS_WIN
2517     -24
2518 #else
2519     -16
2520 #endif
2521     );
2522 }
2523
2524 bool BaseTextEditorWidget::viewportEvent(QEvent *event)
2525 {
2526     d->m_contentsChanged = false;
2527     if (event->type() == QEvent::ContextMenu) {
2528         const QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event);
2529         if (ce->reason() == QContextMenuEvent::Mouse && !textCursor().hasSelection())
2530             setTextCursor(cursorForPosition(ce->pos()));
2531     } else if (event->type() == QEvent::ToolTip) {
2532         const QHelpEvent *he = static_cast<QHelpEvent*>(event);
2533         if (QApplication::keyboardModifiers() & Qt::ControlModifier)
2534             return true; // eat tooltip event when control is pressed
2535         const QPoint &pos = he->pos();
2536
2537         RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(pos);
2538         if (refactorMarker.isValid() && !refactorMarker.tooltip.isEmpty()) {
2539             ToolTip::instance()->show(he->globalPos(),
2540                                       TextContent(refactorMarker.tooltip),
2541                                       viewport(),
2542                                       refactorMarker.rect);
2543             return true;
2544         }
2545
2546         // Allow plugins to show tooltips
2547         const QTextCursor c = cursorForPosition(pos);
2548         const QPoint toolTipPoint = toolTipPosition(c);
2549         bool handled = false;
2550         BaseTextEditor *ed = editor();
2551         emit ed->tooltipOverrideRequested(ed, toolTipPoint, c.position(), &handled);
2552         if (!handled)
2553             emit ed->tooltipRequested(ed, toolTipPoint, c.position());
2554         return true;
2555     }
2556     return QPlainTextEdit::viewportEvent(event);
2557 }
2558
2559
2560 void BaseTextEditorWidget::resizeEvent(QResizeEvent *e)
2561 {
2562     QPlainTextEdit::resizeEvent(e);
2563     QRect cr = rect();
2564     d->m_extraArea->setGeometry(
2565         QStyle::visualRect(layoutDirection(), cr,
2566                            QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height())));
2567 }
2568
2569 QRect BaseTextEditorWidget::foldBox()
2570 {
2571     if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightFoldedBlockNumber < 0)
2572         return QRect();
2573
2574     QTextBlock begin = document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last());
2575
2576     QTextBlock end = document()->findBlockByNumber(d->m_highlightBlocksInfo.close.first());
2577     if (!begin.isValid() || !end.isValid())
2578         return QRect();
2579     QRectF br = blockBoundingGeometry(begin).translated(contentOffset());
2580     QRectF er = blockBoundingGeometry(end).translated(contentOffset());
2581
2582     return QRect(d->m_extraArea->width() - foldBoxWidth(fontMetrics()),
2583                  int(br.top()),
2584                  foldBoxWidth(fontMetrics()),
2585                  er.bottom() - br.top());
2586 }
2587
2588 QTextBlock BaseTextEditorWidget::foldedBlockAt(const QPoint &pos, QRect *box) const
2589 {
2590     QPointF offset(contentOffset());
2591     QTextBlock block = firstVisibleBlock();
2592     qreal top = blockBoundingGeometry(block).translated(offset).top();
2593     qreal bottom = top + blockBoundingRect(block).height();
2594
2595     int viewportHeight = viewport()->height();
2596
2597     while (block.isValid() && top <= viewportHeight) {
2598         QTextBlock nextBlock = block.next();
2599         if (block.isVisible() && bottom >= 0) {
2600             if (nextBlock.isValid() && !nextBlock.isVisible()) {
2601                 QTextLayout *layout = block.layout();
2602                 QTextLine line = layout->lineAt(layout->lineCount()-1);
2603                 QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
2604                 lineRect.adjust(0, 0, -1, -1);
2605
2606                 QRectF collapseRect(lineRect.right() + 12,
2607                                     lineRect.top(),
2608                                     fontMetrics().width(QLatin1String(" {...}; ")),
2609                                     lineRect.height());
2610                 if (collapseRect.contains(pos)) {
2611                     QTextBlock result = block;
2612                     if (box)
2613                         *box = collapseRect.toAlignedRect();
2614                     return result;
2615                 } else {
2616                     block = nextBlock;
2617                     while (nextBlock.isValid() && !nextBlock.isVisible()) {
2618                         block = nextBlock;
2619                         nextBlock = block.next();
2620                     }
2621                 }
2622             }
2623         }
2624
2625         block = nextBlock;
2626         top = bottom;
2627         bottom = top + blockBoundingRect(block).height();
2628     }
2629     return QTextBlock();
2630 }
2631
2632 void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
2633                                                    TextEditorOverlay *overlay)
2634 {
2635     if (m_searchExpr.isEmpty())
2636         return;
2637
2638     int blockPosition = block.position();
2639
2640     QTextCursor cursor = q->textCursor();
2641     QString text = block.text();
2642     text.replace(QChar::Nbsp, QLatin1Char(' '));
2643     int idx = -1;
2644     int l = 1;
2645
2646     while (idx < text.length()) {
2647         idx = m_searchExpr.indexIn(text, idx + l);
2648         if (idx < 0)
2649             break;
2650         l = m_searchExpr.matchedLength();
2651         if (l == 0)
2652             break;
2653         if ((m_findFlags & Find::FindWholeWords)
2654             && ((idx && text.at(idx-1).isLetterOrNumber())
2655                 || (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
2656             continue;
2657
2658         if (!q->inFindScope(blockPosition + idx, blockPosition + idx + l))
2659             continue;
2660
2661         overlay->addOverlaySelection(blockPosition + idx,
2662                                      blockPosition + idx + l,
2663                                      m_searchResultFormat.background().color().darker(120),
2664                                      QColor(),
2665                                      (idx == cursor.selectionStart() - blockPosition
2666                                       && idx + l == cursor.selectionEnd() - blockPosition)?
2667                                      TextEditorOverlay::DropShadow : 0);
2668
2669     }
2670 }
2671
2672
2673 namespace TextEditor {
2674     namespace Internal {
2675         struct BlockSelectionData {
2676             int selectionIndex;
2677             int selectionStart;
2678             int selectionEnd;
2679             int firstColumn;
2680             int lastColumn;
2681         };
2682     }
2683 }
2684
2685 void BaseTextEditorPrivate::clearBlockSelection()
2686 {
2687     if (m_inBlockSelectionMode) {
2688         m_inBlockSelectionMode = false;
2689         m_blockSelection.clear();
2690         QTextCursor cursor = q->textCursor();
2691         cursor.clearSelection();
2692         q->setTextCursor(cursor);
2693     }
2694 }
2695
2696 QString BaseTextEditorPrivate::copyBlockSelection()
2697 {
2698     QString selection;
2699     QTextCursor cursor = q->textCursor();
2700     if (!m_inBlockSelectionMode)
2701         return selection;
2702     const TabSettings &ts = q->tabSettings();
2703     QTextBlock block = m_blockSelection.firstBlock.block();
2704     QTextBlock lastBlock = m_blockSelection.lastBlock.block();
2705     for (;;) {
2706         QString text = block.text();
2707         int startOffset = 0;
2708         int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
2709         int endOffset = 0;
2710         int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
2711
2712         if (startPos == endPos) {
2713             selection += QString(endOffset - startOffset, QLatin1Char(' '));
2714         } else {
2715             if (startOffset < 0)
2716                 selection += QString(-startOffset, QLatin1Char(' '));
2717             if (endOffset < 0)
2718                 --endPos;
2719             selection += text.mid(startPos, endPos - startPos);
2720             if (endOffset < 0) {
2721                 selection += QString(ts.m_tabSize + endOffset, QLatin1Char(' '));
2722             } else if (endOffset > 0) {
2723                 selection += QString(endOffset, QLatin1Char(' '));
2724             }
2725         }
2726         if (block == lastBlock)
2727             break;
2728         selection += QLatin1Char('\n');
2729         block = block.next();
2730     }
2731     return selection;
2732 }
2733
2734 void BaseTextEditorPrivate::removeBlockSelection(const QString &text)
2735 {
2736     QTextCursor cursor = q->textCursor();
2737     if (!cursor.hasSelection() || !m_inBlockSelectionMode)
2738         return;
2739
2740     int cursorPosition = cursor.selectionStart();
2741     cursor.clearSelection();
2742     cursor.beginEditBlock();
2743
2744     const TabSettings &ts = q->tabSettings();
2745     QTextBlock block = m_blockSelection.firstBlock.block();
2746     QTextBlock lastBlock = m_blockSelection.lastBlock.block();
2747     for (;;) {
2748         QString text = block.text();
2749         int startOffset = 0;
2750         int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
2751         int endOffset = 0;
2752         int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
2753
2754         cursor.setPosition(block.position() + startPos);
2755         cursor.setPosition(block.position() + endPos, QTextCursor::KeepAnchor);
2756         cursor.removeSelectedText();
2757
2758         if (startOffset < 0)
2759             cursor.insertText(QString(ts.m_tabSize + startOffset, QLatin1Char(' ')));
2760         if (endOffset < 0)
2761             cursor.insertText(QString(-endOffset, QLatin1Char(' ')));
2762
2763         if (block == lastBlock)
2764             break;
2765         block = block.next();
2766     }
2767
2768     cursor.setPosition(cursorPosition);
2769     if (!text.isEmpty())
2770         cursor.insertText(text);
2771     cursor.endEditBlock();
2772     q->setTextCursor(cursor);
2773 }
2774
2775 void BaseTextEditorPrivate::moveCursorVisible(bool ensureVisible)
2776 {
2777     QTextCursor cursor = q->textCursor();
2778     if (!cursor.block().isVisible()) {
2779         cursor.setVisualNavigation(true);
2780         cursor.movePosition(QTextCursor::Up);
2781         q->setTextCursor(cursor);
2782     }
2783     if (ensureVisible)
2784         q->ensureCursorVisible();
2785 }
2786
2787 static QColor blendColors(const QColor &a, const QColor &b, int alpha)
2788 {
2789     return QColor((a.red()   * (256 - alpha) + b.red()   * alpha) / 256,
2790                   (a.green() * (256 - alpha) + b.green() * alpha) / 256,
2791                   (a.blue()  * (256 - alpha) + b.blue()  * alpha) / 256);
2792 }
2793
2794 static QColor calcBlendColor(const QColor &baseColor, int level, int count)
2795 {
2796     QColor color80;
2797     QColor color90;
2798
2799     if (baseColor.value() > 128) {
2800         const int f90 = 15;
2801         const int f80 = 30;
2802         color80.setRgb(qMax(0, baseColor.red() - f80),
2803                        qMax(0, baseColor.green() - f80),
2804                        qMax(0, baseColor.blue() - f80));
2805         color90.setRgb(qMax(0, baseColor.red() - f90),
2806                        qMax(0, baseColor.green() - f90),
2807                        qMax(0, baseColor.blue() - f90));
2808     } else {
2809         const int f90 = 20;
2810         const int f80 = 40;
2811         color80.setRgb(qMin(255, baseColor.red() + f80),
2812                        qMin(255, baseColor.green() + f80),
2813                        qMin(255, baseColor.blue() + f80));
2814         color90.setRgb(qMin(255, baseColor.red() + f90),
2815                        qMin(255, baseColor.green() + f90),
2816                        qMin(255, baseColor.blue() + f90));
2817     }
2818
2819     if (level == count)
2820         return baseColor;
2821     if (level == 0)
2822         return color80;
2823     if (level == count - 1)
2824         return color90;
2825
2826     const int blendFactor = level * (256 / (count - 2));
2827
2828     return blendColors(color80, color90, blendFactor);
2829 }
2830
2831 void BaseTextEditorWidget::paintEvent(QPaintEvent *e)
2832 {
2833     /*
2834       Here comes an almost verbatim copy of
2835       QPlainTextEdit::paintEvent() so we can adjust the extra
2836       selections dynamically to indicate all search results.
2837     */
2838     //begin QPlainTextEdit::paintEvent()
2839
2840     QPainter painter(viewport());
2841     QTextDocument *doc = document();
2842     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
2843     QTC_ASSERT(documentLayout, return);
2844
2845     QPointF offset(contentOffset());
2846     QTextBlock textCursorBlock = textCursor().block();
2847
2848     bool hasMainSelection = textCursor().hasSelection();
2849     bool suppressSyntaxInIfdefedOutBlock = (d->m_ifdefedOutFormat.foreground()
2850                                            != palette().foreground());
2851
2852     QRect er = e->rect();
2853     QRect viewportRect = viewport()->rect();
2854
2855     qreal lineX = 0;
2856
2857     if (d->m_visibleWrapColumn > 0) {
2858         // Don't use QFontMetricsF::averageCharWidth here, due to it returning
2859         // a fractional size even when this is not supported by the platform.
2860         lineX = QFontMetricsF(font()).width(QLatin1Char('x')) * d->m_visibleWrapColumn + offset.x() + 4;
2861
2862         if (lineX < viewportRect.width()) {
2863             const QBrush background = d->m_ifdefedOutFormat.background();
2864             painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()),
2865                              background);
2866
2867             const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white;
2868             const QPen pen = painter.pen();
2869             painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(),
2870                                        col, 32));
2871             painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom()));
2872             painter.setPen(pen);
2873         }
2874     }
2875
2876     // Set a brush origin so that the WaveUnderline knows where the wave started
2877     painter.setBrushOrigin(offset);
2878
2879 //    // keep right margin clean from full-width selection
2880 //    int maxX = offset.x() + qMax((qreal)viewportRect.width(), documentLayout->documentSize().width())
2881 //               - doc->documentMargin();
2882 //    er.setRight(qMin(er.right(), maxX));
2883 //    painter.setClipRect(er);
2884
2885     bool editable = !isReadOnly();
2886     QTextBlock block = firstVisibleBlock();
2887
2888     QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
2889
2890     if (!d->m_highlightBlocksInfo.isEmpty()) {
2891         const QColor baseColor = palette().base().color();
2892
2893         // extra pass for the block highlight
2894
2895         const int margin = 5;
2896         QTextBlock blockFP = block;
2897         QPointF offsetFP = offset;
2898         while (blockFP.isValid()) {
2899             QRectF r = blockBoundingRect(blockFP).translated(offsetFP);
2900
2901             int n = blockFP.blockNumber();
2902             int depth = 0;
2903             foreach (int i, d->m_highlightBlocksInfo.open)
2904                 if (n >= i)
2905                     ++depth;
2906             foreach (int i, d->m_highlightBlocksInfo.close)
2907                 if (n > i)
2908                     --depth;
2909
2910             int count = d->m_highlightBlocksInfo.count();
2911             if (count) {
2912                 for (int i = 0; i <= depth; ++i) {
2913                     const QColor &blendedColor = calcBlendColor(baseColor, i, count);
2914                     int vi = i > 0 ? d->m_highlightBlocksInfo.visualIndent.at(i-1) : 0;
2915                     QRectF oneRect = r;
2916                     oneRect.setWidth(viewport()->width());
2917                     oneRect.adjust(vi, 0, -8*i, 0);
2918                     if (oneRect.left() >= oneRect.right())
2919                         continue;
2920                     if (lineX > 0 && oneRect.left() < lineX && oneRect.right() > lineX) {
2921                         QRectF otherRect = r;
2922                         otherRect.setLeft(lineX + 1);
2923                         otherRect.setRight(oneRect.right());
2924                         oneRect.setRight(lineX - 1);
2925                         painter.fillRect(otherRect, blendedColor);
2926                     }
2927                     painter.fillRect(oneRect, blendedColor);
2928                 }
2929             }
2930             offsetFP.ry() += r.height();
2931
2932             if (offsetFP.y() > viewportRect.height() + margin)
2933                 break;
2934
2935             blockFP = blockFP.next();
2936             if (!blockFP.isVisible()) {
2937                 // invisible blocks do have zero line count
2938                 blockFP = doc->findBlockByLineNumber(blockFP.firstLineNumber());
2939             }
2940         }
2941     }
2942
2943     int blockSelectionIndex = -1;
2944
2945     if (d->m_inBlockSelectionMode
2946         && context.selections.count() && context.selections.last().cursor == textCursor()) {
2947         blockSelectionIndex = context.selections.size()-1;
2948         context.selections[blockSelectionIndex].format.clearBackground();
2949     }
2950
2951     QTextBlock visibleCollapsedBlock;
2952     QPointF visibleCollapsedBlockOffset;
2953
2954     QTextLayout *cursor_layout = 0;
2955     QPointF cursor_offset;
2956     int cursor_cpos = 0;
2957     QPen cursor_pen;
2958
2959     d->m_searchResultOverlay->clear();
2960     if (!d->m_searchExpr.isEmpty()) { // first pass for the search result overlays
2961
2962         const int margin = 5;
2963         QTextBlock blockFP = block;
2964         QPointF offsetFP = offset;
2965         while (blockFP.isValid()) {
2966             QRectF r = blockBoundingRect(blockFP).translated(offsetFP);
2967
2968             if (r.bottom() >= er.top() - margin && r.top() <= er.bottom() + margin) {
2969                 d->highlightSearchResults(blockFP,
2970                                           d->m_searchResultOverlay);
2971             }
2972             offsetFP.ry() += r.height();
2973
2974             if (offsetFP.y() > viewportRect.height() + margin)
2975                 break;
2976
2977             blockFP = blockFP.next();
2978             if (!blockFP.isVisible()) {
2979                 // invisible blocks do have zero line count
2980                 blockFP = doc->findBlockByLineNumber(blockFP.firstLineNumber());
2981             }
2982         }
2983
2984     } // end first pass
2985
2986
2987     { // extra pass for ifdefed out blocks
2988         QTextBlock blockIDO = block;
2989         QPointF offsetIDO = offset;
2990         while (blockIDO.isValid()) {
2991
2992             QRectF r = blockBoundingRect(blockIDO).translated(offsetIDO);
2993
2994             if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
2995                 if (BaseTextDocumentLayout::ifdefedOut(blockIDO)) {
2996                     QRectF rr = r;
2997                     rr.setRight(viewportRect.width() - offset.x());
2998                     if (lineX > 0)
2999                         rr.setRight(qMin(lineX, rr.right()));
3000                     painter.fillRect(rr, d->m_ifdefedOutFormat.background());
3001                 }
3002             }
3003             offsetIDO.ry() += r.height();
3004
3005             if (offsetIDO.y() > viewportRect.height())
3006                 break;
3007
3008             blockIDO = blockIDO.next();
3009             if (!blockIDO.isVisible()) {
3010                 // invisible blocks do have zero line count
3011                 blockIDO = doc->findBlockByLineNumber(blockIDO.firstLineNumber());
3012             }
3013
3014         }
3015     }
3016
3017     // possible extra pass for the block selection find scope
3018     if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn >= 0) {
3019         QTextBlock blockFS = block;
3020         QPointF offsetFS = offset;
3021         while (blockFS.isValid()) {
3022
3023             QRectF r = blockBoundingRect(blockFS).translated(offsetFS);
3024
3025             if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
3026
3027                 if (blockFS.position() >= d->m_findScopeStart.block().position()
3028                         && blockFS.position() <= d->m_findScopeEnd.block().position()) {
3029                     QTextLayout *layout = blockFS.layout();
3030                     QString text = blockFS.text();
3031                     const TabSettings &ts = tabSettings();
3032                     qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' '));
3033
3034                     int offset = 0;
3035                     int relativePos  =  ts.positionAtColumn(text,
3036                                                             d->m_findScopeVerticalBlockSelectionFirstColumn,
3037                                                             &offset);
3038                     QTextLine line = layout->lineForTextPosition(relativePos);
3039                     qreal x = line.cursorToX(relativePos) + offset * spacew;
3040
3041                     int eoffset = 0;
3042                     int erelativePos  =  ts.positionAtColumn(text,
3043                                                              d->m_findScopeVerticalBlockSelectionLastColumn,
3044                                                              &eoffset);
3045                     QTextLine eline = layout->lineForTextPosition(erelativePos);
3046                     qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
3047
3048                     QRectF rr = line.naturalTextRect();
3049                     rr.moveTop(rr.top() + r.top());
3050                     rr.setLeft(r.left() + x);
3051                     if (line.lineNumber() == eline.lineNumber())  {
3052                         rr.setRight(r.left() + ex);
3053                     }
3054                     painter.fillRect(rr, d->m_searchScopeFormat.background());
3055
3056                     QColor lineCol = d->m_searchScopeFormat.foreground().color();
3057                     QPen pen = painter.pen();
3058                     painter.setPen(lineCol);
3059                     if (blockFS == d->m_findScopeStart.block())
3060                         painter.drawLine(rr.topLeft(), rr.topRight());
3061                     if (blockFS == d->m_findScopeEnd.block())
3062                         painter.drawLine(rr.bottomLeft(), rr.bottomRight());
3063                     painter.drawLine(rr.topLeft(), rr.bottomLeft());
3064                     painter.drawLine(rr.topRight(), rr.bottomRight());
3065                     painter.setPen(pen);
3066                 }
3067             }
3068             offsetFS.ry() += r.height();
3069
3070             if (offsetFS.y() > viewportRect.height())
3071                 break;
3072
3073             blockFS = blockFS.next();
3074             if (!blockFS.isVisible()) {
3075                 // invisible blocks do have zero line count
3076                 blockFS = doc->findBlockByLineNumber(blockFS.firstLineNumber());
3077             }
3078
3079         }
3080     }
3081
3082     if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn < 0) {
3083
3084         TextEditorOverlay *overlay = new TextEditorOverlay(this);
3085         overlay->addOverlaySelection(d->m_findScopeStart.position(),
3086                                      d->m_findScopeEnd.position(),
3087                                      d->m_searchScopeFormat.foreground().color(),
3088                                      d->m_searchScopeFormat.background().color(),
3089                                      TextEditorOverlay::ExpandBegin);
3090         overlay->setAlpha(false);
3091         overlay->paint(&painter, e->rect());
3092         delete overlay;
3093     }
3094
3095
3096
3097     d->m_searchResultOverlay->fill(&painter,
3098                                    d->m_searchResultFormat.background().color(),
3099                                    e->rect());
3100
3101
3102     while (block.isValid()) {
3103
3104         QRectF r = blockBoundingRect(block).translated(offset);
3105
3106         if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
3107
3108             QTextLayout *layout = block.layout();
3109
3110             QTextOption option = layout->textOption();
3111             if (suppressSyntaxInIfdefedOutBlock && BaseTextDocumentLayout::ifdefedOut(block)) {
3112                 option.setFlags(option.flags() | QTextOption::SuppressColors);
3113                 painter.setPen(d->m_ifdefedOutFormat.foreground().color());
3114             } else {
3115                 option.setFlags(option.flags() & ~QTextOption::SuppressColors);
3116                 painter.setPen(context.palette.text().color());
3117             }
3118             layout->setTextOption(option);
3119             layout->setFont(doc->defaultFont()); // this really should be in qplaintextedit when creating the layout!
3120
3121             int blpos = block.position();
3122             int bllen = block.length();
3123
3124             QVector<QTextLayout::FormatRange> selections;
3125             QVector<QTextLayout::FormatRange> prioritySelections;
3126
3127             for (int i = 0; i < context.selections.size(); ++i) {
3128                 const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
3129                 const int selStart = range.cursor.selectionStart() - blpos;
3130                 const int selEnd = range.cursor.selectionEnd() - blpos;
3131                 if (selStart < bllen && selEnd >= 0
3132                     && selEnd >= selStart) {
3133                     QTextLayout::FormatRange o;
3134                     o.start = selStart;
3135                     o.length = selEnd - selStart;
3136                     o.format = range.format;
3137                     if (i == blockSelectionIndex) {
3138                         QString text = block.text();
3139                         const TabSettings &ts = tabSettings();
3140                         o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn);
3141                         o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn) - o.start;
3142                     }
3143                     if ((hasMainSelection && i == context.selections.size()-1)
3144                         || (o.format.foreground().style() == Qt::NoBrush
3145                         && o.format.underlineStyle() != QTextCharFormat::NoUnderline
3146                         && o.format.background() == Qt::NoBrush))
3147                         prioritySelections.append(o);
3148                     else
3149                         selections.append(o);
3150                 }
3151 #if 0
3152                 // we disable fullwidth selection. It's only used for m_highlightCurrentLine which we
3153                 // do differently now
3154                 else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
3155                     && block.contains(range.cursor.position())) {
3156                     // for full width selections we don't require an actual selection, just
3157                     // a position to specify the line. that's more convenience in usage.
3158                     QTextLayout::FormatRange o;
3159                     QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos);
3160                     o.start = l.textStart();
3161                     o.length = l.textLength();
3162                     if (o.start + o.length == bllen - 1)
3163                         ++o.length; // include newline
3164                     o.format = range.format;
3165                     selections.append(o);
3166                 }
3167 #endif
3168             }
3169             selections += prioritySelections;
3170
3171             if (d->m_highlightCurrentLine && block == textCursorBlock) {
3172
3173                 QRectF rr = layout->lineForTextPosition(textCursor().positionInBlock()).rect();
3174                 rr.moveTop(rr.top() + r.top());
3175                 rr.setLeft(0);
3176                 rr.setRight(viewportRect.width() - offset.x());
3177                 QColor color = d->m_currentLineFormat.background().color();
3178                 // set alpha, otherwise we cannot see block highlighting and find scope underneath
3179                 color.setAlpha(128);
3180                 painter.fillRect(rr, color);
3181             }
3182
3183
3184             QRectF blockSelectionCursorRect;
3185             if (d->m_inBlockSelectionMode
3186                     && block.position() >= d->m_blockSelection.firstBlock.block().position()
3187                     && block.position() <= d->m_blockSelection.lastBlock.block().position()) {
3188                 QString text = block.text();
3189                 const TabSettings &ts = tabSettings();
3190                 qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' '));
3191
3192                 int offset = 0;
3193                 int relativePos  =  ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn, &offset);
3194                 QTextLine line = layout->lineForTextPosition(relativePos);
3195                 qreal x = line.cursorToX(relativePos) + offset * spacew;
3196
3197                 int eoffset = 0;
3198                 int erelativePos  =  ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn, &eoffset);
3199                 QTextLine eline = layout->lineForTextPosition(erelativePos);
3200                 qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
3201
3202                 QRectF rr = line.naturalTextRect();
3203                 rr.moveTop(rr.top() + r.top());
3204                 rr.setLeft(r.left() + x);
3205                 if (line.lineNumber() == eline.lineNumber())  {
3206                     rr.setRight(r.left() + ex);
3207                 }
3208                 painter.fillRect(rr, palette().highlight());
3209                 if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopLeft
3210                         && block == d->m_blockSelection.firstBlock.block())
3211                         || (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomLeft
3212                             && block == d->m_blockSelection.lastBlock.block())
3213                         ) {
3214                     rr.setRight(rr.left()+2);
3215                     blockSelectionCursorRect = rr;
3216                 }
3217                 for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) {
3218                     rr = layout->lineAt(i).naturalTextRect();
3219                     rr.moveTop(rr.top() + r.top());
3220                     rr.setLeft(r.left() + x);
3221                     painter.fillRect(rr, palette().highlight());
3222                 }
3223
3224                 rr = eline.naturalTextRect();
3225                 rr.moveTop(rr.top() + r.top());
3226                 rr.setRight(r.left() + ex);
3227                 if (line.lineNumber() != eline.lineNumber())
3228                     painter.fillRect(rr, palette().highlight());
3229                 if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopRight
3230                      && block == d->m_blockSelection.firstBlock.block())
3231                         || (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomRight
3232                             && block == d->m_blockSelection.lastBlock.block())) {
3233                     rr.setLeft(rr.right()-2);
3234                     blockSelectionCursorRect = rr;
3235                 }
3236             }
3237
3238
3239             bool drawCursor = ((editable || true) // we want the cursor in read-only mode
3240                                && context.cursorPosition >= blpos
3241                                && context.cursorPosition < blpos + bllen);
3242
3243             bool drawCursorAsBlock = drawCursor && overwriteMode() ;
3244
3245             if (drawCursorAsBlock) {
3246                 int relativePos = context.cursorPosition - blpos;
3247                 bool doSelection = true;
3248                 QTextLine line = layout->lineForTextPosition(relativePos);
3249                 qreal x = line.cursorToX(relativePos);
3250                 qreal w = 0;
3251                 if (relativePos < line.textLength() - line.textStart()) {
3252                     w = line.cursorToX(relativePos + 1) - x;
3253                     if (doc->characterAt(context.cursorPosition) == QLatin1Char('\t')) {
3254                         doSelection = false;
3255                         qreal space = QFontMetricsF(layout->font()).width(QLatin1Char(' '));
3256                         if (w > space) {
3257                             x += w-space;
3258                             w = space;
3259                         }
3260                     }
3261                 } else
3262                     w = QFontMetrics(layout->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
3263
3264                 QRectF rr = line.rect();
3265                 rr.moveTop(rr.top() + r.top());
3266                 rr.moveLeft(r.left() + x);
3267                 rr.setWidth(w);
3268                 painter.fillRect(rr, palette().text());
3269                 if (doSelection) {
3270                     QTextLayout::FormatRange o;
3271                     o.start = relativePos;
3272                     o.length = 1;
3273                     o.format.setForeground(palette().base());
3274                     selections.append(o);
3275                 }
3276             }
3277
3278
3279
3280             layout->draw(&painter, offset, selections, er);
3281
3282             if ((drawCursor && !drawCursorAsBlock)
3283                 || (editable && context.cursorPosition < -1
3284                     && !layout->preeditAreaText().isEmpty())) {
3285                 int cpos = context.cursorPosition;
3286                 if (cpos < -1)
3287                     cpos = layout->preeditAreaPosition() - (cpos + 2);
3288                 else
3289                     cpos -= blpos;
3290                 cursor_layout = layout;
3291                 cursor_offset = offset;
3292                 cursor_cpos = cpos;
3293                 cursor_pen = painter.pen();
3294             }
3295
3296 #ifndef Q_WS_MAC // no visible cursor on mac
3297             if (blockSelectionCursorRect.isValid())
3298                 painter.fillRect(blockSelectionCursorRect, palette().text());
3299 #endif
3300
3301         }
3302
3303         offset.ry() += r.height();
3304
3305         if (offset.y() > viewportRect.height())
3306             break;
3307
3308         block = block.next();
3309
3310         if (!block.isVisible()) {
3311             if (block.blockNumber() == d->visibleFoldedBlockNumber) {
3312                 visibleCollapsedBlock = block;
3313                 visibleCollapsedBlockOffset = offset + QPointF(0,1);
3314             }
3315
3316             // invisible blocks do have zero line count
3317             block = doc->findBlockByLineNumber(block.firstLineNumber());
3318         }
3319     }
3320     painter.setPen(context.palette.text().color());
3321
3322     if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom()
3323         && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) {
3324         painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background());
3325     }
3326
3327     //end QPlainTextEdit::paintEvent()
3328
3329     offset = contentOffset();
3330     block = firstVisibleBlock();
3331
3332     qreal top = blockBoundingGeometry(block).translated(offset).top();
3333     qreal bottom = top + blockBoundingRect(block).height();
3334
3335     QTextCursor cursor = textCursor();
3336     bool hasSelection = cursor.hasSelection();
3337     int selectionStart = cursor.selectionStart();
3338     int selectionEnd = cursor.selectionEnd();
3339
3340
3341     while (block.isValid() && top <= e->rect().bottom()) {
3342         QTextBlock nextBlock = block.next();
3343         QTextBlock nextVisibleBlock = nextBlock;
3344
3345         if (!nextVisibleBlock.isVisible()) {
3346             // invisible blocks do have zero line count
3347             nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber());
3348             // paranoia in case our code somewhere did not set the line count
3349             // of the invisible block to 0
3350             while (nextVisibleBlock.isValid() && !nextVisibleBlock.isVisible())
3351                 nextVisibleBlock = nextVisibleBlock.next();
3352         }
3353         if (block.isVisible() && bottom >= e->rect().top()) {
3354             if (d->m_displaySettings.m_visualizeWhitespace) {
3355                 QTextLayout *layout = block.layout();
3356                 int lineCount = layout->lineCount();
3357                 if (lineCount >= 2 || !nextBlock.isValid()) {
3358                     painter.save();
3359                     painter.setPen(Qt::lightGray);
3360                     for (int i = 0; i < lineCount-1; ++i) { // paint line wrap indicator
3361                         QTextLine line = layout->lineAt(i);
3362                         QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
3363                         QChar visualArrow((ushort)0x21b5);
3364                         painter.drawText(QPointF(lineRect.right(),
3365                                                  lineRect.top() + line.ascent()),
3366                                          visualArrow);
3367                     }
3368                     if (!nextBlock.isValid()) { // paint EOF symbol
3369                         QTextLine line = layout->lineAt(lineCount-1);
3370                         QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
3371                         int h = 4;
3372                         lineRect.adjust(0, 0, -1, -1);
3373                         QPainterPath path;
3374                         QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent()));
3375                         path.moveTo(pos);
3376                         path.lineTo(pos + QPointF(-h, -h));
3377                         path.lineTo(pos + QPointF(0, -2*h));
3378                         path.lineTo(pos + QPointF(h, -h));
3379                         path.closeSubpath();
3380                         painter.setBrush(painter.pen().color());
3381                         painter.drawPath(path);
3382                     }
3383                     painter.restore();
3384                 }
3385             }
3386
3387             if (nextBlock.isValid() && !nextBlock.isVisible()) {
3388
3389                 bool selectThis = (hasSelection
3390                                    && nextBlock.position() >= selectionStart
3391                                    && nextBlock.position() < selectionEnd);
3392                 if (selectThis) {
3393                     painter.save();
3394                     painter.setBrush(palette().highlight());
3395                 }
3396
3397                 QTextLayout *layout = block.layout();
3398                 QTextLine line = layout->lineAt(layout->lineCount()-1);
3399                 QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
3400                 lineRect.adjust(0, 0, -1, -1);
3401
3402                 QRectF collapseRect(lineRect.right() + 12,
3403                                     lineRect.top(),
3404                                     fontMetrics().width(QLatin1String(" {...}; ")),
3405                                     lineRect.height());
3406                 painter.setRenderHint(QPainter::Antialiasing, true);
3407                 painter.translate(.5, .5);
3408                 painter.drawRoundedRect(collapseRect.adjusted(0, 0, 0, -1), 3, 3);
3409                 painter.setRenderHint(QPainter::Antialiasing, false);
3410                 painter.translate(-.5, -.5);
3411
3412                 QString replacement = QLatin1String("...");
3413
3414                 if (TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock)) {
3415                     if (nextBlockUserData->foldingStartIncluded())
3416                         replacement.prepend(nextBlock.text().trimmed().left(1));
3417                 }
3418
3419                 block = nextVisibleBlock.previous();
3420                 if (!block.isValid())
3421                     block = doc->lastBlock();
3422
3423                 if (TextBlockUserData *blockUserData = BaseTextDocumentLayout::testUserData(block)) {
3424                     if (blockUserData->foldingEndIncluded()) {
3425                         QString right = block.text().trimmed();
3426                         if (right.endsWith(QLatin1Char(';'))) {
3427                             right.chop(1);
3428                             right = right.trimmed();
3429                             replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
3430                             replacement.append(QLatin1Char(';'));
3431                         } else {
3432                             replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
3433                         }
3434                     }
3435                 }
3436
3437                 if (selectThis)
3438                     painter.setPen(palette().highlightedText().color());
3439                 painter.drawText(collapseRect, Qt::AlignCenter, replacement);
3440                 if (selectThis)
3441                     painter.restore();
3442             }
3443         }
3444
3445         block = nextVisibleBlock;
3446         top = bottom;
3447         bottom = top + blockBoundingRect(block).height();
3448     }
3449
3450     if (d->m_animator && d->m_animator->isRunning()) {
3451         QTextCursor cursor = textCursor();
3452         cursor.setPosition(d->m_animator->position());
3453         d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
3454     }
3455
3456     // draw the overlays, but only if we do not have a find scope, otherwise the
3457     // view becomes too noisy.
3458     if (d->m_findScopeStart.isNull()) {
3459         if (d->m_overlay->isVisible())
3460             d->m_overlay->paint(&painter, e->rect());
3461
3462         if (d->m_snippetOverlay->isVisible())
3463             d->m_snippetOverlay->paint(&painter, e->rect());
3464
3465         if (!d->m_refactorOverlay->isEmpty())
3466             d->m_refactorOverlay->paint(&painter, e->rect());
3467     }
3468
3469     if (!d->m_searchResultOverlay->isEmpty()) {
3470         d->m_searchResultOverlay->paint(&painter, e->rect());
3471         d->m_searchResultOverlay->clear();
3472     }
3473
3474
3475     // draw the cursor last, on top of everything
3476     if (cursor_layout && !d->m_inBlockSelectionMode) {
3477         painter.setPen(cursor_pen);
3478         cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
3479     }
3480
3481     if (visibleCollapsedBlock.isValid()) {
3482         drawCollapsedBlockPopup(painter,
3483                                 visibleCollapsedBlock,
3484                                 visibleCollapsedBlockOffset,
3485                                 er);
3486     }
3487 }
3488
3489 void BaseTextEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
3490                                              const QTextBlock &block,
3491                                              QPointF offset,
3492                                              const QRect &clip)
3493 {
3494     int margin = block.document()->documentMargin();
3495     qreal maxWidth = 0;
3496     qreal blockHeight = 0;
3497     QTextBlock b = block;
3498
3499     while (!b.isVisible()) {
3500         b.setVisible(true); // make sure block bounding rect works
3501         QRectF r = blockBoundingRect(b).translated(offset);
3502
3503         QTextLayout *layout = b.layout();
3504         for (int i = layout->lineCount()-1; i >= 0; --i)
3505             maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2*margin);
3506
3507         blockHeight += r.height();
3508
3509         b.setVisible(false); // restore previous state
3510         b.setLineCount(0); // restore 0 line count for invisible block
3511         b = b.next();
3512     }
3513
3514     painter.save();
3515     painter.setRenderHint(QPainter::Antialiasing, true);
3516     painter.translate(.5, .5);
3517     QBrush brush = palette().base();
3518     if (d->m_ifdefedOutFormat.hasProperty(QTextFormat::BackgroundBrush))
3519         brush = d->m_ifdefedOutFormat.background();
3520     painter.setBrush(brush);
3521     painter.drawRoundedRect(QRectF(offset.x(),
3522                                    offset.y(),
3523                                    maxWidth, blockHeight).adjusted(0, 0, 0, 0), 3, 3);
3524     painter.restore();
3525
3526     QTextBlock end = b;
3527     b = block;
3528     while (b != end) {
3529         b.setVisible(true); // make sure block bounding rect works
3530         QRectF r = blockBoundingRect(b).translated(offset);
3531         QTextLayout *layout = b.layout();
3532         QVector<QTextLayout::FormatRange> selections;
3533         layout->draw(&painter, offset, selections, clip);
3534
3535         b.setVisible(false); // restore previous state
3536         b.setLineCount(0); // restore 0 line count for invisible block
3537         offset.ry() += r.height();
3538         b = b.next();
3539     }
3540 }
3541
3542 QWidget *BaseTextEditorWidget::extraArea() const
3543 {
3544     return d->m_extraArea;
3545 }
3546
3547 int BaseTextEditorWidget::extraAreaWidth(int *markWidthPtr) const
3548 {
3549     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document()->documentLayout());
3550     if (!documentLayout)
3551         return 0;
3552
3553     if (!d->m_marksVisible && documentLayout->hasMarks)
3554         d->m_marksVisible = true;
3555
3556     int space = 0;
3557     const QFontMetrics fm(d->m_extraArea->fontMetrics());
3558
3559     if (d->m_lineNumbersVisible) {
3560         QFont fnt = d->m_extraArea->font();
3561         // this works under the assumption that bold or italic can only make a font wider
3562         fnt.setBold(d->m_currentLineNumberFormat.font().bold());
3563         fnt.setItalic(d->m_currentLineNumberFormat.font().italic());
3564         const QFontMetrics linefm(fnt);
3565
3566         int digits = 2;
3567         int max = qMax(1, blockCount());
3568         while (max >= 100) {
3569             max /= 10;
3570             ++digits;
3571         }
3572         space += linefm.width(QLatin1Char('9')) * digits;
3573     }
3574     int markWidth = 0;
3575
3576     if (d->m_marksVisible) {
3577         markWidth += fm.lineSpacing();
3578 //     if (documentLayout->doubleMarkCount)
3579 //         markWidth += fm.lineSpacing() / 3;
3580         space += markWidth;
3581     } else {
3582         space += 2;
3583     }
3584
3585     if (markWidthPtr)
3586         *markWidthPtr = markWidth;
3587
3588     space += 4;
3589
3590     if (d->m_codeFoldingVisible)
3591         space += foldBoxWidth(fm);
3592     return space;
3593 }
3594
3595 void BaseTextEditorWidget::slotUpdateExtraAreaWidth()
3596 {
3597     if (isLeftToRight())
3598         setViewportMargins(extraAreaWidth(), 0, 0, 0);
3599     else
3600         setViewportMargins(0, 0, extraAreaWidth(), 0);
3601 }
3602
3603 static void drawRectBox(QPainter *painter, const QRect &rect, bool start, bool end,
3604                         const QPalette &pal)
3605 {
3606     painter->save();
3607     painter->setRenderHint(QPainter::Antialiasing, false);
3608
3609     QRgb b = pal.base().color().rgb();
3610     QRgb h = pal.highlight().color().rgb();
3611     QColor c = Utils::StyleHelper::mergedColors(b,h, 50);
3612
3613     QLinearGradient grad(rect.topLeft(), rect.topRight());
3614     grad.setColorAt(0, c.lighter(110));
3615     grad.setColorAt(1, c.lighter(130));
3616     QColor outline = c;
3617
3618     painter->fillRect(rect, grad);
3619     painter->setPen(outline);
3620     if (start)
3621         painter->drawLine(rect.topLeft() + QPoint(1, 0), rect.topRight() -  QPoint(1, 0));
3622     if (end)
3623         painter->drawLine(rect.bottomLeft() + QPoint(1, 0), rect.bottomRight() -  QPoint(1, 0));
3624
3625     painter->drawLine(rect.topRight() + QPoint(0, start ? 1 : 0), rect.bottomRight() - QPoint(0, end ? 1 : 0));
3626     painter->drawLine(rect.topLeft() + QPoint(0, start ? 1 : 0), rect.bottomLeft() - QPoint(0, end ? 1 : 0));
3627
3628     painter->restore();
3629 }
3630
3631 void BaseTextEditorWidget::extraAreaPaintEvent(QPaintEvent *e)
3632 {
3633     QTextDocument *doc = document();
3634     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
3635     QTC_ASSERT(documentLayout, return);
3636
3637     int selStart = textCursor().selectionStart();
3638     int selEnd = textCursor().selectionEnd();
3639
3640     QPalette pal = d->m_extraArea->palette();
3641     pal.setCurrentColorGroup(QPalette::Active);
3642     QPainter painter(d->m_extraArea);
3643     const QFontMetrics fm(d->m_extraArea->font());
3644     int fmLineSpacing = fm.lineSpacing();
3645
3646     int markWidth = 0;
3647     if (d->m_marksVisible)
3648         markWidth += fm.lineSpacing();
3649
3650     const int collapseColumnWidth = d->m_codeFoldingVisible ? foldBoxWidth(fm): 0;
3651     const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth;
3652
3653     painter.fillRect(e->rect(), pal.color(QPalette::Base));
3654     painter.fillRect(e->rect().intersected(QRect(0, 0, extraAreaWidth, INT_MAX)),
3655                      pal.color(QPalette::Background));
3656
3657     QTextBlock block = firstVisibleBlock();
3658     int blockNumber = block.blockNumber();
3659     qreal top = blockBoundingGeometry(block).translated(contentOffset()).top();
3660     qreal bottom = top;
3661
3662     while (block.isValid() && top <= e->rect().bottom()) {
3663
3664         top = bottom;
3665         const qreal height = blockBoundingRect(block).height();
3666         bottom = top + height;
3667         QTextBlock nextBlock = block.next();
3668
3669         QTextBlock nextVisibleBlock = nextBlock;
3670         int nextVisibleBlockNumber = blockNumber + 1;
3671
3672         if (!nextVisibleBlock.isVisible()) {
3673             // invisible blocks do have zero line count
3674             nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber());
3675             nextVisibleBlockNumber = nextVisibleBlock.blockNumber();
3676         }
3677
3678         if (bottom < e->rect().top()) {
3679             block = nextVisibleBlock;
3680             blockNumber = nextVisibleBlockNumber;
3681             continue;
3682         }
3683
3684         painter.setPen(pal.color(QPalette::Dark));
3685
3686         if (d->m_codeFoldingVisible || d->m_marksVisible) {
3687             painter.save();
3688             painter.setRenderHint(QPainter::Antialiasing, false);
3689
3690             int previousBraceDepth = block.previous().userState();
3691             if (previousBraceDepth >= 0)
3692                 previousBraceDepth >>= 8;
3693             else
3694                 previousBraceDepth = 0;
3695
3696             int braceDepth = block.userState();
3697             if (!nextBlock.isVisible()) {
3698                 QTextBlock lastInvisibleBlock = nextVisibleBlock.previous();
3699                 if (!lastInvisibleBlock.isValid())
3700                     lastInvisibleBlock = doc->lastBlock();
3701                 braceDepth = lastInvisibleBlock.userState();
3702             }
3703             if (braceDepth >= 0)
3704                 braceDepth >>= 8;
3705             else
3706                 braceDepth = 0;
3707
3708             if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) {
3709                 if (d->m_marksVisible) {
3710                     int xoffset = 0;
3711                     foreach (ITextMark *mrk, userData->marks()) {
3712                         int x = 0;
3713                         int radius = fmLineSpacing - 1;
3714                         QRect r(x + xoffset, top, radius, radius);
3715                         mrk->icon().paint(&painter, r, Qt::AlignCenter);
3716                         xoffset += 2;
3717                     }
3718                 }
3719             }
3720
3721             if (d->m_codeFoldingVisible) {
3722
3723                 int extraAreaHighlightFoldBlockNumber = -1;
3724                 int extraAreaHighlightFoldEndBlockNumber = -1;
3725                 bool endIsVisible = false;
3726                 if (!d->m_highlightBlocksInfo.isEmpty()) {
3727                     extraAreaHighlightFoldBlockNumber =  d->m_highlightBlocksInfo.open.last();
3728                     extraAreaHighlightFoldEndBlockNumber =  d->m_highlightBlocksInfo.close.first();
3729                     endIsVisible = doc->findBlockByNumber(extraAreaHighlightFoldEndBlockNumber).isVisible();
3730
3731 //                    QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1);
3732 //                    if (TextBlockUserData::hasCollapseAfter(before)) {
3733 //                        extraAreaHighlightCollapseBlockNumber--;
3734 //                    }
3735                 }
3736
3737                 TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock);
3738
3739                 bool drawBox = nextBlockUserData
3740                                && BaseTextDocumentLayout::foldingIndent(block) < nextBlockUserData->foldingIndent();
3741
3742
3743
3744                 bool active = blockNumber == extraAreaHighlightFoldBlockNumber;
3745
3746                 bool drawStart = active;
3747                 bool drawEnd = blockNumber == extraAreaHighlightFoldEndBlockNumber || (drawStart && !endIsVisible);
3748                 bool hovered = blockNumber >= extraAreaHighlightFoldBlockNumber
3749                                && blockNumber <= extraAreaHighlightFoldEndBlockNumber;
3750
3751                 int boxWidth = foldBoxWidth(fm);
3752                 if (hovered) {
3753                     int itop = qRound(top);
3754                     int ibottom = qRound(bottom);
3755                     QRect box = QRect(extraAreaWidth + 1, itop, boxWidth - 2, ibottom - itop);
3756                     drawRectBox(&painter, box, drawStart, drawEnd, pal);
3757                 }
3758
3759                 if (drawBox) {
3760                     bool expanded = nextBlock.isVisible();
3761                     int size = boxWidth/4;
3762                     QRect box(extraAreaWidth + size, top + size,
3763                               2 * (size) + 1, 2 * (size) + 1);
3764                     drawFoldingMarker(&painter, pal, box, expanded, active, hovered);
3765                 }
3766             }
3767
3768             painter.restore();
3769         }
3770
3771
3772         if (d->m_revisionsVisible && block.revision() != documentLayout->lastSaveRevision) {
3773             painter.save();
3774             painter.setRenderHint(QPainter::Antialiasing, false);
3775             if (block.revision() < 0)
3776                 painter.setPen(QPen(Qt::darkGreen, 2));
3777             else
3778                 painter.setPen(QPen(Qt::red, 2));
3779             painter.drawLine(extraAreaWidth - 1, top, extraAreaWidth - 1, bottom - 1);
3780             painter.restore();
3781         }
3782
3783         if (d->m_lineNumbersVisible) {
3784             const QString &number = QString::number(blockNumber + 1);
3785             bool selected = (
3786                     (selStart < block.position() + block.length()
3787                     && selEnd > block.position())
3788                     || (selStart == selEnd && selStart == block.position())
3789                     );
3790             if (selected) {
3791                 painter.save();
3792                 QFont f = painter.font();
3793                 f.setBold(d->m_currentLineNumberFormat.font().bold());
3794                 f.setItalic(d->m_currentLineNumberFormat.font().italic());
3795                 painter.setFont(f);
3796                 painter.setPen(d->m_currentLineNumberFormat.foreground().color());
3797             }
3798             painter.drawText(QRectF(markWidth, top, extraAreaWidth - markWidth - 4, height), Qt::AlignRight, number);
3799             if (selected)
3800                 painter.restore();
3801         }
3802
3803         block = nextVisibleBlock;
3804         blockNumber = nextVisibleBlockNumber;
3805     }
3806 }
3807
3808 void BaseTextEditorWidget::drawFoldingMarker(QPainter *painter, const QPalette &pal,
3809                                        const QRect &rect,
3810                                        bool expanded,
3811                                        bool active,
3812                                        bool hovered) const
3813 {
3814     Q_UNUSED(active)
3815     Q_UNUSED(hovered)
3816     QStyle *s = style();
3817     if (ManhattanStyle *ms = qobject_cast<ManhattanStyle*>(s))
3818         s = ms->baseStyle();
3819
3820     if (!qstrcmp(s->metaObject()->className(), "OxygenStyle")) {
3821         painter->save();
3822         painter->setPen(Qt::NoPen);
3823         int size = rect.size().width();
3824         int sqsize = 2*(size/2);
3825
3826         QColor textColor = pal.buttonText().color();
3827         QColor brushColor = textColor;
3828
3829         textColor.setAlpha(100);
3830         brushColor.setAlpha(100);
3831
3832         QPolygon a;
3833         if (expanded) {
3834             // down arrow
3835             a.setPoints(3, 0, sqsize/3,  sqsize/2, sqsize  - sqsize/3,  sqsize, sqsize/3);
3836         } else {
3837             // right arrow
3838             a.setPoints(3, sqsize - sqsize/3, sqsize/2,  sqsize/2 - sqsize/3, 0,  sqsize/2 - sqsize/3, sqsize);
3839             painter->setBrush(brushColor);
3840         }
3841         painter->translate(0.5, 0.5);
3842         painter->setRenderHint(QPainter::Antialiasing);
3843         painter->translate(rect.topLeft());
3844         painter->setPen(textColor);
3845         painter->setBrush(textColor);
3846         painter->drawPolygon(a);
3847         painter->restore();
3848     } else {
3849         QStyleOptionViewItemV2 opt;
3850         opt.rect = rect;
3851         opt.state = QStyle::State_Active | QStyle::State_Item | QStyle::State_Children;
3852         if (expanded)
3853             opt.state |= QStyle::State_Open;
3854         if (active)
3855             opt.state |= QStyle::State_MouseOver | QStyle::State_Enabled | QStyle::State_Selected;
3856         if (hovered)
3857             opt.palette.setBrush(QPalette::Window, pal.highlight());
3858
3859          // QGtkStyle needs a small correction to draw the marker in the right place
3860         if (!qstrcmp(s->metaObject()->className(), "QGtkStyle"))
3861            opt.rect.translate(-2, 0);
3862         else if (!qstrcmp(s->metaObject()->className(), "QMacStyle"))
3863             opt.rect.translate(-1, 0);
3864
3865         s->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
3866     }
3867 }
3868
3869 void BaseTextEditorWidget::slotModificationChanged(bool m)
3870 {
3871     if (m)
3872         return;
3873
3874     QTextDocument *doc = document();
3875     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
3876     QTC_ASSERT(documentLayout, return);
3877     int oldLastSaveRevision = documentLayout->lastSaveRevision;
3878     documentLayout->lastSaveRevision = doc->revision();
3879
3880     if (oldLastSaveRevision != documentLayout->lastSaveRevision) {
3881         QTextBlock block = doc->begin();
3882         while (block.isValid()) {
3883             if (block.revision() < 0 || block.revision() != oldLastSaveRevision) {
3884                 block.setRevision(-documentLayout->lastSaveRevision - 1);
3885             } else {
3886                 block.setRevision(documentLayout->lastSaveRevision);
3887             }
3888             block = block.next();
3889         }
3890     }
3891     d->m_extraArea->update();
3892 }
3893
3894 void BaseTextEditorWidget::slotUpdateRequest(const QRect &r, int dy)
3895 {
3896     if (dy)
3897         d->m_extraArea->scroll(0, dy);
3898     else if (r.width() > 4) { // wider than cursor width, not just cursor blinking
3899         d->m_extraArea->update(0, r.y(), d->m_extraArea->width(), r.height());
3900         if (!d->m_searchExpr.isEmpty()) {
3901             const int m = d->m_searchResultOverlay->dropShadowWidth();
3902             viewport()->update(r.adjusted(-m, -m, m, m));
3903         }
3904     }
3905
3906     if (r.contains(viewport()->rect()))
3907         slotUpdateExtraAreaWidth();
3908 }
3909
3910 void BaseTextEditorWidget::saveCurrentCursorPositionForNavigation()
3911 {
3912     d->m_lastCursorChangeWasInteresting = true;
3913     d->m_tempNavigationState = saveState();
3914 }
3915
3916 void BaseTextEditorWidget::updateCurrentLineHighlight()
3917 {
3918     QList<QTextEdit::ExtraSelection> extraSelections;
3919
3920     if (d->m_highlightCurrentLine) {
3921         QTextEdit::ExtraSelection sel;
3922         sel.format.setBackground(d->m_currentLineFormat.background());
3923         sel.format.setProperty(QTextFormat::FullWidthSelection, true);
3924         sel.cursor = textCursor();
3925         sel.cursor.clearSelection();
3926         extraSelections.append(sel);
3927     }
3928
3929     setExtraSelections(CurrentLineSelection, extraSelections);
3930
3931
3932     // the extra area shows information for the entire current block, not just the currentline.
3933     // This is why we must force a bigger update region.
3934     int cursorBlockNumber = textCursor().blockNumber();
3935     if (cursorBlockNumber != d->m_cursorBlockNumber) {
3936         QPointF offset = contentOffset();
3937         QTextBlock block = document()->findBlockByNumber(d->m_cursorBlockNumber);
3938         if (block.isValid())
3939             d->m_extraArea->update(blockBoundingGeometry(block).translated(offset).toAlignedRect());
3940         block = document()->findBlockByNumber(cursorBlockNumber);
3941         if (block.isValid() && block.isVisible())
3942             d->m_extraArea->update(blockBoundingGeometry(block).translated(offset).toAlignedRect());
3943         d->m_cursorBlockNumber = cursorBlockNumber;
3944     }
3945
3946 }
3947
3948 void BaseTextEditorWidget::slotCursorPositionChanged()
3949 {
3950 #if 0
3951     qDebug() << "block" << textCursor().blockNumber()+1
3952             << "brace depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block())
3953             << "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent();
3954 #endif
3955     if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) {
3956         Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(editor(), d->m_tempNavigationState);
3957         d->m_lastCursorChangeWasInteresting = false;
3958     } else if (d->m_contentsChanged) {
3959         saveCurrentCursorPositionForNavigation();
3960     }
3961     updateHighlights();
3962 }
3963
3964 void BaseTextEditorWidget::updateHighlights()
3965 {
3966     if (d->m_parenthesesMatchingEnabled && hasFocus()) {
3967         // Delay update when no matching is displayed yet, to avoid flicker
3968         if (extraSelections(ParenthesesMatchingSelection).isEmpty()
3969             && d->m_animator == 0) {
3970             d->m_parenthesesMatchingTimer->start(50);
3971         } else {
3972              // use 0-timer, not direct call, to give the syntax highlighter a chance
3973             // to update the parantheses information
3974             d->m_parenthesesMatchingTimer->start(0);
3975         }
3976     }
3977
3978     updateCurrentLineHighlight();
3979
3980     if (d->m_displaySettings.m_highlightBlocks) {
3981         QTextCursor cursor = textCursor();
3982         d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
3983         d->m_highlightBlocksTimer->start(100);
3984     }
3985 }
3986
3987 void BaseTextEditorWidget::slotUpdateBlockNotify(const QTextBlock &block)
3988 {
3989     static bool blockRecursion = false;
3990     if (blockRecursion)
3991         return;
3992     blockRecursion = true;
3993     if (d->m_overlay->isVisible()) {
3994         /* an overlay might draw outside the block bounderies, force
3995            complete viewport update */
3996         viewport()->update();
3997     } else {
3998         if (block.previous().isValid() && block.userState() != block.previous().userState()) {
3999         /* The syntax highlighting state changes. This opens up for
4000            the possibility that the paragraph has braces that support
4001            code folding. In this case, do the save thing and also
4002            update the previous block, which might contain a fold
4003            box which now is invalid.*/
4004             emit requestBlockUpdate(block.previous());
4005         }
4006         if (!d->m_findScopeStart.isNull()) {
4007             if (block.position() < d->m_findScopeEnd.position()
4008                 && block.position()+block.length() >= d->m_findScopeStart.position()) {
4009                 QTextBlock b = block.document()->findBlock(d->m_findScopeStart.position());
4010                 do {
4011                     emit requestBlockUpdate(b);
4012                     b = b.next();
4013                 } while (b.isValid() && b.position() < d->m_findScopeEnd.position());
4014             }
4015         }
4016     }
4017     blockRecursion = false;
4018 }
4019
4020 void BaseTextEditorWidget::timerEvent(QTimerEvent *e)
4021 {
4022     if (e->timerId() == d->autoScrollTimer.timerId()) {
4023         const QPoint globalPos = QCursor::pos();
4024         const QPoint pos = d->m_extraArea->mapFromGlobal(globalPos);
4025         QRect visible = d->m_extraArea->rect();
4026         verticalScrollBar()->triggerAction( pos.y() < visible.center().y() ?
4027                                             QAbstractSlider::SliderSingleStepSub
4028                                             : QAbstractSlider::SliderSingleStepAdd);
4029         QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
4030         extraAreaMouseEvent(&ev);
4031         int delta = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
4032         if (delta < 7)
4033             delta = 7;
4034         int timeout = 4900 / (delta * delta);
4035         d->autoScrollTimer.start(timeout, this);
4036
4037     } else if (e->timerId() == d->foldedBlockTimer.timerId()) {
4038         d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber;
4039         d->suggestedVisibleFoldedBlockNumber = -1;
4040         d->foldedBlockTimer.stop();
4041         viewport()->update();
4042     }
4043     QPlainTextEdit::timerEvent(e);
4044 }
4045
4046
4047 void BaseTextEditorPrivate::clearVisibleFoldedBlock()
4048 {
4049     if (suggestedVisibleFoldedBlockNumber) {
4050         suggestedVisibleFoldedBlockNumber = -1;
4051         foldedBlockTimer.stop();
4052     }
4053     if (visibleFoldedBlockNumber >= 0) {
4054         visibleFoldedBlockNumber = -1;
4055         q->viewport()->update();
4056     }
4057 }
4058
4059 void BaseTextEditorWidget::mouseMoveEvent(QMouseEvent *e)
4060 {
4061     updateLink(e);
4062
4063     if (e->buttons() == Qt::NoButton) {
4064         const QTextBlock collapsedBlock = foldedBlockAt(e->pos());
4065         const int blockNumber = collapsedBlock.next().blockNumber();
4066         if (blockNumber < 0) {
4067             d->clearVisibleFoldedBlock();
4068         } else if (blockNumber != d->visibleFoldedBlockNumber) {
4069             d->suggestedVisibleFoldedBlockNumber = blockNumber;
4070             d->foldedBlockTimer.start(40, this);
4071         }
4072
4073         const RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
4074
4075         // Update the mouse cursor
4076         if ((collapsedBlock.isValid() || refactorMarker.isValid()) && !d->m_mouseOnFoldedMarker) {
4077             d->m_mouseOnFoldedMarker = true;
4078             viewport()->setCursor(Qt::PointingHandCursor);
4079         } else if (!collapsedBlock.isValid() && !refactorMarker.isValid() && d->m_mouseOnFoldedMarker) {
4080             d->m_mouseOnFoldedMarker = false;
4081             viewport()->setCursor(Qt::IBeamCursor);
4082         }
4083     } else {
4084         QPlainTextEdit::mouseMoveEvent(e);
4085
4086         if (e->modifiers() & Qt::AltModifier) {
4087             if (!d->m_inBlockSelectionMode) {
4088                 d->m_blockSelection.fromSelection(tabSettings(), textCursor());
4089                 d->m_inBlockSelectionMode = true;
4090             } else {
4091                 QTextCursor cursor = textCursor();
4092
4093                 // get visual column
4094                 int column = tabSettings().columnAt(cursor.block().text(), cursor.positionInBlock());
4095                 if (cursor.positionInBlock() == cursor.block().length()-1) {
4096                     column += (e->pos().x() - cursorRect().center().x())/QFontMetricsF(font()).width(QLatin1Char(' '));
4097                 }
4098                 d->m_blockSelection.moveAnchor(cursor.blockNumber(), column);
4099                 setTextCursor(d->m_blockSelection.selection(tabSettings()));
4100                 viewport()->update();
4101             }
4102         }
4103     }
4104     if (viewport()->cursor().shape() == Qt::BlankCursor)
4105         viewport()->setCursor(Qt::IBeamCursor);
4106 }
4107
4108 static bool handleForwardBackwardMouseButtons(QMouseEvent *e)
4109 {
4110     if (e->button() == Qt::XButton1) {
4111         Core::EditorManager::instance()->goBackInNavigationHistory();
4112         return true;
4113     }
4114     if (e->button() == Qt::XButton2) {
4115         Core::EditorManager::instance()->goForwardInNavigationHistory();
4116         return true;
4117     }
4118
4119     return false;
4120 }
4121
4122 void BaseTextEditorWidget::mousePressEvent(QMouseEvent *e)
4123 {
4124     if (e->button() == Qt::LeftButton) {
4125         d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop
4126
4127         QTextBlock foldedBlock = foldedBlockAt(e->pos());
4128         if (foldedBlock.isValid()) {
4129             toggleBlockVisible(foldedBlock);
4130             viewport()->setCursor(Qt::IBeamCursor);
4131         }
4132
4133         RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
4134         if (refactorMarker.isValid()) {
4135             qDebug() << "refactorMarkerClicked" << refactorMarker.cursor.position();
4136             emit refactorMarkerClicked(refactorMarker);
4137         } else {
4138             updateLink(e);
4139
4140             if (d->m_currentLink.isValid())
4141                 d->m_linkPressed = true;
4142         }
4143     }
4144
4145 #ifdef Q_OS_LINUX
4146     if (handleForwardBackwardMouseButtons(e))
4147         return;
4148 #endif
4149
4150     QPlainTextEdit::mousePressEvent(e);
4151 }
4152
4153 void BaseTextEditorWidget::mouseReleaseEvent(QMouseEvent *e)
4154 {
4155     if (mouseNavigationEnabled()
4156         && d->m_linkPressed
4157         && e->modifiers() & Qt::ControlModifier
4158         && !(e->modifiers() & Qt::ShiftModifier)
4159         && e->button() == Qt::LeftButton
4160         ) {
4161         const QTextCursor cursor = cursorForPosition(e->pos());
4162         if (openLink(findLinkAt(cursor))) {
4163             clearLink();
4164             return;
4165         }
4166     }
4167
4168 #ifndef Q_OS_LINUX
4169     if (handleForwardBackwardMouseButtons(e))
4170         return;
4171 #endif
4172
4173     QPlainTextEdit::mouseReleaseEvent(e);
4174 }
4175
4176 void BaseTextEditorWidget::leaveEvent(QEvent *e)
4177 {
4178     // Clear link emulation when the mouse leaves the editor
4179     clearLink();
4180     QPlainTextEdit::leaveEvent(e);
4181 }
4182
4183 void BaseTextEditorWidget::keyReleaseEvent(QKeyEvent *e)
4184 {
4185     // Clear link emulation when Ctrl is released
4186     if (e->key() == Qt::Key_Control)
4187         clearLink();
4188
4189     QPlainTextEdit::keyReleaseEvent(e);
4190 }
4191
4192 void BaseTextEditorWidget::dragEnterEvent(QDragEnterEvent *e)
4193 {
4194     // If the drag event contains URLs, we don't want to insert them as text
4195     if (e->mimeData()->hasUrls()) {
4196         e->ignore();
4197         return;
4198     }
4199
4200     QPlainTextEdit::dragEnterEvent(e);
4201 }
4202
4203 void BaseTextEditorWidget::extraAreaLeaveEvent(QEvent *)
4204 {
4205     // fake missing mouse move event from Qt
4206     QMouseEvent me(QEvent::MouseMove, QPoint(-1, -1), Qt::NoButton, 0, 0);
4207     extraAreaMouseEvent(&me);
4208 }
4209
4210 void BaseTextEditorWidget::extraAreaMouseEvent(QMouseEvent *e)
4211 {
4212     QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
4213
4214     int markWidth;
4215     extraAreaWidth(&markWidth);
4216
4217     if (d->m_codeFoldingVisible
4218         && e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking
4219         // Update which folder marker is highlighted
4220         const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber;
4221         d->extraAreaHighlightFoldedBlockNumber = -1;
4222
4223         if (e->pos().x() > extraArea()->width() - foldBoxWidth(fontMetrics())) {
4224             d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
4225         } else if (d->m_displaySettings.m_highlightBlocks) {
4226             QTextCursor cursor = textCursor();
4227             d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
4228         }
4229
4230         if (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber)
4231             d->m_highlightBlocksTimer->start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0);
4232         }
4233
4234     // Set whether the mouse cursor is a hand or normal arrow
4235     if (e->type() == QEvent::MouseMove) {
4236         bool hand = (e->pos().x() <= markWidth);
4237         if (hand != (d->m_extraArea->cursor().shape() == Qt::PointingHandCursor))
4238             d->m_extraArea->setCursor(hand ? Qt::PointingHandCursor : Qt::ArrowCursor);
4239     }
4240
4241     if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) {
4242         if (e->button() == Qt::LeftButton) {
4243             int boxWidth = foldBoxWidth(fontMetrics());
4244             if (d->m_codeFoldingVisible && e->pos().x() > extraArea()->width() - boxWidth) {
4245                 if (!cursor.block().next().isVisible()) {
4246                     toggleBlockVisible(cursor.block());
4247                     d->moveCursorVisible(false);
4248                 } else if (foldBox().contains(e->pos())) {
4249                     cursor.setPosition(
4250                             document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position()
4251                             );
4252                     QTextBlock c = cursor.block();
4253                     toggleBlockVisible(c);
4254                     d->moveCursorVisible(false);
4255                 }
4256             } else if (d->m_lineNumbersVisible && e->pos().x() > markWidth) {
4257                 QTextCursor selection = cursor;
4258                 selection.setVisualNavigation(true);
4259                 d->extraAreaSelectionAnchorBlockNumber = selection.blockNumber();
4260                 selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
4261                 selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
4262                 setTextCursor(selection);
4263             } else {
4264                 d->extraAreaToggleMarkBlockNumber = cursor.blockNumber();
4265             }
4266         } else if (d->m_marksVisible && e->button() == Qt::RightButton) {
4267             QMenu * contextMenu = new QMenu(this);
4268             emit editor()->markContextMenuRequested(editor(), cursor.blockNumber() + 1, contextMenu);
4269             if (!contextMenu->isEmpty())
4270                 contextMenu->exec(e->globalPos());
4271             delete contextMenu;
4272         }
4273     } else if (d->extraAreaSelectionAnchorBlockNumber >= 0) {
4274         QTextCursor selection = cursor;
4275         selection.setVisualNavigation(true);
4276         if (e->type() == QEvent::MouseMove) {
4277             QTextBlock anchorBlock = document()->findBlockByNumber(d->extraAreaSelectionAnchorBlockNumber);
4278             selection.setPosition(anchorBlock.position());
4279             if (cursor.blockNumber() < d->extraAreaSelectionAnchorBlockNumber) {
4280                 selection.movePosition(QTextCursor::EndOfBlock);
4281                 selection.movePosition(QTextCursor::Right);
4282             }
4283             selection.setPosition(cursor.block().position(), QTextCursor::KeepAnchor);
4284             if (cursor.blockNumber() >= d->extraAreaSelectionAnchorBlockNumber) {
4285                 selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
4286                 selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
4287             }
4288
4289             if (e->pos().y() >= 0 && e->pos().y() <= d->m_extraArea->height())
4290                 d->autoScrollTimer.stop();
4291             else if (!d->autoScrollTimer.isActive())
4292                 d->autoScrollTimer.start(100, this);
4293
4294         } else {
4295             d->autoScrollTimer.stop();
4296             d->extraAreaSelectionAnchorBlockNumber = -1;
4297             return;
4298         }
4299         setTextCursor(selection);
4300     } else if (d->extraAreaToggleMarkBlockNumber >= 0 && d->m_marksVisible && d->m_requestMarkEnabled) {
4301         if (e->type() == QEvent::MouseButtonRelease && e->button() == Qt::LeftButton) {
4302             int n = d->extraAreaToggleMarkBlockNumber;
4303             d->extraAreaToggleMarkBlockNumber = -1;
4304             if (cursor.blockNumber() == n) {
4305                 int line = n + 1;
4306                 emit editor()->markRequested(editor(), line);
4307             }
4308         }
4309     }
4310 }
4311
4312 void BaseTextEditorWidget::ensureCursorVisible()
4313 {
4314     QTextBlock block = textCursor().block();
4315     if (!block.isVisible()) {
4316         while (!block.isVisible() && block.previous().isValid())
4317             block = block.previous();
4318         toggleBlockVisible(block);
4319     }
4320     QPlainTextEdit::ensureCursorVisible();
4321 }
4322
4323 void BaseTextEditorWidget::toggleBlockVisible(const QTextBlock &block)
4324 {
4325     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document()->documentLayout());
4326     QTC_ASSERT(documentLayout, return);
4327
4328     bool visible = block.next().isVisible();
4329     BaseTextDocumentLayout::doFoldOrUnfold(block, !visible);
4330     documentLayout->requestUpdate();
4331     documentLayout->emitDocumentSizeChanged();
4332 }
4333
4334
4335 const TabSettings &BaseTextEditorWidget::tabSettings() const
4336 {
4337     return d->m_document->tabSettings();
4338 }
4339
4340 const DisplaySettings &BaseTextEditorWidget::displaySettings() const
4341 {
4342     return d->m_displaySettings;
4343 }
4344
4345
4346 void BaseTextEditorWidget::indentOrUnindent(bool doIndent)
4347 {
4348     const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
4349
4350     QTextCursor cursor = textCursor();
4351     maybeClearSomeExtraSelections(cursor);
4352     cursor.beginEditBlock();
4353
4354     if (cursor.hasSelection()) {
4355         // Indent or unindent the selected lines
4356         int pos = cursor.position();
4357         int anchor = cursor.anchor();
4358         int start = qMin(anchor, pos);
4359         int end = qMax(anchor, pos);
4360
4361         QTextDocument *doc = document();
4362         QTextBlock startBlock = doc->findBlock(start);
4363         QTextBlock endBlock = doc->findBlock(end-1).next();
4364
4365         for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
4366             QString text = block.text();
4367             int indentPosition = tabSettings.lineIndentPosition(text);
4368             if (!doIndent && !indentPosition)
4369                 indentPosition = tabSettings.firstNonSpace(text);
4370             int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent);
4371             cursor.setPosition(block.position() + indentPosition);
4372             cursor.insertText(tabSettings.indentationString(0, targetColumn, block));
4373             cursor.setPosition(block.position());
4374             cursor.setPosition(block.position() + indentPosition, QTextCursor::KeepAnchor);
4375             cursor.removeSelectedText();
4376         }
4377         cursor.endEditBlock();
4378     } else {
4379         // Indent or unindent at cursor position
4380         QTextBlock block = cursor.block();
4381         QString text = block.text();
4382         int indentPosition = cursor.positionInBlock();
4383         int spaces = tabSettings.spacesLeftFromPosition(text, indentPosition);
4384         int startColumn = tabSettings.columnAt(text, indentPosition - spaces);
4385         int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent);
4386         cursor.setPosition(block.position() + indentPosition);
4387         cursor.setPosition(block.position() + indentPosition - spaces, QTextCursor::KeepAnchor);
4388         cursor.removeSelectedText();
4389         cursor.insertText(tabSettings.indentationString(startColumn, targetColumn, block));
4390         cursor.endEditBlock();
4391         setTextCursor(cursor);
4392     }
4393 }
4394
4395 void BaseTextEditorWidget::handleHomeKey(bool anchor)
4396 {
4397     QTextCursor cursor = textCursor();
4398     QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
4399
4400     if (anchor)
4401         mode = QTextCursor::KeepAnchor;
4402
4403     const int initpos = cursor.position();
4404     int pos = cursor.block().position();
4405     QChar character = characterAt(pos);
4406     const QLatin1Char tab = QLatin1Char('\t');
4407
4408     while (character == tab || character.category() == QChar::Separator_Space) {
4409         ++pos;
4410         if (pos == initpos)
4411             break;
4412         character = characterAt(pos);
4413     }
4414
4415     // Go to the start of the block when we're already at the start of the text
4416     if (pos == initpos)
4417         pos = cursor.block().position();
4418
4419     cursor.setPosition(pos, mode);
4420     setTextCursor(cursor);
4421 }
4422
4423 void BaseTextEditorWidget::handleBackspaceKey()
4424 {
4425     QTextCursor cursor = textCursor();
4426     int pos = cursor.position();
4427     QTC_ASSERT(!cursor.hasSelection(), return);
4428
4429     bool cursorWithinSnippet = false;
4430     if (d->m_snippetOverlay->isVisible()) {
4431         QTextCursor snippetCursor = cursor;
4432         snippetCursor.movePosition(QTextCursor::Left);
4433         cursorWithinSnippet = d->snippetCheckCursor(snippetCursor);
4434     }
4435
4436     const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
4437
4438     if (tabSettings.m_autoIndent && d->m_autoCompleter->autoBackspace(cursor))
4439         return;
4440
4441     bool handled = false;
4442     if (!tabSettings.m_smartBackspace) {
4443         if (cursorWithinSnippet)
4444             cursor.beginEditBlock();
4445         cursor.deletePreviousChar();
4446         handled = true;
4447     } else {
4448         QTextBlock currentBlock = cursor.block();
4449         int positionInBlock = pos - currentBlock.position();
4450         const QString blockText = currentBlock.text();
4451         if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
4452             if (cursorWithinSnippet)
4453                 cursor.beginEditBlock();
4454             cursor.deletePreviousChar();
4455             handled = true;
4456         } else {
4457             int previousIndent = 0;
4458             const int indent = tabSettings.columnAt(blockText, positionInBlock);
4459
4460             for (QTextBlock previousNonEmptyBlock = currentBlock.previous();
4461                  previousNonEmptyBlock.isValid();
4462                  previousNonEmptyBlock = previousNonEmptyBlock.previous()) {
4463                 QString previousNonEmptyBlockText = previousNonEmptyBlock.text();
4464                 if (previousNonEmptyBlockText.trimmed().isEmpty())
4465                     continue;
4466                 previousIndent =
4467                     tabSettings.columnAt(previousNonEmptyBlockText,
4468                                          tabSettings.firstNonSpace(previousNonEmptyBlockText));
4469                 if (previousIndent < indent) {
4470                     cursor.beginEditBlock();
4471                     cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
4472                     cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
4473                     cursor.endEditBlock();
4474                     handled = true;
4475                     break;
4476                 }
4477             }
4478         }
4479     }
4480
4481     if (!handled) {
4482         if (cursorWithinSnippet)
4483             cursor.beginEditBlock();
4484         cursor.deletePreviousChar();
4485     }
4486
4487     if (cursorWithinSnippet) {
4488         cursor.endEditBlock();
4489         d->m_snippetOverlay->updateEquivalentSelections(cursor);
4490     }
4491
4492     setTextCursor(cursor);
4493 }
4494
4495 void BaseTextEditorWidget::wheelEvent(QWheelEvent *e)
4496 {
4497     d->clearVisibleFoldedBlock();
4498     if (scrollWheelZoomingEnabled() && e->modifiers() & Qt::ControlModifier) {
4499         const int delta = e->delta();
4500         if (delta < 0)
4501             zoomOut();
4502         else if (delta > 0)
4503             zoomIn();
4504         return;
4505     }
4506     QPlainTextEdit::wheelEvent(e);
4507 }
4508
4509 void BaseTextEditorWidget::zoomIn(int range)
4510 {
4511     d->clearVisibleFoldedBlock();
4512     emit requestFontZoom(range*10);
4513 }
4514
4515 void BaseTextEditorWidget::zoomOut(int range)
4516 {
4517     zoomIn(-range);
4518 }
4519
4520 void BaseTextEditorWidget::zoomReset()
4521 {
4522     emit requestZoomReset();
4523 }
4524
4525 void BaseTextEditorWidget::indentInsertedText(const QTextCursor &tc)
4526 {
4527     indent(tc.document(), tc, QChar::Null);
4528 }
4529
4530 void BaseTextEditorWidget::indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar)
4531 {
4532     maybeClearSomeExtraSelections(cursor);
4533     d->m_indenter->indent(doc, cursor, typedChar, this);
4534 }
4535
4536 void BaseTextEditorWidget::reindent(QTextDocument *doc, const QTextCursor &cursor)
4537 {
4538     maybeClearSomeExtraSelections(cursor);
4539     d->m_indenter->reindent(doc, cursor, this);
4540 }
4541
4542 BaseTextEditorWidget::Link BaseTextEditorWidget::findLinkAt(const QTextCursor &, bool)
4543 {
4544     return Link();
4545 }
4546
4547 bool BaseTextEditorWidget::openLink(const Link &link)
4548 {
4549     if (link.fileName.isEmpty())
4550         return false;
4551
4552     if (baseTextDocument()->fileName() == link.fileName) {
4553         Core::EditorManager *editorManager = Core::EditorManager::instance();
4554         editorManager->addCurrentPositionToNavigationHistory();
4555         gotoLine(link.line, link.column);
4556         setFocus();
4557         return true;
4558     }
4559
4560     return openEditorAt(link.fileName, link.line, link.column, QString(),
4561                           Core::EditorManager::IgnoreNavigationHistory
4562                         | Core::EditorManager::ModeSwitch);
4563 }
4564
4565 void BaseTextEditorWidget::updateLink(QMouseEvent *e)
4566 {
4567     bool linkFound = false;
4568
4569     if (mouseNavigationEnabled() && e->modifiers() & Qt::ControlModifier) {
4570         // Link emulation behaviour for 'go to definition'
4571         const QTextCursor cursor = cursorForPosition(e->pos());
4572
4573         // Check that the mouse was actually on the text somewhere
4574         bool onText = cursorRect(cursor).right() >= e->x();
4575         if (!onText) {
4576             QTextCursor nextPos = cursor;
4577             nextPos.movePosition(QTextCursor::Right);
4578             onText = cursorRect(nextPos).right() >= e->x();
4579         }
4580
4581         const Link link = findLinkAt(cursor, false);
4582
4583         if (onText && link.isValid()) {
4584             showLink(link);
4585             linkFound = true;
4586         }
4587     }
4588
4589     if (!linkFound)
4590         clearLink();
4591 }
4592
4593 void BaseTextEditorWidget::showLink(const Link &link)
4594 {
4595     if (d->m_currentLink == link)
4596         return;
4597
4598     QTextEdit::ExtraSelection sel;
4599     sel.cursor = textCursor();
4600     sel.cursor.setPosition(link.begin);
4601     sel.cursor.setPosition(link.end, QTextCursor::KeepAnchor);
4602     sel.format = d->m_linkFormat;
4603     sel.format.setFontUnderline(true);
4604     setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
4605     viewport()->setCursor(Qt::PointingHandCursor);
4606     d->m_currentLink = link;
4607     d->m_linkPressed = false;
4608 }
4609
4610 void BaseTextEditorWidget::clearLink()
4611 {
4612     if (!d->m_currentLink.isValid())
4613         return;
4614
4615     setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
4616     viewport()->setCursor(Qt::IBeamCursor);
4617     d->m_currentLink = Link();
4618     d->m_linkPressed = false;
4619 }
4620
4621 void BaseTextEditorPrivate::updateMarksBlock(const QTextBlock &block)
4622 {
4623     if (const TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
4624         foreach (ITextMark *mrk, userData->marks())
4625             mrk->updateBlock(block);
4626 }
4627
4628 void BaseTextEditorPrivate::updateMarksLineNumber()
4629 {
4630     QTextDocument *doc = q->document();
4631     QTextBlock block = doc->begin();
4632     int blockNumber = 0;
4633     while (block.isValid()) {
4634         if (const TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
4635             foreach (ITextMark *mrk, userData->marks()) {
4636                 mrk->updateLineNumber(blockNumber + 1);
4637             }
4638         block = block.next();
4639         ++blockNumber;
4640     }
4641 }
4642
4643 void BaseTextEditorWidget::markBlocksAsChanged(QList<int> blockNumbers)
4644 {
4645     QTextBlock block = document()->begin();
4646     while (block.isValid()) {
4647         if (block.revision() < 0)
4648             block.setRevision(-block.revision() - 1);
4649         block = block.next();
4650     }
4651     foreach (const int blockNumber, blockNumbers) {
4652         QTextBlock block = document()->findBlockByNumber(blockNumber);
4653         if (block.isValid())
4654             block.setRevision(-block.revision() - 1);
4655     }
4656 }
4657
4658
4659 void BaseTextEditorWidget::highlightSearchResults(const QString &txt, Find::FindFlags findFlags)
4660 {
4661     QString pattern = txt;
4662     if (pattern.size() < 2)
4663         pattern.clear(); // highlighting single characters is a bit pointless
4664
4665     if (d->m_searchExpr.pattern() == pattern)
4666         return;
4667     d->m_searchExpr.setPattern(pattern);
4668     d->m_searchExpr.setPatternSyntax((findFlags & Find::FindRegularExpression) ?
4669                                      QRegExp::RegExp : QRegExp::FixedString);
4670     d->m_searchExpr.setCaseSensitivity((findFlags & Find::FindCaseSensitively) ?
4671                                        Qt::CaseSensitive : Qt::CaseInsensitive);
4672     d->m_findFlags = findFlags;
4673
4674     d->m_delayedUpdateTimer->start(10);
4675 }
4676
4677 int BaseTextEditorWidget::verticalBlockSelectionFirstColumn() const
4678 {
4679     if (d->m_inBlockSelectionMode)
4680         return d->m_blockSelection.firstVisualColumn;
4681     return -1;
4682 }
4683
4684 int BaseTextEditorWidget::verticalBlockSelectionLastColumn() const
4685 {
4686     if (d->m_inBlockSelectionMode)
4687         return d->m_blockSelection.lastVisualColumn;
4688     return -1;
4689 }
4690
4691 QRegion BaseTextEditorWidget::translatedLineRegion(int lineStart, int lineEnd) const
4692 {
4693     QRegion region;
4694     for (int i = lineStart ; i <= lineEnd; i++) {
4695         QTextBlock block = document()->findBlockByNumber(i);
4696         QPoint topLeft = blockBoundingGeometry(block).translated(contentOffset()).topLeft().toPoint();
4697
4698         if (block.isValid()) {
4699             QTextLayout *layout = block.layout();
4700
4701             for (int i = 0; i < layout->lineCount();i++) {
4702                 QTextLine line = layout->lineAt(i);
4703                 region += line.naturalTextRect().translated(topLeft).toRect();
4704             }
4705         }
4706     }
4707     return region;
4708 }
4709
4710 void BaseTextEditorWidget::setFindScope(const QTextCursor &start, const QTextCursor &end,
4711                                   int verticalBlockSelectionFirstColumn,
4712                                   int verticalBlockSelectionLastColumn)
4713 {
4714     if (start != d->m_findScopeStart
4715             || end != d->m_findScopeEnd
4716             || verticalBlockSelectionFirstColumn != d->m_findScopeVerticalBlockSelectionFirstColumn
4717             || verticalBlockSelectionLastColumn != d->m_findScopeVerticalBlockSelectionLastColumn) {
4718         d->m_findScopeStart = start;
4719         d->m_findScopeEnd = end;
4720         d->m_findScopeVerticalBlockSelectionFirstColumn = verticalBlockSelectionFirstColumn;
4721         d->m_findScopeVerticalBlockSelectionLastColumn = verticalBlockSelectionLastColumn;
4722         viewport()->update();
4723     }
4724 }
4725
4726 void BaseTextEditorWidget::_q_animateUpdate(int position, QPointF lastPos, QRectF rect)
4727 {
4728     QTextCursor cursor(textCursor());
4729     cursor.setPosition(position);
4730     viewport()->update(QRectF(cursorRect(cursor).topLeft() + rect.topLeft(), rect.size()).toAlignedRect());
4731     if (!lastPos.isNull())
4732         viewport()->update(QRectF(lastPos + rect.topLeft(), rect.size()).toAlignedRect());
4733 }
4734
4735
4736 BaseTextEditorAnimator::BaseTextEditorAnimator(QObject *parent)
4737         :QObject(parent)
4738 {
4739     m_value = 0;
4740     m_timeline = new QTimeLine(256, this);
4741     m_timeline->setCurveShape(QTimeLine::SineCurve);
4742     connect(m_timeline, SIGNAL(valueChanged(qreal)), this, SLOT(step(qreal)));
4743     connect(m_timeline, SIGNAL(finished()), this, SLOT(deleteLater()));
4744     m_timeline->start();
4745 }
4746
4747
4748 void BaseTextEditorAnimator::setData(QFont f, QPalette pal, const QString &text)
4749 {
4750     m_font = f;
4751     m_palette = pal;
4752     m_text = text;
4753     QFontMetrics fm(m_font);
4754     m_size = QSizeF(fm.width(m_text), fm.height());
4755 }
4756
4757 void BaseTextEditorAnimator::draw(QPainter *p, const QPointF &pos)
4758 {
4759     m_lastDrawPos = pos;
4760     p->setPen(m_palette.text().color());
4761     QFont f = m_font;
4762     f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
4763     QFontMetrics fm(f);
4764     int width = fm.width(m_text);
4765     QRectF r((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
4766     r.translate(pos);
4767     p->fillRect(r, m_palette.base());
4768     p->setFont(f);
4769     p->drawText(r, m_text);
4770 }
4771
4772 bool BaseTextEditorAnimator::isRunning() const
4773 {
4774     return m_timeline->state() == QTimeLine::Running;
4775 }
4776
4777 QRectF BaseTextEditorAnimator::rect() const
4778 {
4779     QFont f = m_font;
4780     f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
4781     QFontMetrics fm(f);
4782     int width = fm.width(m_text);
4783     return QRectF((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
4784 }
4785
4786 void BaseTextEditorAnimator::step(qreal v)
4787 {
4788     QRectF before = rect();
4789     m_value = v;
4790     QRectF after = rect();
4791     emit updateRequest(m_position, m_lastDrawPos, before.united(after));
4792 }
4793
4794 void BaseTextEditorAnimator::finish()
4795 {
4796     m_timeline->stop();
4797     step(0);
4798     deleteLater();
4799 }
4800
4801 void BaseTextEditorWidget::_q_matchParentheses()
4802 {
4803     if (isReadOnly())
4804         return;
4805
4806     QTextCursor backwardMatch = textCursor();
4807     QTextCursor forwardMatch = textCursor();
4808     const TextBlockUserData::MatchType backwardMatchType = TextBlockUserData::matchCursorBackward(&backwardMatch);
4809     const TextBlockUserData::MatchType forwardMatchType = TextBlockUserData::matchCursorForward(&forwardMatch);
4810
4811     QList<QTextEdit::ExtraSelection> extraSelections;
4812
4813     if (backwardMatchType == TextBlockUserData::NoMatch && forwardMatchType == TextBlockUserData::NoMatch) {
4814         setExtraSelections(ParenthesesMatchingSelection, extraSelections); // clear
4815         return;
4816     }
4817
4818     int animatePosition = -1;
4819     if (backwardMatch.hasSelection()) {
4820         QTextEdit::ExtraSelection sel;
4821         if (backwardMatchType == TextBlockUserData::Mismatch) {
4822             sel.cursor = backwardMatch;
4823             sel.format = d->m_mismatchFormat;
4824         } else {
4825
4826             if (d->m_displaySettings.m_animateMatchingParentheses) {
4827                 animatePosition = backwardMatch.selectionStart();
4828             } else if (d->m_formatRange) {
4829                 sel.cursor = backwardMatch;
4830                 sel.format = d->m_rangeFormat;
4831                 extraSelections.append(sel);
4832             }
4833
4834             sel.cursor = backwardMatch;
4835             sel.format = d->m_matchFormat;
4836
4837             sel.cursor.setPosition(backwardMatch.selectionStart());
4838             sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
4839             extraSelections.append(sel);
4840
4841             sel.cursor.setPosition(backwardMatch.selectionEnd());
4842             sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
4843         }
4844         extraSelections.append(sel);
4845     }
4846
4847     if (forwardMatch.hasSelection()) {
4848         QTextEdit::ExtraSelection sel;
4849         if (forwardMatchType == TextBlockUserData::Mismatch) {
4850             sel.cursor = forwardMatch;
4851             sel.format = d->m_mismatchFormat;
4852         } else {
4853
4854             if (d->m_displaySettings.m_animateMatchingParentheses) {
4855                 animatePosition = forwardMatch.selectionEnd()-1;
4856             } else if (d->m_formatRange) {
4857                 sel.cursor = forwardMatch;
4858                 sel.format = d->m_rangeFormat;
4859                 extraSelections.append(sel);
4860             }
4861
4862             sel.cursor = forwardMatch;
4863             sel.format = d->m_matchFormat;
4864
4865             sel.cursor.setPosition(forwardMatch.selectionStart());
4866             sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
4867             extraSelections.append(sel);
4868
4869             sel.cursor.setPosition(forwardMatch.selectionEnd());
4870             sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
4871         }
4872         extraSelections.append(sel);
4873     }
4874
4875
4876     if (animatePosition >= 0) {
4877         foreach (const QTextEdit::ExtraSelection &sel, BaseTextEditorWidget::extraSelections(ParenthesesMatchingSelection)) {
4878             if (sel.cursor.selectionStart() == animatePosition
4879                 || sel.cursor.selectionEnd() - 1 == animatePosition) {
4880                 animatePosition = -1;
4881                 break;
4882             }
4883         }
4884     }
4885
4886     if (animatePosition >= 0) {
4887         if (d->m_animator)
4888             d->m_animator->finish();  // one animation is enough
4889         d->m_animator = new BaseTextEditorAnimator(this);
4890         d->m_animator->setPosition(animatePosition);
4891         QPalette pal;
4892         pal.setBrush(QPalette::Text, d->m_matchFormat.foreground());
4893         pal.setBrush(QPalette::Base, d->m_rangeFormat.background());
4894         d->m_animator->setData(font(), pal, characterAt(d->m_animator->position()));
4895         connect(d->m_animator, SIGNAL(updateRequest(int,QPointF,QRectF)),
4896                 this, SLOT(_q_animateUpdate(int,QPointF,QRectF)));
4897     }
4898
4899     setExtraSelections(ParenthesesMatchingSelection, extraSelections);
4900 }
4901
4902 void BaseTextEditorWidget::_q_highlightBlocks()
4903 {
4904     BaseTextEditorPrivateHighlightBlocks highlightBlocksInfo;
4905
4906     QTextBlock block;
4907     if (d->extraAreaHighlightFoldedBlockNumber >= 0) {
4908         block = document()->findBlockByNumber(d->extraAreaHighlightFoldedBlockNumber);
4909         if (block.isValid()
4910             && block.next().isValid()
4911             && BaseTextDocumentLayout::foldingIndent(block.next())
4912             > BaseTextDocumentLayout::foldingIndent(block))
4913             block = block.next();
4914     }
4915
4916     QTextBlock closeBlock = block;
4917     while (block.isValid()) {
4918         int foldingIndent = BaseTextDocumentLayout::foldingIndent(block);
4919
4920         while (block.previous().isValid() && BaseTextDocumentLayout::foldingIndent(block) >= foldingIndent)
4921             block = block.previous();
4922         int nextIndent = BaseTextDocumentLayout::foldingIndent(block);
4923         if (nextIndent == foldingIndent)
4924             break;
4925         highlightBlocksInfo.open.prepend(block.blockNumber());
4926         while (closeBlock.next().isValid()
4927             && BaseTextDocumentLayout::foldingIndent(closeBlock.next()) >= foldingIndent )
4928             closeBlock = closeBlock.next();
4929         highlightBlocksInfo.close.append(closeBlock.blockNumber());
4930         int visualIndent = qMin(d->visualIndent(block), d->visualIndent(closeBlock));
4931         highlightBlocksInfo.visualIndent.prepend(visualIndent);
4932     }
4933
4934 #if 0
4935     if (block.isValid()) {
4936         QTextCursor cursor(block);
4937         if (d->extraAreaHighlightCollapseColumn >= 0)
4938             cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn,
4939                                                         block.length()-1));
4940         QTextCursor closeCursor;
4941         bool firstRun = true;
4942         while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) {
4943             firstRun = false;
4944             highlightBlocksInfo.open.prepend(cursor.blockNumber());
4945             int visualIndent = d->visualIndent(cursor.block());
4946             if (closeCursor.isNull())
4947                 closeCursor = cursor;
4948             if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) {
4949                 highlightBlocksInfo.close.append(closeCursor.blockNumber());
4950                 visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block()));
4951             }
4952             highlightBlocksInfo.visualIndent.prepend(visualIndent);
4953         }
4954     }
4955 #endif
4956     if (d->m_highlightBlocksInfo != highlightBlocksInfo) {
4957         d->m_highlightBlocksInfo = highlightBlocksInfo;
4958         viewport()->update();
4959         d->m_extraArea->update();
4960     }
4961 }
4962
4963 void BaseTextEditorWidget::setActionHack(QObject *hack)
4964 {
4965     d->m_actionHack = hack;
4966 }
4967
4968 QObject *BaseTextEditorWidget::actionHack() const
4969 {
4970     return d->m_actionHack;
4971 }
4972
4973 void BaseTextEditorWidget::changeEvent(QEvent *e)
4974 {
4975     QPlainTextEdit::changeEvent(e);
4976     if (e->type() == QEvent::ApplicationFontChange
4977         || e->type() == QEvent::FontChange) {
4978         if (d->m_extraArea) {
4979             QFont f = d->m_extraArea->font();
4980             f.setPointSizeF(font().pointSizeF());
4981             d->m_extraArea->setFont(f);
4982             slotUpdateExtraAreaWidth();
4983             d->m_extraArea->update();
4984         }
4985     }
4986 }
4987
4988 void BaseTextEditorWidget::focusInEvent(QFocusEvent *e)
4989 {
4990     QPlainTextEdit::focusInEvent(e);
4991     updateHighlights();
4992 }
4993
4994 void BaseTextEditorWidget::focusOutEvent(QFocusEvent *e)
4995 {
4996     QPlainTextEdit::focusOutEvent(e);
4997     if (viewport()->cursor().shape() == Qt::BlankCursor)
4998         viewport()->setCursor(Qt::IBeamCursor);
4999 }
5000
5001
5002 void BaseTextEditorWidget::maybeSelectLine()
5003 {
5004     QTextCursor cursor = textCursor();
5005     if (!cursor.hasSelection()) {
5006         const QTextBlock &block = cursor.block();
5007         if (block.next().isValid()) {
5008             cursor.setPosition(block.position());
5009             cursor.setPosition(block.next().position(), QTextCursor::KeepAnchor);
5010         } else {
5011             cursor.movePosition(QTextCursor::EndOfBlock);
5012             cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
5013             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
5014         }
5015         setTextCursor(cursor);
5016     }
5017 }
5018
5019 // shift+del
5020 void BaseTextEditorWidget::cutLine()
5021 {
5022     maybeSelectLine();
5023     cut();
5024 }
5025
5026 void BaseTextEditorWidget::deleteLine()
5027 {
5028     maybeSelectLine();
5029     textCursor().removeSelectedText();
5030 }
5031
5032 void BaseTextEditorWidget::setExtraSelections(ExtraSelectionKind kind, const QList<QTextEdit::ExtraSelection> &selections)
5033 {
5034     if (selections.isEmpty() && d->m_extraSelections[kind].isEmpty())
5035         return;
5036     d->m_extraSelections[kind] = selections;
5037
5038     if (kind == CodeSemanticsSelection) {
5039         d->m_overlay->clear();
5040         foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
5041             d->m_overlay->addOverlaySelection(selection.cursor,
5042                                               selection.format.background().color(),
5043                                               selection.format.background().color(),
5044                                               TextEditorOverlay::LockSize);
5045         }
5046         d->m_overlay->setVisible(!d->m_overlay->isEmpty());
5047     } else if (kind == SnippetPlaceholderSelection) {
5048         d->m_snippetOverlay->clear();
5049         foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
5050             d->m_snippetOverlay->addOverlaySelection(selection.cursor,
5051                                               selection.format.background().color(),
5052                                               selection.format.background().color(),
5053                                               TextEditorOverlay::ExpandBegin);
5054         }
5055         d->m_snippetOverlay->mapEquivalentSelections();
5056         d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty());
5057     } else {
5058         QList<QTextEdit::ExtraSelection> all;
5059         for (int i = 0; i < NExtraSelectionKinds; ++i) {
5060             if (i == CodeSemanticsSelection || i == SnippetPlaceholderSelection)
5061                 continue;
5062             all += d->m_extraSelections[i];
5063         }
5064         QPlainTextEdit::setExtraSelections(all);
5065     }
5066 }
5067
5068 QList<QTextEdit::ExtraSelection> BaseTextEditorWidget::extraSelections(ExtraSelectionKind kind) const
5069 {
5070     return d->m_extraSelections[kind];
5071 }
5072
5073 void BaseTextEditorWidget::maybeClearSomeExtraSelections(const QTextCursor &cursor)
5074 {
5075     const int smallSelectionSize = 50 * 50;
5076     if (cursor.selectionEnd() - cursor.selectionStart() < smallSelectionSize)
5077         return;
5078
5079     d->m_extraSelections[UndefinedSymbolSelection].clear();
5080     d->m_extraSelections[ObjCSelection].clear();
5081     d->m_extraSelections[CodeWarningsSelection].clear();
5082
5083     QList<QTextEdit::ExtraSelection> all;
5084     for (int i = 0; i < NExtraSelectionKinds; ++i) {
5085         if (i == CodeSemanticsSelection || i == SnippetPlaceholderSelection)
5086             continue;
5087         all += d->m_extraSelections[i];
5088     }
5089     QPlainTextEdit::setExtraSelections(all);
5090 }
5091
5092 QString BaseTextEditorWidget::extraSelectionTooltip(int pos) const
5093 {
5094     QList<QTextEdit::ExtraSelection> all;
5095     for (int i = 0; i < NExtraSelectionKinds; ++i) {
5096         const QList<QTextEdit::ExtraSelection> &sel = d->m_extraSelections[i];
5097         for (int j = 0; j < sel.size(); ++j) {
5098             const QTextEdit::ExtraSelection &s = sel.at(j);
5099             if (s.cursor.selectionStart() <= pos
5100                 && s.cursor.selectionEnd() >= pos
5101                 && !s.format.toolTip().isEmpty())
5102                 return s.format.toolTip();
5103         }
5104     }
5105     return QString();
5106 }
5107
5108 // the blocks list must be sorted
5109 void BaseTextEditorWidget::setIfdefedOutBlocks(const QList<BaseTextEditorWidget::BlockRange> &blocks)
5110 {
5111     QTextDocument *doc = document();
5112     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
5113     QTC_ASSERT(documentLayout, return);
5114
5115     bool needUpdate = false;
5116
5117     QTextBlock block = doc->firstBlock();
5118
5119     int rangeNumber = 0;
5120     int braceDepthDelta = 0;
5121     while (block.isValid()) {
5122         bool cleared = false;
5123         bool set = false;
5124         if (rangeNumber < blocks.size()) {
5125             const BlockRange &range = blocks.at(rangeNumber);
5126             if (block.position() >= range.first && ((block.position() + block.length() - 1) <= range.last || !range.last)) {
5127                 set = BaseTextDocumentLayout::setIfdefedOut(block);
5128             } else {
5129                 cleared = BaseTextDocumentLayout::clearIfdefedOut(block);
5130             }
5131             if (block.contains(range.last))
5132                 ++rangeNumber;
5133         } else {
5134             cleared = BaseTextDocumentLayout::clearIfdefedOut(block);
5135         }
5136
5137         if (cleared || set) {
5138             needUpdate = true;
5139             int delta = BaseTextDocumentLayout::braceDepthDelta(block);
5140             if (cleared)
5141                 braceDepthDelta += delta;
5142             else if (set)
5143                 braceDepthDelta -= delta;
5144         }
5145
5146         if (braceDepthDelta) {
5147             BaseTextDocumentLayout::changeBraceDepth(block,braceDepthDelta);
5148             BaseTextDocumentLayout::changeFoldingIndent(block, braceDepthDelta); // ### C++ only, refactor!
5149         }
5150
5151         block = block.next();
5152     }
5153
5154     if (needUpdate)
5155         documentLayout->requestUpdate();
5156 }
5157
5158 void BaseTextEditorWidget::format()
5159 {
5160     QTextCursor cursor = textCursor();
5161     cursor.beginEditBlock();
5162     indent(document(), cursor, QChar::Null);
5163     cursor.endEditBlock();
5164 }
5165
5166 void BaseTextEditorWidget::rewrapParagraph()
5167 {
5168     const int paragraphWidth = displaySettings().m_wrapColumn;
5169     const QRegExp anyLettersOrNumbers = QRegExp("\\w");
5170     const int tabSize = tabSettings().m_tabSize;
5171
5172     QTextCursor cursor = textCursor();
5173     cursor.beginEditBlock();
5174
5175     // Find start of paragraph.
5176
5177     while (cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) {
5178         QTextBlock block = cursor.block();
5179         QString text = block.text();
5180
5181         // If this block is empty, move marker back to previous and terminate.
5182         if (!text.contains(anyLettersOrNumbers)) {
5183             cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
5184             break;
5185         }
5186     }
5187
5188     cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
5189
5190     // Find indent level of current block.
5191
5192     int indentLevel = 0;
5193     QString text = cursor.block().text();
5194
5195     for (int i = 0; i < text.length(); i++) {
5196         const QChar ch = text.at(i);
5197
5198         if (ch == QLatin1Char(' '))
5199             indentLevel++;
5200         else if (ch == QLatin1Char('\t'))
5201             indentLevel += tabSize - (indentLevel % tabSize);
5202         else
5203             break;
5204     }
5205
5206     // If there is a common prefix, it should be kept and expanded to all lines.
5207     // this allows nice reflowing of doxygen style comments.
5208     QTextCursor nextBlock = cursor;
5209     QString commonPrefix;
5210
5211     if (nextBlock.movePosition(QTextCursor::NextBlock))
5212     {
5213          QString nText = nextBlock.block().text();
5214          int maxLength = qMin(text.length(), nText.length());
5215
5216          for (int i = 0; i < maxLength; ++i) {
5217              const QChar ch = text.at(i);
5218
5219              if (ch != nText[i] || ch.isLetterOrNumber())
5220                  break;
5221              commonPrefix.append(ch);
5222          }
5223     }
5224
5225
5226     // Find end of paragraph.
5227     while (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor)) {
5228         QString text = cursor.block().text();
5229
5230         if (!text.contains(anyLettersOrNumbers))
5231             break;
5232     }
5233
5234
5235     QString selectedText = cursor.selectedText();
5236
5237     // Preserve initial indent level.or common prefix.
5238     QString spacing;
5239
5240     if (commonPrefix.isEmpty()) {
5241         spacing = tabSettings().indentationString(0, indentLevel, textCursor().block());
5242     } else {
5243         spacing = commonPrefix;
5244         indentLevel = commonPrefix.length();
5245     }
5246
5247     int currentLength = indentLevel;
5248     QString result;
5249     result.append(spacing);
5250
5251     // Remove existing instances of any common prefix from paragraph to
5252     // reflow.
5253     selectedText.remove(0, commonPrefix.length());
5254     commonPrefix.prepend(QChar::ParagraphSeparator);
5255     selectedText.replace(commonPrefix, QLatin1String("\n"));
5256
5257     // remove any repeated spaces, trim lines to PARAGRAPH_WIDTH width and
5258     // keep the same indentation level as first line in paragraph.
5259     QString currentWord;
5260
5261     for (int i = 0; i < selectedText.length(); ++i) {
5262         QChar ch = selectedText.at(i);
5263         if (ch.isSpace()) {
5264             if (!currentWord.isEmpty()) {
5265                 currentLength += currentWord.length() + 1;
5266
5267                 if (currentLength > paragraphWidth) {
5268                     currentLength = currentWord.length() + 1 + indentLevel;
5269                     result.chop(1); // remove trailing space
5270                     result.append(QChar::ParagraphSeparator);
5271                     result.append(spacing);
5272                 }
5273
5274                 result.append(currentWord);
5275                 result.append(QLatin1Char(' '));
5276                 currentWord.clear();
5277             }
5278
5279             continue;
5280         }
5281
5282         currentWord.append(ch);
5283     }
5284     result.chop(1);
5285     result.append(QChar::ParagraphSeparator);
5286
5287     cursor.insertText(result);
5288     cursor.endEditBlock();
5289 }
5290
5291 void BaseTextEditorWidget::unCommentSelection()
5292 {
5293 }
5294
5295 void BaseTextEditorWidget::showEvent(QShowEvent* e)
5296 {
5297     if (!d->m_fontSettings.isEmpty()) {
5298         setFontSettings(d->m_fontSettings);
5299         d->m_fontSettings.clear();
5300     }
5301     QPlainTextEdit::showEvent(e);
5302 }
5303
5304
5305 void BaseTextEditorWidget::setFontSettingsIfVisible(const TextEditor::FontSettings &fs)
5306 {
5307     if (!isVisible()) {
5308         d->m_fontSettings = fs;
5309         return;
5310     }
5311     setFontSettings(fs);
5312 }
5313
5314 void BaseTextEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
5315 {
5316     const QTextCharFormat textFormat = fs.toTextCharFormat(QLatin1String(Constants::C_TEXT));
5317     const QTextCharFormat selectionFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SELECTION));
5318     const QTextCharFormat lineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_LINE_NUMBER));
5319     const QTextCharFormat searchResultFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_RESULT));
5320     d->m_searchScopeFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_SCOPE));
5321     const QTextCharFormat parenthesesFormat = fs.toTextCharFormat(QLatin1String(Constants::C_PARENTHESES));
5322     d->m_currentLineFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE));
5323     d->m_currentLineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE_NUMBER));
5324     d->m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
5325     d->m_ifdefedOutFormat = fs.toTextCharFormat(QLatin1String(Constants::C_DISABLED_CODE));
5326     QFont font(textFormat.font());
5327
5328     const QColor foreground = textFormat.foreground().color();
5329     const QColor background = textFormat.background().color();
5330     QPalette p = palette();
5331     p.setColor(QPalette::Text, foreground);
5332     p.setColor(QPalette::Foreground, foreground);
5333     p.setColor(QPalette::Base, background);
5334     p.setColor(QPalette::Highlight, (selectionFormat.background().style() != Qt::NoBrush) ?
5335                selectionFormat.background().color() :
5336                QApplication::palette().color(QPalette::Highlight));
5337
5338     p.setBrush(QPalette::HighlightedText, selectionFormat.foreground());
5339
5340     p.setBrush(QPalette::Inactive, QPalette::Highlight, p.highlight());
5341     p.setBrush(QPalette::Inactive, QPalette::HighlightedText, p.highlightedText());
5342     setPalette(p);
5343     setFont(font);
5344     setTabSettings(d->m_document->tabSettings()); // update tabs, they depend on the font
5345
5346     // Line numbers
5347     QPalette ep = d->m_extraArea->palette();
5348     ep.setColor(QPalette::Dark, lineNumberFormat.foreground().color());
5349     ep.setColor(QPalette::Background, lineNumberFormat.background().style() != Qt::NoBrush ?
5350                 lineNumberFormat.background().color() : background);
5351     d->m_extraArea->setPalette(ep);
5352
5353     // Search results
5354     d->m_searchResultFormat.setBackground(searchResultFormat.background());
5355
5356     // Matching braces
5357     d->m_matchFormat.setForeground(parenthesesFormat.foreground());
5358     d->m_rangeFormat.setBackground(parenthesesFormat.background());
5359
5360
5361     // snippests
5362     d->m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
5363     d->m_occurrencesFormat.clearForeground();
5364     d->m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
5365     d->m_occurrenceRenameFormat.clearForeground();
5366
5367     slotUpdateExtraAreaWidth();   // Adjust to new font width
5368     updateCurrentLineHighlight(); // Make sure it takes the new color
5369 }
5370
5371 void BaseTextEditorWidget::setTabSettings(const TabSettings &ts)
5372 {
5373     d->m_document->setTabSettings(ts);
5374     int charWidth = QFontMetrics(font()).width(QChar(' '));
5375     setTabStopWidth(charWidth * ts.m_tabSize);
5376 }
5377
5378 void BaseTextEditorWidget::setDisplaySettings(const DisplaySettings &ds)
5379 {
5380     setLineWrapMode(ds.m_textWrapping ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
5381     setLineNumbersVisible(ds.m_displayLineNumbers);
5382     setVisibleWrapColumn(ds.m_showWrapColumn ? ds.m_wrapColumn : 0);
5383     setHighlightCurrentLine(ds.m_highlightCurrentLine);
5384     setRevisionsVisible(ds.m_markTextChanges);
5385     setCenterOnScroll(ds.m_centerCursorOnScroll);
5386
5387     if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
5388         if (SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
5389             highlighter->rehighlight();
5390         QTextOption option =  document()->defaultTextOption();
5391         if (ds.m_visualizeWhitespace)
5392             option.setFlags(option.flags() | QTextOption::ShowTabsAndSpaces);
5393         else
5394             option.setFlags(option.flags() & ~QTextOption::ShowTabsAndSpaces);
5395         option.setFlags(option.flags() | QTextOption::AddSpaceForLineAndParagraphSeparators);
5396         document()->setDefaultTextOption(option);
5397     }
5398
5399     d->m_displaySettings = ds;
5400     if (!ds.m_highlightBlocks) {
5401         d->extraAreaHighlightFoldedBlockNumber = -1;
5402         d->m_highlightBlocksInfo = BaseTextEditorPrivateHighlightBlocks();
5403     }
5404
5405     updateCodeFoldingVisible();
5406     updateHighlights();
5407     viewport()->update();
5408     extraArea()->update();
5409 }
5410
5411 void BaseTextEditorWidget::setBehaviorSettings(const TextEditor::BehaviorSettings &bs)
5412 {
5413     setMouseNavigationEnabled(bs.m_mouseNavigation);
5414     setScrollWheelZoomingEnabled(bs.m_scrollWheelZooming);
5415 }
5416
5417 void BaseTextEditorWidget::setStorageSettings(const StorageSettings &storageSettings)
5418 {
5419     d->m_document->setStorageSettings(storageSettings);
5420 }
5421
5422 void BaseTextEditorWidget::setCompletionSettings(const TextEditor::CompletionSettings &completionSettings)
5423 {
5424     d->m_autoCompleter->setAutoParenthesesEnabled(completionSettings.m_autoInsertBrackets);
5425     d->m_autoCompleter->setSurroundWithEnabled(completionSettings.m_autoInsertBrackets);
5426 }
5427
5428 void BaseTextEditorWidget::setExtraEncodingSettings(const ExtraEncodingSettings &extraEncodingSettings)
5429 {
5430     d->m_document->setExtraEncodingSettings(extraEncodingSettings);
5431 }
5432
5433 void BaseTextEditorWidget::fold()
5434 {
5435     QTextDocument *doc = document();
5436     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
5437     QTC_ASSERT(documentLayout, return);
5438     QTextBlock block = textCursor().block();
5439     if (!(BaseTextDocumentLayout::canFold(block) && block.next().isVisible())) {
5440         // find the closest previous block which can fold
5441         int indent = BaseTextDocumentLayout::foldingIndent(block);
5442         while (block.isValid() && (BaseTextDocumentLayout::foldingIndent(block) >= indent || !block.isVisible()))
5443             block = block.previous();
5444     }
5445     if (block.isValid()) {
5446         BaseTextDocumentLayout::doFoldOrUnfold(block, false);
5447         d->moveCursorVisible();
5448         documentLayout->requestUpdate();
5449         documentLayout->emitDocumentSizeChanged();
5450     }
5451 }
5452
5453 void BaseTextEditorWidget::unfold()
5454 {
5455     QTextDocument *doc = document();
5456     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
5457     QTC_ASSERT(documentLayout, return);
5458     QTextBlock block = textCursor().block();
5459     while (block.isValid() && !block.isVisible())
5460         block = block.previous();
5461     BaseTextDocumentLayout::doFoldOrUnfold(block, true);
5462     d->moveCursorVisible();
5463     documentLayout->requestUpdate();
5464     documentLayout->emitDocumentSizeChanged();
5465 }
5466
5467 void BaseTextEditorWidget::unfoldAll()
5468 {
5469     QTextDocument *doc = document();
5470     BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
5471     QTC_ASSERT(documentLayout, return);
5472
5473     QTextBlock block = doc->firstBlock();
5474     bool makeVisible = true;
5475     while (block.isValid()) {
5476         if (block.isVisible() && BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) {
5477             makeVisible = false;
5478             break;
5479         }
5480         block = block.next();
5481     }
5482
5483     block = doc->firstBlock();
5484
5485     while (block.isValid()) {
5486         if (BaseTextDocumentLayout::canFold(block))
5487             BaseTextDocumentLayout::doFoldOrUnfold(block, makeVisible);
5488         block = block.next();
5489     }
5490
5491     d->moveCursorVisible();
5492     documentLayout->requestUpdate();
5493     documentLayout->emitDocumentSizeChanged();
5494     centerCursor();
5495 }
5496
5497 void BaseTextEditorWidget::setTextCodec(QTextCodec *codec)
5498 {
5499     baseTextDocument()->setCodec(codec);
5500 }
5501
5502 QTextCodec *BaseTextEditorWidget::textCodec() const
5503 {
5504     return baseTextDocument()->codec();
5505 }
5506
5507 void BaseTextEditorWidget::setReadOnly(bool b)
5508 {
5509     QPlainTextEdit::setReadOnly(b);
5510     if (b)
5511         setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard);
5512 }
5513
5514 void BaseTextEditorWidget::cut()
5515 {
5516     if (d->m_inBlockSelectionMode) {
5517         copy();
5518         d->removeBlockSelection();
5519         return;
5520     }
5521     QPlainTextEdit::cut();
5522 }
5523
5524 void BaseTextEditorWidget::paste()
5525 {
5526     if (d->m_inBlockSelectionMode) {
5527         d->removeBlockSelection();
5528     }
5529     QPlainTextEdit::paste();
5530 }
5531
5532 QMimeData *BaseTextEditorWidget::createMimeDataFromSelection() const
5533 {
5534     if (d->m_inBlockSelectionMode) {
5535         QMimeData *mimeData = new QMimeData;
5536         QString text = d->copyBlockSelection();
5537         mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"), text.toUtf8());
5538         mimeData->setText(text); // for exchangeability
5539         return mimeData;
5540     } else if (textCursor().hasSelection()) {
5541         QTextCursor cursor = textCursor();
5542         QMimeData *mimeData = new QMimeData;
5543
5544         // Copy the selected text as plain text
5545         QString text = cursor.selectedText();
5546         convertToPlainText(text);
5547         mimeData->setText(text);
5548
5549         // Copy the selected text as HTML
5550         {
5551             // Create a new document from the selected text document fragment
5552             QTextDocument *tempDocument = new QTextDocument;
5553             QTextCursor tempCursor(tempDocument);
5554             tempCursor.insertFragment(cursor.selection());
5555
5556             // Apply the additional formats set by the syntax highlighter
5557             QTextBlock start = document()->findBlock(cursor.selectionStart());
5558             QTextBlock end = document()->findBlock(cursor.selectionEnd());
5559             end = end.next();
5560
5561             const int selectionStart = cursor.selectionStart();
5562             const int endOfDocument = tempDocument->characterCount() - 1;
5563             for (QTextBlock current = start; current.isValid() && current != end; current = current.next()) {
5564                 const QTextLayout *layout = current.layout();
5565                 foreach (const QTextLayout::FormatRange &range, layout->additionalFormats()) {
5566                     const int start = current.position() + range.start - selectionStart;
5567                     const int end = start + range.length;
5568                     if (end <= 0 || start >= endOfDocument)
5569                         continue;
5570                     tempCursor.setPosition(qMax(start, 0));
5571                     tempCursor.setPosition(qMin(end, endOfDocument), QTextCursor::KeepAnchor);
5572                     tempCursor.setCharFormat(range.format);
5573                 }
5574             }
5575
5576             // Reset the user states since they are not interesting
5577             for (QTextBlock block = tempDocument->begin(); block.isValid(); block = block.next())
5578                 block.setUserState(-1);
5579
5580             // Make sure the text appears pre-formatted
5581             tempCursor.setPosition(0);
5582             tempCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
5583             QTextBlockFormat blockFormat = tempCursor.blockFormat();
5584             blockFormat.setNonBreakableLines(true);
5585             tempCursor.setBlockFormat(blockFormat);
5586
5587             mimeData->setHtml(tempCursor.selection().toHtml());
5588             delete tempDocument;
5589         }
5590
5591         /*
5592           Try to figure out whether we are copying an entire block, and store the complete block
5593           including indentation in the qtcreator.blocktext mimetype.
5594         */
5595         QTextCursor selstart = cursor;
5596         selstart.setPosition(cursor.selectionStart());
5597         QTextCursor selend = cursor;
5598         selend.setPosition(cursor.selectionEnd());
5599         const TabSettings &ts = d->m_document->tabSettings();
5600
5601         bool startOk = ts.cursorIsAtBeginningOfLine(selstart);
5602         bool multipleBlocks = (selend.block() != selstart.block());
5603
5604         if (startOk && multipleBlocks) {
5605             selstart.movePosition(QTextCursor::StartOfBlock);
5606             if (ts.cursorIsAtBeginningOfLine(selend))
5607                 selend.movePosition(QTextCursor::StartOfBlock);
5608             cursor.setPosition(selstart.position());
5609             cursor.setPosition(selend.position(), QTextCursor::KeepAnchor);
5610             text = cursor.selectedText();
5611             mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.blocktext"), text.toUtf8());
5612         }
5613         return mimeData;
5614     }
5615     return 0;
5616 }
5617
5618 bool BaseTextEditorWidget::canInsertFromMimeData(const QMimeData *source) const
5619 {
5620     return QPlainTextEdit::canInsertFromMimeData(source);
5621 }
5622
5623 void BaseTextEditorWidget::insertFromMimeData(const QMimeData *source)
5624 {
5625     if (isReadOnly())
5626         return;
5627
5628     if (source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"))) {
5629         QString text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.vblocktext")));
5630         if (text.isEmpty())
5631             return;
5632
5633         if (CompletionSupport::instance()->isActive())
5634             setFocus();
5635
5636         QStringList lines = text.split(QLatin1Char('\n'));
5637         QTextCursor cursor = textCursor();
5638         cursor.beginEditBlock();
5639         const TabSettings &ts = d->m_document->tabSettings();
5640         int initialCursorPosition = cursor.position();
5641         int column = ts.columnAt(cursor.block().text(), cursor.positionInBlock());
5642         cursor.insertText(lines.first());
5643         for (int i = 1; i < lines.count(); ++i) {
5644             QTextBlock next = cursor.block().next();
5645             if (next.isValid()) {
5646                 cursor.setPosition(next.position());
5647             } else {
5648                 cursor.movePosition(QTextCursor::EndOfBlock);
5649                 cursor.insertBlock();
5650             }
5651             int offset = 0;
5652             int position = ts.positionAtColumn(cursor.block().text(), column, &offset);
5653             cursor.setPosition(cursor.block().position() + position);
5654             if (offset < 0) {
5655                 cursor.deleteChar();
5656                 cursor.insertText(QString(-offset, QLatin1Char(' ')));
5657             } else {
5658                 cursor.insertText(QString(offset, QLatin1Char(' ')));
5659             }
5660             cursor.insertText(lines.at(i));
5661         }
5662         cursor.setPosition(initialCursorPosition);
5663         cursor.endEditBlock();
5664         setTextCursor(cursor);
5665         ensureCursorVisible();
5666
5667         if (d->m_snippetOverlay->isVisible() && lines.count() > 1) {
5668             d->m_snippetOverlay->hide();
5669             d->m_snippetOverlay->clear();
5670         }
5671
5672         return;
5673     }
5674
5675     QString text = source->text();
5676     if (text.isEmpty())
5677         return;
5678
5679     if (CompletionSupport::instance()->isActive())
5680         setFocus();
5681
5682     if (d->m_snippetOverlay->isVisible() && (text.contains(QLatin1Char('\n'))
5683                                              || text.contains(QLatin1Char('\t')))) {
5684         d->m_snippetOverlay->hide();
5685         d->m_snippetOverlay->clear();
5686     }
5687
5688     const TabSettings &ts = d->m_document->tabSettings();
5689     QTextCursor cursor = textCursor();
5690     if (!ts.m_autoIndent) {
5691         cursor.beginEditBlock();
5692         cursor.insertText(text);
5693         cursor.endEditBlock();
5694         setTextCursor(cursor);
5695         return;
5696     }
5697
5698     cursor.beginEditBlock();
5699     cursor.removeSelectedText();
5700
5701     bool insertAtBeginningOfLine = ts.cursorIsAtBeginningOfLine(cursor);
5702
5703     if (insertAtBeginningOfLine
5704         && source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))) {
5705         text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.blocktext")));
5706         if (text.isEmpty())
5707             return;
5708     }
5709
5710     int reindentBlockStart = cursor.blockNumber() + (insertAtBeginningOfLine?0:1);
5711
5712     bool hasFinalNewline = (text.endsWith(QLatin1Char('\n'))
5713                             || text.endsWith(QChar::ParagraphSeparator)
5714                             || text.endsWith(QLatin1Char('\r')));
5715
5716     if (insertAtBeginningOfLine
5717         && hasFinalNewline) // since we'll add a final newline, preserve current line's indentation
5718         cursor.setPosition(cursor.block().position());
5719
5720     int cursorPosition = cursor.position();
5721     cursor.insertText(text);
5722
5723     int reindentBlockEnd = cursor.blockNumber() - (hasFinalNewline?1:0);
5724
5725     if (reindentBlockStart < reindentBlockEnd
5726         || (reindentBlockStart == reindentBlockEnd
5727             && (!insertAtBeginningOfLine || hasFinalNewline))) {
5728         if (insertAtBeginningOfLine && !hasFinalNewline) {
5729             QTextCursor unnecessaryWhitespace = cursor;
5730             unnecessaryWhitespace.setPosition(cursorPosition);
5731             unnecessaryWhitespace.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
5732             unnecessaryWhitespace.removeSelectedText();
5733         }
5734         QTextCursor c = cursor;
5735         c.setPosition(cursor.document()->findBlockByNumber(reindentBlockStart).position());
5736         c.setPosition(cursor.document()->findBlockByNumber(reindentBlockEnd).position(),
5737                       QTextCursor::KeepAnchor);
5738         reindent(document(), c);
5739     }
5740
5741     cursor.endEditBlock();
5742     setTextCursor(cursor);
5743 }
5744
5745 void BaseTextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
5746 {
5747     menu->addSeparator();
5748     Core::ActionManager *am = Core::ICore::instance()->actionManager();
5749
5750     QAction *a = am->command(Core::Constants::CUT)->action();
5751     if (a && a->isEnabled())
5752         menu->addAction(a);
5753     a = am->command(Core::Constants::COPY)->action();
5754     if (a && a->isEnabled())
5755         menu->addAction(a);
5756     a = am->command(Core::Constants::PASTE)->action();
5757     if (a && a->isEnabled())
5758         menu->addAction(a);
5759 }
5760
5761
5762 BaseTextEditor::BaseTextEditor(BaseTextEditorWidget *editor)
5763   : e(editor)
5764 {
5765     using namespace Find;
5766     Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
5767     BaseTextFind *baseTextFind = new BaseTextFind(editor);
5768     connect(baseTextFind, SIGNAL(highlightAll(QString,Find::FindFlags)),
5769             editor, SLOT(highlightSearchResults(QString,Find::FindFlags)));
5770     connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor,QTextCursor,int,int)),
5771             editor, SLOT(setFindScope(QTextCursor,QTextCursor,int,int)));
5772     aggregate->add(baseTextFind);
5773     aggregate->add(editor);
5774
5775     m_cursorPositionLabel = new Utils::LineColumnLabel;
5776
5777     m_stretchWidget = new QWidget;
5778     m_stretchWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
5779
5780     m_toolBar = new QToolBar;
5781     m_toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
5782     m_toolBar->addWidget(m_stretchWidget);
5783     m_cursorPositionLabelAction = m_toolBar->addWidget(m_cursorPositionLabel);
5784
5785     connect(editor, SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
5786 }
5787
5788 BaseTextEditor::~BaseTextEditor()
5789 {
5790     delete m_toolBar;
5791     delete e;
5792 }
5793
5794 QWidget *BaseTextEditor::toolBar()
5795 {
5796     return m_toolBar;
5797 }
5798
5799 void BaseTextEditor::insertExtraToolBarWidget(BaseTextEditor::Side side,
5800                                               QWidget *widget)
5801 {
5802     if (widget->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag) {
5803         if (m_stretchWidget)
5804             m_stretchWidget->deleteLater();
5805         m_stretchWidget = 0;
5806     }
5807
5808     if (side == Right)
5809         m_toolBar->insertWidget(m_cursorPositionLabelAction, widget);
5810     else
5811         m_toolBar->insertWidget(m_toolBar->actions().first(), widget);
5812 }
5813
5814 int BaseTextEditor::find(const QString &) const
5815 {
5816     return 0;
5817 }
5818
5819 int BaseTextEditor::currentLine() const
5820 {
5821     return e->textCursor().blockNumber() + 1;
5822 }
5823
5824 int BaseTextEditor::currentColumn() const
5825 {
5826     QTextCursor cursor = e->textCursor();
5827     return cursor.position() - cursor.block().position() + 1;
5828 }
5829
5830 int BaseTextEditor::columnCount() const
5831 {
5832     return e->columnCount();
5833 }
5834
5835 int BaseTextEditor::rowCount() const
5836 {
5837     return e->rowCount();
5838 }
5839
5840 QRect BaseTextEditor::cursorRect(int pos) const
5841 {
5842     QTextCursor tc = e->textCursor();
5843     if (pos >= 0)
5844         tc.setPosition(pos);
5845     QRect result = e->cursorRect(tc);
5846     result.moveTo(e->viewport()->mapToGlobal(result.topLeft()));
5847     return result;
5848 }
5849
5850 QString BaseTextEditor::contents() const
5851 {
5852     return e->toPlainText();
5853 }
5854
5855 QString BaseTextEditor::selectedText() const
5856 {
5857     if (e->textCursor().hasSelection())
5858         return e->textCursor().selectedText();
5859     return QString();
5860 }
5861
5862 QString BaseTextEditor::textAt(int pos, int length) const
5863 {
5864     QTextCursor c = e->textCursor();
5865
5866     if (pos < 0)
5867         pos = 0;
5868     c.movePosition(QTextCursor::End);
5869     if (pos + length > c.position())
5870         length = c.position() - pos;
5871
5872     c.setPosition(pos);
5873     c.setPosition(pos + length, QTextCursor::KeepAnchor);
5874
5875     return c.selectedText();
5876 }
5877
5878 void BaseTextEditor::remove(int length)
5879 {
5880     QTextCursor tc = e->textCursor();
5881     tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
5882     tc.removeSelectedText();
5883 }
5884
5885 void BaseTextEditor::insert(const QString &string)
5886 {
5887     QTextCursor tc = e->textCursor();
5888     tc.insertText(string);
5889 }
5890
5891 void BaseTextEditor::replace(int length, const QString &string)
5892 {
5893     QTextCursor tc = e->textCursor();
5894     tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
5895     tc.insertText(string);
5896 }
5897
5898 void BaseTextEditor::setCursorPosition(int pos)
5899 {
5900     QTextCursor tc = e->textCursor();
5901     tc.setPosition(pos);
5902     e->setTextCursor(tc);
5903 }
5904
5905 void BaseTextEditor::select(int toPos)
5906 {
5907     QTextCursor tc = e->textCursor();
5908     tc.setPosition(toPos, QTextCursor::KeepAnchor);
5909     e->setTextCursor(tc);
5910 }
5911
5912 void BaseTextEditor::updateCursorPosition()
5913 {
5914     const QTextCursor cursor = e->textCursor();
5915     const QTextBlock block = cursor.block();
5916     const int line = block.blockNumber() + 1;
5917     const int column = cursor.position() - block.position();
5918     m_cursorPositionLabel->setText(tr("Line: %1, Col: %2").arg(line).arg(e->tabSettings().columnAt(block.text(), column)+1),
5919                                    tr("Line: 9999, Col: 999"));
5920     m_contextHelpId.clear();
5921
5922     if (!block.isVisible())
5923         e->ensureCursorVisible();
5924
5925 }
5926
5927 QString BaseTextEditor::contextHelpId() const
5928 {
5929     if (m_contextHelpId.isEmpty())
5930         emit const_cast<BaseTextEditor*>(this)->contextHelpIdRequested(e->editor(),
5931                                                                                e->textCursor().position());
5932     return m_contextHelpId;
5933 }
5934
5935
5936 void BaseTextEditorWidget::setRefactorMarkers(const Internal::RefactorMarkers &markers)
5937 {
5938     foreach (const RefactorMarker &marker, d->m_refactorOverlay->markers())
5939         requestBlockUpdate(marker.cursor.block());
5940     d->m_refactorOverlay->setMarkers(markers);
5941     foreach (const RefactorMarker &marker, markers)
5942         requestBlockUpdate(marker.cursor.block());
5943 }
5944
5945 void BaseTextEditorWidget::doFoo() {
5946 #ifdef DO_FOO
5947     qDebug() << Q_FUNC_INFO;
5948     RefactorMarkers markers = d->m_refactorOverlay->markers();
5949     RefactorMarker marker;
5950     marker.tooltip = "Hello World";
5951     marker.cursor = textCursor();
5952     markers += marker;
5953     setRefactorMarkers(markers);
5954 #endif
5955 }
5956
5957 void Internal::BaseTextBlockSelection::moveAnchor(int blockNumber, int visualColumn)
5958 {
5959     if (visualColumn >= 0) {
5960         if (anchor % 2) {
5961             lastVisualColumn = visualColumn;
5962             if (lastVisualColumn < firstVisualColumn) {
5963                 qSwap(firstVisualColumn, lastVisualColumn);
5964                 anchor = (Anchor) (anchor - 1);
5965             }
5966         } else {
5967             firstVisualColumn = visualColumn;
5968             if (firstVisualColumn > lastVisualColumn) {
5969                 qSwap(firstVisualColumn, lastVisualColumn);
5970                 anchor = (Anchor) (anchor + 1);
5971             }
5972         }
5973     }
5974
5975     if (blockNumber >= 0 && blockNumber < firstBlock.document()->blockCount()) {
5976         if (anchor <= TopRight) {
5977             firstBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
5978             if (firstBlock.blockNumber() > lastBlock.blockNumber()) {
5979                 qSwap(firstBlock, lastBlock);
5980                 anchor = (Anchor) (anchor + 2);
5981             }
5982         } else {
5983             lastBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
5984             if (lastBlock.blockNumber() < firstBlock.blockNumber()) {
5985                 qSwap(firstBlock, lastBlock);
5986                 anchor = (Anchor) (anchor - 2);
5987             }
5988         }
5989     }
5990     firstBlock.movePosition(QTextCursor::StartOfBlock);
5991     lastBlock.movePosition(QTextCursor::EndOfBlock);
5992 }
5993
5994 QTextCursor Internal::BaseTextBlockSelection::selection(const TabSettings &ts) const
5995 {
5996     QTextCursor cursor = firstBlock;
5997     if (anchor <= TopRight) {
5998         cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn));
5999         cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn),
6000                            QTextCursor::KeepAnchor);
6001     } else {
6002         cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn));
6003         cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn),
6004                            QTextCursor::KeepAnchor);
6005     }
6006     return cursor;
6007 }
6008
6009 void Internal::BaseTextBlockSelection::fromSelection(const TabSettings &ts, const QTextCursor &selection)
6010 {
6011     firstBlock = selection;
6012     firstBlock.setPosition(selection.selectionStart());
6013     firstVisualColumn = ts.columnAt(firstBlock.block().text(), firstBlock.positionInBlock());
6014     lastBlock = selection;
6015     lastBlock.setPosition(selection.selectionEnd());
6016     lastVisualColumn = ts.columnAt(lastBlock.block().text(), lastBlock.positionInBlock());
6017     if (selection.anchor() > selection.position())
6018         anchor = TopLeft;
6019     else
6020         anchor = BottomRight;
6021
6022     firstBlock.movePosition(QTextCursor::StartOfBlock);
6023     lastBlock.movePosition(QTextCursor::EndOfBlock);
6024 }
6025 bool BaseTextEditorWidget::inFindScope(const QTextCursor &cursor)
6026 {
6027     if (cursor.isNull())
6028         return false;
6029     return inFindScope(cursor.selectionStart(), cursor.selectionEnd());
6030 }
6031
6032 bool BaseTextEditorWidget::inFindScope(int selectionStart, int selectionEnd)
6033 {
6034     if (d->m_findScopeStart.isNull())
6035         return true; // no scope, everything is included
6036     if (selectionStart < d->m_findScopeStart.position())
6037         return false;
6038     if (selectionEnd > d->m_findScopeEnd.position())
6039         return false;
6040     if (d->m_findScopeVerticalBlockSelectionFirstColumn < 0)
6041         return true;
6042     QTextBlock block = document()->findBlock(selectionStart);
6043     if (block != document()->findBlock(selectionEnd))
6044         return false;
6045     QString text = block.text();
6046     const TabSettings &ts = tabSettings();
6047     int startPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionFirstColumn);
6048     int endPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionLastColumn);
6049     if (selectionStart - block.position() < startPosition)
6050         return false;
6051     if (selectionEnd - block.position() > endPosition)
6052         return false;
6053     return true;
6054 }
6055
6056 void BaseTextEditorWidget::setBlockSelection(bool on)
6057 {
6058     if (d->m_inBlockSelectionMode != on) {
6059         d->m_inBlockSelectionMode = on;
6060         if (on)
6061             d->m_blockSelection.fromSelection(tabSettings(), textCursor());
6062     }
6063     viewport()->update();
6064 }
6065
6066 bool BaseTextEditorWidget::hasBlockSelection() const
6067 {
6068     return d->m_inBlockSelectionMode;
6069 }
6070
6071 void BaseTextEditorWidget::handleBlockSelection(int diff_row, int diff_col)
6072 {
6073
6074     if (!d->m_inBlockSelectionMode) {
6075         d->m_blockSelection.fromSelection(tabSettings(), textCursor());
6076         d->m_inBlockSelectionMode = true;
6077     }
6078
6079     d->m_blockSelection.moveAnchor(d->m_blockSelection.anchorBlockNumber() + diff_row,
6080                                    d->m_blockSelection.anchorColumnNumber() + diff_col);
6081     setTextCursor(d->m_blockSelection.selection(tabSettings()));
6082
6083     viewport()->update();
6084
6085 // ### TODO ensure horizontal visibility
6086 //    const bool rtl = q->isRightToLeft();
6087 //    if (cr.left() < visible.left() || cr.right() > visible.right()) {
6088 //        int x = cr.center().x() + horizontalOffset() - visible.width()/2;
6089 //        hbar->setValue(rtl ? hbar->maximum() - x : x);
6090 //    }
6091
6092 }
6093
6094 int BaseTextEditorWidget::columnCount() const
6095 {
6096     QFontMetricsF fm(font());
6097     return viewport()->rect().width() / fm.width(QLatin1Char('x'));
6098 }
6099
6100 int BaseTextEditorWidget::rowCount() const
6101 {
6102     QFontMetricsF fm(font());
6103     return viewport()->rect().height() / fm.lineSpacing();
6104 }
6105
6106 /**
6107   Helper method to transform a selected text. If nothing is selected at the moment
6108   the word under the cursor is used.
6109   The type of the transformation is determined by the method pointer given.
6110
6111   @param method     pointer to the QString method to use for the transformation
6112
6113   @see uppercaseSelection, lowercaseSelection
6114 */
6115 void BaseTextEditorWidget::transformSelection(Internal::TransformationMethod method)
6116 {
6117     QTextCursor cursor = textCursor();
6118
6119     int pos    = cursor.position();
6120     int anchor = cursor.anchor();
6121
6122     if (!cursor.hasSelection()) {
6123         // if nothing is selected, select the word over the cursor
6124         cursor.select(QTextCursor::WordUnderCursor);
6125     }
6126
6127     QString text = cursor.selectedText();
6128     QString transformedText = (text.*method)();
6129
6130     if (transformedText == text) {
6131         // if the transformation does not do anything to the selection, do no create an undo step
6132         return;
6133     }
6134
6135     cursor.insertText(transformedText);
6136
6137     // (re)select the changed text
6138     // Note: this assumes the transformation did not change the length,
6139     cursor.setPosition(anchor);
6140     cursor.setPosition(pos, QTextCursor::KeepAnchor);
6141     setTextCursor(cursor);
6142 }