cppeditor.cpp 67.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "cppeditor.h"
31

32
#include "cppautocompleter.h"
con's avatar
con committed
33
#include "cppeditorconstants.h"
34
#include "cppeditorplugin.h"
35
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
36
#include "cpphighlighter.h"
37
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
38
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
39

40
41
42
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
43
#include <cpptools/cpptoolseditorsupport.h>
Roberto Raggi's avatar
Roberto Raggi committed
44
#include <cpptools/cpptoolsplugin.h>
45
#include <cpptools/cpptoolsconstants.h>
46
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
47
#include <cpptools/cppcodeformatter.h>
48
#include <cpptools/cppcompletionassistprovider.h>
49
50
#include <cpptools/cpphighlightingsupport.h>
#include <cpptools/cpplocalsymbols.h>
51
#include <cpptools/cppqtstyleindenter.h>
52
#include <cpptools/cpptoolsreuse.h>
53
54
#include <cpptools/doxygengenerator.h>
#include <cpptools/cpptoolssettings.h>
55
#include <cpptools/symbolfinder.h>
56
57
58
59
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/session.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/nodesvisitor.h>
con's avatar
con committed
60
#include <texteditor/basetextdocument.h>
61
#include <texteditor/basetextdocumentlayout.h>
62
63
64
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
65
#include <texteditor/fontsettings.h>
66
#include <texteditor/refactoroverlay.h>
67
68
69
70
71
72
73

#include <utils/qtcassert.h>

#include <cplusplus/ASTPath.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/BackwardsScanner.h>
con's avatar
con committed
74

75
76
#include <QDebug>
#include <QTimer>
77
#include <QPointer>
78
79
80
81
82
83
84
85
#include <QSignalMapper>
#include <QAction>
#include <QHeaderView>
#include <QMenu>
#include <QTextEdit>
#include <QComboBox>
#include <QTreeView>
#include <QSortFilterProxyModel>
86
#include <QToolButton>
con's avatar
con committed
87

Roberto Raggi's avatar
Roberto Raggi committed
88
enum {
89
    UPDATE_OUTLINE_INTERVAL = 500,
90
91
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
92
93
};

Roberto Raggi's avatar
Roberto Raggi committed
94
using namespace CPlusPlus;
95
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
96
97
using namespace CppEditor::Internal;

con's avatar
con committed
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
namespace {

class OverviewTreeView : public QTreeView
{
public:
    OverviewTreeView(QWidget *parent = 0)
        : QTreeView(parent)
    {
        // TODO: Disable the root for all items (with a custom delegate?)
        setRootIsDecorated(false);
    }

    void sync()
    {
        expandAll();
113
114
115
116
    }

    void adjustWidth()
    {
hjk's avatar
hjk committed
117
        const int w = Core::ICore::mainWindow()->geometry().width();
118
119
120
121
122
123
124
125
        setMaximumWidth(w);
        setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
    }
};

class OverviewCombo : public QComboBox
{
public:
126
    OverviewCombo(QWidget *parent = 0) : QComboBox(parent), m_skipNextHide(false)
127
128
    {}

129
130
131
132
133
134
135
136
137
138
139
    bool eventFilter(QObject* object, QEvent* event)
    {
        if (event->type() == QEvent::MouseButtonPress && object == view()->viewport()) {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QModelIndex index = view()->indexAt(mouseEvent->pos());
            if (!view()->visualRect(index).contains(mouseEvent->pos()))
                m_skipNextHide = true;
        }
        return false;
    }

140
141
142
143
    void showPopup()
    {
        static_cast<OverviewTreeView *>(view())->adjustWidth();
        QComboBox::showPopup();
con's avatar
con committed
144
    }
145
146
147
148
149
150
151
152
153
154
155

