OSDN Git Service

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