    virtual void hidePopup()
    {
        if (m_skipNextHide)
            m_skipNextHide = false;
        else
            QComboBox::hidePopup();
    }

private:
    bool m_skipNextHide;
con's avatar
con committed
156
157
};

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
class OverviewProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
        QSortFilterProxyModel(parent),
        m_sourceModel(sourceModel)
    {
        setSourceModel(m_sourceModel);
    }

    bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
    {
        // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
        const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
        CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
        if (symbol && symbol->isGenerated())
            return false;

        return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
    }
private:
    CPlusPlus::OverviewModel *m_sourceModel;
};

183
184
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
185
    const Name *_declarationName;
186
187
188
189
190
191
192
193
    QList<Function *> *_functions;

public:
    FindFunctionDefinitions()
        : _declarationName(0),
          _functions(0)
    { }

Roberto Raggi's avatar
Roberto Raggi committed
194
    void operator()(const Name *declarationName, Scope *globals,
195
196
197
198
199
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
200
201
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
202
203
204
205
206
207
208
209
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
210
211
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
212
            name = q->name();
213
214
215
216
217
218
219
220

        if (_declarationName->isEqualTo(name))
            _functions->append(function);

        return false;
    }
};

221
struct CanonicalSymbol
222
{
223
    CPPEditorWidget *editor;
224
225
226
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

227
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
228
        : editor(editor), info(info)
229
230
    {
        typeOfExpression.init(info.doc, info.snapshot);
231
        typeOfExpression.setExpandTemplates(true);
232
233
234
235
236
237
238
    }

    const LookupContext &context() const
    {
        return typeOfExpression.context();
    }

239
    static inline bool isIdentifierChar(const QChar &ch)
240
241
242
243
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

244
245
246
247
248
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

249
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
250
251
                                        const QTextCursor &cursor,
                                        QString *code)
252
    {
253
        if (!info.doc)
254
255
256
257
258
259
260
261
262
263
            return 0;

        QTextCursor tc = cursor;
        int line, col;
        editor->convertPosition(tc.position(), &line, &col);
        ++col; // 1-based line and 1-based column

        QTextDocument *document = editor->document();

        int pos = tc.position();
264

265
266
        if (!isIdentifierChar(document->characterAt(pos)))
            if (!(pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
267
268
269
                return 0;

        while (isIdentifierChar(document->characterAt(pos)))
270
271
272
            ++pos;
        tc.setPosition(pos);

273
274
275
276
277
278
279
280
281
282
283
        ExpressionUnderCursor expressionUnderCursor;
        *code = expressionUnderCursor(tc);
        return info.doc->scopeAt(line, col);
    }

    Symbol *operator()(const QTextCursor &cursor)
    {
        QString code;

        if (Scope *scope = getScopeAndExpression(cursor, &code))
            return operator()(scope, code);
284

285
286
287
288
289
290
291
292
        return 0;
    }

    Symbol *operator()(Scope *scope, const QString &code)
    {
        return canonicalSymbol(scope, code, typeOfExpression);
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
293
294
    static Symbol *canonicalSymbol(Scope *scope, const QString &code,
                                   TypeOfExpression &typeOfExpression)
295
    {
296
297
        const QList<LookupItem> results =
                typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
298

Roberto Raggi's avatar
Roberto Raggi committed
299
300
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
301
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
302

303
            if (!(decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
304
305
                break;

306
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
307
308
309
310
311
312
                const Identifier *declId = decl->identifier();
                const Identifier *classId = classScope->identifier();

                if (classId && classId->isEqualTo(declId))
                    continue; // skip it, it's a ctor or a dtor.

313
                if (Function *funTy = r.declaration()->type()->asFunctionType()) {
314
315
316
317
                    if (funTy->isVirtual())
                        return r.declaration();
                }
            }
Roberto Raggi's avatar
Roberto Raggi committed
318
319
320
        }

        for (int i = 0; i < results.size(); ++i) {
321
322
323
324
325
326
327
328
            const LookupItem &r = results.at(i);

            if (r.declaration())
                return r.declaration();
        }

        return 0;
    }
329

330
331
};

332
333
334
/// Check if previous line is a CppStyle Doxygen Comment
bool isPreviousLineCppStyleComment(const QTextCursor &cursor)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
335
336
337
    const QTextBlock &currentBlock = cursor.block();
    if (!currentBlock.isValid())
        return false;
338

Orgad Shaneh's avatar
Orgad Shaneh committed
339
340
341
    const QTextBlock &actual = currentBlock.previous();
    if (!actual.isValid())
        return false;
342

Orgad Shaneh's avatar
Orgad Shaneh committed
343
344
345
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
346

Orgad Shaneh's avatar
Orgad Shaneh committed
347
    return false;
348
349
350
351
352
}

/// Check if next line is a CppStyle Doxygen Comment
bool isNextLineCppStyleComment(const QTextCursor &cursor)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
353
354
355
    const QTextBlock &currentBlock = cursor.block();
    if (!currentBlock.isValid())
        return false;
356

Orgad Shaneh's avatar
Orgad Shaneh committed
357
358
359
    const QTextBlock &actual = currentBlock.next();
    if (!actual.isValid())
        return false;
360

Orgad Shaneh's avatar
Orgad Shaneh committed
361
362
363
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
364

Orgad Shaneh's avatar
Orgad Shaneh committed
365
    return false;
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
}

/// Check if line is a CppStyle Doxygen comment and the cursor is after the comment
bool isCursorAfterCppComment(const QTextCursor &cursor, const QTextDocument *doc)
{
    QTextCursor cursorFirstNonBlank(cursor);
    cursorFirstNonBlank.movePosition(QTextCursor::StartOfLine);
    while (doc->characterAt(cursorFirstNonBlank.position()).isSpace()
           && cursorFirstNonBlank.movePosition(QTextCursor::NextCharacter)) {
    }

    const QTextBlock& block = cursorFirstNonBlank.block();
    const QString text = block.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return (cursor.position() >= cursorFirstNonBlank.position() + 3);

    return false;
}

bool isCppStyleContinuation(const QTextCursor& cursor)
{
    return (isPreviousLineCppStyleComment(cursor) || isNextLineCppStyleComment(cursor));
}

DoxygenGenerator::DocumentationStyle doxygenStyle(const QTextCursor &cursor,
                                                  const QTextDocument *doc)
{
    const int pos = cursor.position();

395
    QString comment = QString(doc->characterAt(pos - 3))
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
            + doc->characterAt(pos - 2)
            + doc->characterAt(pos - 1);

    if (comment == QLatin1String("/**"))
        return CppTools::DoxygenGenerator::JavaStyle;
    else if (comment == QLatin1String("/*!"))
        return CppTools::DoxygenGenerator::QtStyle;
    else if (comment == QLatin1String("///"))
        return CppTools::DoxygenGenerator::CppStyleA;
    else
        return CppTools::DoxygenGenerator::CppStyleB;
}

bool handleDoxygenCppStyleContinuation(QTextCursor &cursor,
                                       QKeyEvent *e)
{
    const int blockPos = cursor.positionInBlock();
    const QString &text = cursor.block().text();
    int offset = 0;
    for (; offset < blockPos; ++offset) {
        if (!text.at(offset).isSpace())
            break;
    }

    // If the line does not start with the comment we don't
    // consider it as a continuation. Handles situations like:
    // void d(); ///<enter>
    if (!(text.trimmed().startsWith(QLatin1String("///"))
Orgad Shaneh's avatar
Orgad Shaneh committed
424
425
426
          || text.startsWith(QLatin1String("//!")))) {
        return false;
    }
427
428
429
430
431
432

    QString newLine(QLatin1Char('\n'));
    newLine.append(QString(offset, QLatin1Char(' '))); // indent correctly

    const QString commentMarker = text.mid(offset, 3);
    newLine.append(commentMarker);
433
    newLine.append(QLatin1Char(' '));
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

    cursor.insertText(newLine);
    e->accept();
    return true;
}

bool handleDoxygenContinuation(QTextCursor &cursor,
                               QKeyEvent *e,
                               const QTextDocument *doc,
                               const bool enableDoxygen,
                               const bool leadingAsterisks)
{
    // It might be a continuation if:
    // a) current line starts with /// or //! and cursor is positioned after the comment
    // b) current line is in the middle of a multi-line Qt or Java style comment

    if (enableDoxygen && !cursor.atEnd() && isCursorAfterCppComment(cursor, doc))
        return handleDoxygenCppStyleContinuation(cursor, e);

    if (!leadingAsterisks)
        return false;

    // We continue the comment if the cursor is after a comment's line asterisk and if
    // there's no asterisk immediately after the cursor (that would already be considered
    // a leading asterisk).
    int offset = 0;
    const int blockPos = cursor.positionInBlock();
    const QString &text = cursor.block().text();
    for (; offset < blockPos; ++offset) {
        if (!text.at(offset).isSpace())
            break;
    }

    if (offset < blockPos
            && (text.at(offset) == QLatin1Char('*')
                || (offset < blockPos - 1
                    && text.at(offset) == QLatin1Char('/')
                    && text.at(offset + 1) == QLatin1Char('*')))) {
        int followinPos = blockPos;
        for (; followinPos < text.length(); ++followinPos) {
            if (!text.at(followinPos).isSpace())
                break;
        }
        if (followinPos == text.length()
                || text.at(followinPos) != QLatin1Char('*')) {
            QString newLine(QLatin1Char('\n'));
            QTextCursor c(cursor);
            c.movePosition(QTextCursor::StartOfBlock);
            c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset);
            newLine.append(c.selectedText());
            if (text.at(offset) == QLatin1Char('/')) {
485
                newLine.append(QLatin1String(" * "));
486
487
488
489
490
            } else {
                int start = offset;
                while (offset < blockPos && text.at(offset) == QLatin1Char('*'))
                    ++offset;
                newLine.append(QString(offset - start, QLatin1Char('*')));
491
                newLine.append(QLatin1Char(' '));
492
493
494
495
496
497
498
499
500
501
            }
            cursor.insertText(newLine);
            e->accept();
            return true;
        }
    }

    return false;
}

con's avatar
con committed
502
503
} // end of anonymous namespace

504
505
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
506
{
507
508
509
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
con's avatar
con committed
510
511
}

512
513
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

514
515
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
516
    , m_currentRenameSelection(NoCurrentRenameSelection)
517
    , m_inRename(false)
mae's avatar
mae committed
518
519
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
520
    , m_objcEnabled(false)
521
    , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
522
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(this))
523
    , m_preprocessorButton(0)
con's avatar
con committed
524
{
525
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
526

con's avatar
con committed
527
528
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
529
    setCodeFoldingSupported(true);
530
    setIndenter(new CppTools::CppQtStyleIndenter);
531
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
532

533
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
534

535
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
536
    if (m_modelManager) {
537
538
539
540
541
        CppEditorSupport *editorSupport = m_modelManager->cppEditorSupport(editor());
        connect(editorSupport, SIGNAL(documentUpdated()),
                this, SLOT(onDocumentUpdated()));
        connect(editorSupport, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
                this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Robert Loehning's avatar
Robert Loehning committed
542
543
        connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)),
                this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)));
con's avatar
con committed
544
    }
545

546
    m_highlightRevision = 0;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
547
548
549
550
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)),
            SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()),
            SLOT(finishHighlightSymbolUsages()));
551
552
553
554

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
555
556
557
558
559
560
561

    connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
            this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));

    m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this);
    connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
562
563
564
565
566

    connect(CppTools::CppToolsSettings::instance(),
            SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
            this,
            SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
con's avatar
con committed
567
568
}

569
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
570
{
571
    if (m_modelManager)
572
        m_modelManager->deleteCppEditorSupport(editor());
con's avatar
con committed
573
574
}

575
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
576
{
577
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
578
579
580
581
    createToolBar(editable);
    return editable;
}

582
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
583
{
584
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
585
    m_outlineCombo->setMinimumContentsLength(22);
586
587

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
588
    QSizePolicy policy = m_outlineCombo->sizePolicy();
589
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
590
    m_outlineCombo->setSizePolicy(policy);
591

Kai Koehne's avatar
Kai Koehne committed
592
593
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
594
    outlineView->setItemsExpandable(true);
Kai Koehne's avatar
Kai Koehne committed
595
    m_outlineCombo->setView(outlineView);
596
    m_outlineCombo->setMaxVisibleItems(40);
597
    outlineView->viewport()->installEventFilter(m_outlineCombo);
con's avatar
con committed
598

Kai Koehne's avatar
Kai Koehne committed
599
600
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
601
    if (CppEditorPlugin::instance()->sortedOutline())
602
603
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
604
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
605
606
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
607
    m_outlineCombo->setModel(m_proxyModel);
608

Kai Koehne's avatar
Kai Koehne committed
609
610
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
611
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
612
    m_sortAction->setChecked(sortedOutline());
613
614
    connect(m_sortAction, SIGNAL(toggled(bool)),
            CppEditorPlugin::instance(), SLOT(setSortedOutline(bool)));
Kai Koehne's avatar
Kai Koehne committed
615
    m_outlineCombo->addAction(m_sortAction);
con's avatar
con committed
616

617
618
619
620
621
    m_updateOutlineTimer = new QTimer(this);
    m_updateOutlineTimer->setSingleShot(true);
    m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));

Kai Koehne's avatar
Kai Koehne committed
622
623
624
625
    m_updateOutlineIndexTimer = new QTimer(this);
    m_updateOutlineIndexTimer->setSingleShot(true);
    m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
Roberto Raggi's avatar
Roberto Raggi committed
626

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
627
628
629
630
631
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

632
633
634
    m_updateFunctionDeclDefLinkTimer = new QTimer(this);
    m_updateFunctionDeclDefLinkTimer->setSingleShot(true);
    m_updateFunctionDeclDefLinkTimer->setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
635
636
    connect(m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
            this, SLOT(updateFunctionDeclDefLinkNow()));
637

Kai Koehne's avatar
Kai Koehne committed
638
639
640
    connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
    connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
con's avatar
con committed
641

642
643
    // set up slots to document changes
    updateContentsChangedSignal();
con's avatar
con committed
644

645
646
647
    // set up function declaration - definition link
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
Roberto Raggi's avatar
Roberto Raggi committed
648
649
650
651
652

    // set up the semantic highlighter
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));

653
654
655
656
657
658
659
    m_preprocessorButton = new QToolButton(this);
    m_preprocessorButton->setText(QLatin1String("#"));
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
    connect(m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_preprocessorButton);
660
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
661
662
}

663
void CPPEditorWidget::paste()
mae's avatar
mae committed
664
{
665
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
666
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
667
668
669
670
        return;
    }

    startRename();
671
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
672
673
674
    finishRename();
}

675
void CPPEditorWidget::cut()
mae's avatar
mae committed
676
{
677
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
678
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
679
680
681
682
        return;
    }

    startRename();
683
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
684
685
686
    finishRename();
}

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
void CPPEditorWidget::selectAll()
{
    // if we are currently renaming a symbol
    // and the cursor is over that symbol, select just that symbol
    if (m_currentRenameSelection != NoCurrentRenameSelection) {
        QTextCursor cursor = textCursor();
        int selectionBegin = m_currentRenameSelectionBegin.position();
        int selectionEnd = m_currentRenameSelectionEnd.position();

        if (cursor.position() >= selectionBegin
                && cursor.position() <= selectionEnd) {
            cursor.setPosition(selectionBegin);
            cursor.setPosition(selectionEnd, QTextCursor::KeepAnchor);
            setTextCursor(cursor);
            return;
        }
    }

    BaseTextEditorWidget::selectAll();
}

708
void CPPEditorWidget::setMimeType(const QString &mt)
709
{
710
    const QString &filePath = editor()->document()->filePath();
711
712
713
    const QString &projectFile = ProjectExplorer::SessionManager::value(
                QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX) + filePath).toString();
    const QByteArray &additionalDirectives = ProjectExplorer::SessionManager::value(
714
                projectFile + QLatin1Char(',') + filePath).toString().toUtf8();
715
716
717
718

    QSharedPointer<SnapshotUpdater> updater
            = m_modelManager->cppEditorSupport(editor())->snapshotUpdater();
    updater->setEditorDefines(additionalDirectives);
719

720
721
722
    m_preprocessorButton->setProperty("highlightWidget", !additionalDirectives.trimmed().isEmpty());
    m_preprocessorButton->update();

723
    BaseTextEditorWidget::setMimeType(mt);
724
725
    setObjCEnabled(mt == QLatin1String(CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE)
                   || mt == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
726
727
}

728
void CPPEditorWidget::setObjCEnabled(bool onoff)
729
730
731
732
{
    m_objcEnabled = onoff;
}

733
bool CPPEditorWidget::isObjCEnabled() const
734
735
{ return m_objcEnabled; }

736
void CPPEditorWidget::startRename()
mae's avatar
mae committed
737
738
739
740
{
    m_inRenameChanged = false;
}

741
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
{
    if (!m_inRenameChanged)
        return;

    m_inRename = true;

    QTextCursor cursor = textCursor();
    cursor.joinPreviousEditBlock();

    cursor.setPosition(m_currentRenameSelectionEnd.position());
    cursor.setPosition(m_currentRenameSelectionBegin.position(), QTextCursor::KeepAnchor);
    m_renameSelections[m_currentRenameSelection].cursor = cursor;
    QString text = cursor.selectedText();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        if (i == m_currentRenameSelection)
            continue;
        QTextEdit::ExtraSelection &s = m_renameSelections[i];
        int pos = s.cursor.selectionStart();
        s.cursor.removeSelectedText();
        s.cursor.insertText(text);
        s.cursor.setPosition(pos, QTextCursor::KeepAnchor);
    }

    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
    cursor.endEditBlock();

    m_inRename = false;
}

772
void CPPEditorWidget::abortRename()
773
{
774
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
775
        return;
776
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
777
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
778
779
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
780
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
781

782
    semanticRehighlight(/* force = */ true);
783
784
}

785
786
787
/// \brief Called by \c CppEditorSupport when the document corresponding to the
///        file in this editor is updated.
void CPPEditorWidget::onDocumentUpdated()
con's avatar
con committed
788
{
789
    m_updateOutlineTimer->start();
con's avatar
con committed
790
791
}

792
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
793
{
794
    if (!doc)
Christian Kamm's avatar
Christian Kamm committed
795
796
797
798
799
        return 0;

    int line, col;
    convertPosition(cursor.position(), &line, &col);

800
801
802
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
        const QByteArray name = identifierUnderCursor(&macroCursor).toLatin1();
803
        if (macro->name() == name)
804
805
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
806
        return &use->macro();
807
    }
Christian Kamm's avatar
Christian Kamm committed
808
809
810

    return 0;
}
811

812
void CPPEditorWidget::findUsages()
813
{
814
815
816
    if (!m_modelManager)
        return;

817
    SemanticInfo info = m_lastSemanticInfo;
818
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
819
    info.snapshot.insert(info.doc);
820

821
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
822
        m_modelManager->findMacroUsages(*macro);
823
824
825
826
827
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
            m_modelManager->findUsages(canonicalSymbol, cs.context());
828
    }
829
830
}

831

832
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
833
{
834
835
836
    if (!m_modelManager)
        return;

837
    SemanticInfo info = m_lastSemanticInfo;
838
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
839
840
    info.snapshot.insert(info.doc);

841
842
843
844
845
846
847
848
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        m_modelManager->renameMacroUsages(*macro, replacement);
    } else {
        CanonicalSymbol cs(this, info);
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
                m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
    }
849
850
}

851
void CPPEditorWidget::renameUsages()
852
{
853
854
855
    renameUsagesNow();
}

856
void CPPEditorWidget::markSymbolsNow()
857
{
858
859
860
861
862
863
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
864

865
866
867
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
868
869
870

    QList<QTextEdit::ExtraSelection> selections;

871
872
873
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
874

875
876
        if (column)
            --column;  // adjust the column position.
877

878
        const int len = unit->tokenAt(index).f.length;
879

880
881
882
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
883

884
885
886
887
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
888

Roberto Raggi's avatar
Roberto Raggi committed
889
    }
890
891

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
892
893
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
894
895
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc,
                                     Snapshot snapshot)
896
897
{
    TypeOfExpression typeOfExpression;
898
899
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
900
901
    // make possible to instantiate templates
    typeOfExpression.setExpandTemplates(true);
902
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
903
904
        return CppModelManagerInterface::instance()->references(canonicalSymbol,
                                                                typeOfExpression.context());
905
906
907
    return QList<int>();
}

908
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
909
910
911
{
    abortRename();

912
    if (!info.doc)
913
914
        return;

915
916
917
918
919
920
921
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        QList<QTextEdit::ExtraSelection> selections;

        //Macro definition
        if (macro->fileName() == info.doc->fileName()) {
            QTextCursor cursor(document());
            cursor.setPosition(macro->offset());
Nikolai Kosjar's avatar
Nikolai Kosjar committed
922
923
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
                                macro->name().length());
924
925
926
927
928
929
930
931

            QTextEdit::ExtraSelection sel;
            sel.format = m_occurrencesFormat;
            sel.cursor = cursor;
            selections.append(sel);
        }

        //Other macro uses
932
        foreach (const Document::MacroUse &use, info.doc->macroUses()) {
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
            if (use.macro().line() != macro->line()
                    || use.macro().offset() != macro->offset()
                    || use.macro().length() != macro->length()
                    || use.macro().fileName() != macro->fileName())
                continue;

            QTextCursor cursor(document());
            cursor.setPosition(use.begin());
            cursor.setPosition(use.end(), QTextCursor::KeepAnchor);

            QTextEdit::ExtraSelection sel;
            sel.format = m_occurrencesFormat;
            sel.cursor = cursor;
            selections.append(sel);
        }
948

949
950
951
952
953
954
955
956
        setExtraSelections(CodeSemanticsSelection, selections);
    } else {
        CanonicalSymbol cs(this, info);
        QString expression;
        if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
            m_references.cancel();
            m_referencesRevision = info.revision;
            m_referencesCursorPosition = position();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
957
958
            m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc,
                                             info.snapshot);
959
960
961
962
            m_referencesWatcher.setFuture(m_references);
        } else {
            const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

963
            if (!selections.isEmpty())
964
965
                setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
        }
966
967
968
    }
}

969
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
970
{
971
972
973
    if (!m_modelManager)
        return;

974
975
    CppEditorSupport *edSup = m_modelManager->cppEditorSupport(editor());
    updateSemanticInfo(edSup->recalculateSemanticInfo(/* emitSignalWhenFinished = */ false));
mae's avatar
mae committed
976
    abortRename();
977

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
978
979
980
981
982
    QTextCursor c = textCursor();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        QTextEdit::ExtraSelection s = m_renameSelections.at(i);
        if (c.position() >= s.cursor.anchor()
983
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
984
            m_currentRenameSelection = i;
mae's avatar
mae committed
985
986
987
988
            m_firstRenameChange = true;
            m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
                                                        m_renameSelections[i].cursor.selectionStart());
            m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
Nikolai Kosjar's avatar
Nikolai Kosjar committed
989
                                                      m_renameSelections[i].cursor.selectionEnd());
990
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
991
992
993
994
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
995
996

    if (m_renameSelections.isEmpty())
997
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
998
999
}

1000
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
1001
{
1002
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
1003
1004
        return;

mae's avatar
mae committed
1005
1006
1007
    if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
        // we are inserting at the beginning of the rename selection => expand
        m_currentRenameSelectionBegin.setPosition(position);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1008
1009
        m_renameSelections[m_currentRenameSelection].cursor.setPosition(position,
                                                                        QTextCursor::KeepAnchor);
mae's avatar
mae committed
1010
1011
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1012
1013
    // the condition looks odd, but keep in mind that the begin
    // and end cursors do move automatically
mae's avatar
mae committed
1014
1015
1016
1017
    m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
                         && position + charsAdded <= m_currentRenameSelectionEnd.position());

    if (!m_inRenameChanged)
1018
1019
1020
1021
1022
1023
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

1024
1025
1026
1027
1028
1029
1030
1031
void CPPEditorWidget::updatePreprocessorButtonTooltip()
{
    QTC_ASSERT(m_preprocessorButton, return);
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    QTC_ASSERT(cmd, return);
    m_preprocessorButton->setToolTip(cmd->action()->toolTip());
}

1032
1033
1034
1035
1036
1037
1038
void CPPEditorWidget::jumpToOutlineElement(int index)
{
    QModelIndex modelIndex = m_outlineCombo->view()->currentIndex();
    // When the user clicks on an item in the combo box,
    // the view's currentIndex is updated, so we want to use that.
    // When the scroll wheel was used on the combo box,
    // the view's currentIndex is not updated,
1039
    // but the passed index to this function is correct.
1040
1041
1042
1043
1044
1045
1046
1047
    // So, if the view has a current index, we reset it, to be able
    // to distinguish wheel events later
    if (modelIndex.isValid())
        m_outlineCombo->view()->setCurrentIndex(QModelIndex());
    else
        modelIndex = m_proxyModel->index(index, 0); // toplevel index
    QModelIndex sourceIndex = m_proxyModel->mapToSource(modelIndex);
    Symbol *symbol = m_outlineModel->symbolFromIndex(sourceIndex);
1048
    if (!symbol)
con's avatar
con committed
1049
1050
        return;

1051
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
1052
1053
}

1054
void CPPEditorWidget::setSortedOutline(bool sort)
1055
{
Kai Koehne's avatar
Kai Koehne committed
1056
    if (sort != sortedOutline()) {
1057
1058
1059
1060
1061
1062
1063
        if (sort)
            m_proxyModel->sort(0, Qt::AscendingOrder);
        else
            m_proxyModel->sort(-1, Qt::AscendingOrder);
        bool block = m_sortAction->blockSignals(true);
        m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
        m_sortAction->blockSignals(block);
Kai Koehne's avatar
Kai Koehne committed
1064
        updateOutlineIndexNow();
1065
1066
1067
    }
}

1068
bool CPPEditorWidget::sortedOutline() const
1069
1070
1071
1072
{
    return (m_proxyModel->sortColumn() == 0);
}

1073
void CPPEditorWidget::updateOutlineNow()
1074
{
1075
1076
1077
    if (!m_modelManager)
        return;

1078
    const Snapshot snapshot = m_modelManager->snapshot();
1079
    Document::Ptr document = snapshot.document(editorDocument()->filePath());
1080
1081
1082
1083
1084
1085
1086
1087
1088

    if (!document)
        return;

    if (document->editorRevision() != editorRevision()) {
        m_updateOutlineTimer->start();
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
1089
    m_outlineModel->rebuild(document);
1090

Kai Koehne's avatar
Kai Koehne committed
1091
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
1092
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
1093
    updateOutlineIndexNow();
1094
1095