diffeditor.cpp 23 KB
Newer Older
1
2
/****************************************************************************
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
6
7
8
9
10
11
**
** This file is part of Qt Creator.
**
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
16
17
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
27
28
29
30
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

31
#include "diffeditor.h"
32
#include "diffeditorconstants.h"
33
#include "diffeditordocument.h"
34
#include "diffview.h"
35
36

#include <coreplugin/icore.h>
37
#include <coreplugin/coreconstants.h>
Jarek Kobus's avatar
Jarek Kobus committed
38
39
#include <coreplugin/minisplitter.h>

40
#include <texteditor/texteditor.h>
Jarek Kobus's avatar
Jarek Kobus committed
41
42
#include <texteditor/texteditorsettings.h>
#include <texteditor/displaysettings.h>
hjk's avatar
hjk committed
43
#include <texteditor/marginsettings.h>
44

45
#include <utils/algorithm.h>
46
#include <utils/fileutils.h>
47
#include <utils/qtcassert.h>
48

49
#include <QAction>
jkobus's avatar
jkobus committed
50
#include <QStackedWidget>
51
52
53
54
#include <QToolButton>
#include <QSpinBox>
#include <QStyle>
#include <QLabel>
55
#include <QHBoxLayout>
jkobus's avatar
jkobus committed
56
57
#include <QToolBar>
#include <QComboBox>
58
#include <QDir>
jkobus's avatar
jkobus committed
59
#include <QTextCodec>
60
#include <QTextBlock>
jkobus's avatar
jkobus committed
61
62

static const char settingsGroupC[] = "DiffEditor";
63
static const char descriptionVisibleKeyC[] = "DescriptionVisible";
Tobias Hunger's avatar
Tobias Hunger committed
64
static const char horizontalScrollBarSynchronizationKeyC[] = "HorizontalScrollBarSynchronization";
65
66
67
68
static const char contextLineCountKeyC[] = "ContextLineNumbers";
static const char ignoreWhitespaceKeyC[] = "IgnoreWhitespace";

static const char diffViewKeyC[] = "DiffEditorType";
jkobus's avatar
jkobus committed
69
70
71

static const char legacySettingsGroupC[] = "Git";
static const char useDiffEditorKeyC[] = "UseDiffEditor";
72

Jarek Kobus's avatar
Jarek Kobus committed
73
74
using namespace TextEditor;

Tobias Hunger's avatar
Tobias Hunger committed
75
76
77
78
79
80
81
82
83
84
85
86
87
namespace {

class Guard
{
public:
    Guard(int *state) : m_state(state) { ++(*state); }
    ~Guard() { --(*m_state); QTC_ASSERT(*m_state >= 0, return); }
private:
    int *m_state;
};

} // namespace

88
namespace DiffEditor {
Jarek Kobus's avatar
Jarek Kobus committed
89
90
namespace Internal {

91
class DescriptionEditorWidget : public TextEditorWidget
Jarek Kobus's avatar
Jarek Kobus committed
92
93
94
95
{
    Q_OBJECT
public:
    DescriptionEditorWidget(QWidget *parent = 0);
96
    virtual QSize sizeHint() const override;
Jarek Kobus's avatar
Jarek Kobus committed
97

98
signals:
99
    void requestBranchList();
100

Jarek Kobus's avatar
Jarek Kobus committed
101
protected:
102
103
    void mouseMoveEvent(QMouseEvent *e) override;
    void mouseReleaseEvent(QMouseEvent *e) override;
104

105
106
    void setDisplaySettings(const DisplaySettings &ds) override;
    void setMarginSettings(const MarginSettings &ms) override;
hjk's avatar
hjk committed
107

108
109
110
111
112
113
    bool findContentsUnderCursor(const QTextCursor &cursor);
    void highlightCurrentContents();
    void handleCurrentContents();

private:
    QTextCursor m_currentCursor;
Jarek Kobus's avatar
Jarek Kobus committed
114
115
116
};

DescriptionEditorWidget::DescriptionEditorWidget(QWidget *parent)
117
    : TextEditorWidget(parent)
Jarek Kobus's avatar
Jarek Kobus committed
118
{
119
120
    setupFallBackEditor("DiffEditor.DescriptionEditor");

Jarek Kobus's avatar
Jarek Kobus committed
121
122
123
124
125
126
127
    DisplaySettings settings = displaySettings();
    settings.m_textWrapping = false;
    settings.m_displayLineNumbers = false;
    settings.m_highlightCurrentLine = false;
    settings.m_displayFoldingMarkers = false;
    settings.m_markTextChanges = false;
    settings.m_highlightBlocks = false;
128
    TextEditorWidget::setDisplaySettings(settings);
Jarek Kobus's avatar
Jarek Kobus committed
129
130
131
132
133
134
135
136
137

    setCodeFoldingSupported(true);
    setFrameStyle(QFrame::NoFrame);

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
}

QSize DescriptionEditorWidget::sizeHint() const
{
138
    QSize size = TextEditorWidget::sizeHint();
Jarek Kobus's avatar
Jarek Kobus committed
139
140
141
142
143
144
145
146
    size.setHeight(size.height() / 5);
    return size;
}

void DescriptionEditorWidget::setDisplaySettings(const DisplaySettings &ds)
{
    DisplaySettings settings = displaySettings();
    settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
147
    TextEditorWidget::setDisplaySettings(settings);
Jarek Kobus's avatar
Jarek Kobus committed
148
149
}

hjk's avatar
hjk committed
150
151
152
void DescriptionEditorWidget::setMarginSettings(const MarginSettings &ms)
{
    Q_UNUSED(ms);
153
    TextEditorWidget::setMarginSettings(MarginSettings());
hjk's avatar
hjk committed
154
155
}

156
157
158
void DescriptionEditorWidget::mouseMoveEvent(QMouseEvent *e)
{
    if (e->buttons()) {
159
        TextEditorWidget::mouseMoveEvent(e);
160
161
162
163
164
165
166
167
168
169
170
171
172
173
        return;
    }

    Qt::CursorShape cursorShape;

    const QTextCursor cursor = cursorForPosition(e->pos());
    if (findContentsUnderCursor(cursor)) {
        highlightCurrentContents();
        cursorShape = Qt::PointingHandCursor;
    } else {
        setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
        cursorShape = Qt::IBeamCursor;
    }

174
    TextEditorWidget::mouseMoveEvent(e);
175
176
177
178
179
180
181
182
183
184
185
186
187
188
    viewport()->setCursor(cursorShape);
}

void DescriptionEditorWidget::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton && !(e->modifiers() & Qt::ShiftModifier)) {
        const QTextCursor cursor = cursorForPosition(e->pos());
        if (findContentsUnderCursor(cursor)) {
            handleCurrentContents();
            e->accept();
            return;
        }
    }

189
    TextEditorWidget::mouseReleaseEvent(e);
190
191
192
193
194
195
196
197
198
199
200
201
202
203
}

bool DescriptionEditorWidget::findContentsUnderCursor(const QTextCursor &cursor)
{
    m_currentCursor = cursor;
    return cursor.block().text() == QLatin1String(Constants::EXPAND_BRANCHES);
}

void DescriptionEditorWidget::highlightCurrentContents()
{
    QTextEdit::ExtraSelection sel;
    sel.cursor = m_currentCursor;
    sel.cursor.select(QTextCursor::LineUnderCursor);
    sel.format.setFontUnderline(true);
204
    setExtraSelections(TextEditorWidget::OtherSelection,
205
206
207
208
209
210
211
212
                       QList<QTextEdit::ExtraSelection>() << sel);
}

void DescriptionEditorWidget::handleCurrentContents()
{
    m_currentCursor.select(QTextCursor::LineUnderCursor);
    m_currentCursor.removeSelectedText();
    m_currentCursor.insertText(QLatin1String("Branches: Expanding..."));
213
    emit requestBranchList();
214
215
}

216
///////////////////////////////// DiffEditor //////////////////////////////////
217

218
219
DiffEditor::DiffEditor()
    : m_document(0)
220
    , m_descriptionWidget(0)
jkobus's avatar
jkobus committed
221
    , m_stackedWidget(0)
222
    , m_toolBar(0)
jkobus's avatar
jkobus committed
223
    , m_entriesComboBox(0)
Tobias Hunger's avatar
Tobias Hunger committed
224
    , m_contextSpinBox(0)
225
226
    , m_toggleSyncAction(0)
    , m_whitespaceButtonAction(0)
Jarek Kobus's avatar
Jarek Kobus committed
227
    , m_toggleDescriptionAction(0)
228
    , m_reloadAction(0)
Tobias Hunger's avatar
Tobias Hunger committed
229
    , m_viewSwitcherAction(0)
230
231
    , m_currentViewIndex(-1)
    , m_currentDiffFileIndex(-1)
Tobias Hunger's avatar
Tobias Hunger committed
232
    , m_ignoreChanges(0)
233
234
    , m_sync(false)
    , m_showDescription(true)
235
{
236
    // Editor:
237
    setDuplicateSupported(true);
238

239
    // Widget:
Jarek Kobus's avatar
Jarek Kobus committed
240
    QSplitter *splitter = new Core::MiniSplitter(Qt::Vertical);
241

242
    m_descriptionWidget = new DescriptionEditorWidget(splitter);
Jarek Kobus's avatar
Jarek Kobus committed
243
244
    m_descriptionWidget->setReadOnly(true);
    splitter->addWidget(m_descriptionWidget);
245

jkobus's avatar
jkobus committed
246
247
248
    m_stackedWidget = new QStackedWidget(splitter);
    splitter->addWidget(m_stackedWidget);

249
250
    addView(new SideBySideView);
    addView(new UnifiedView);
251

Jarek Kobus's avatar
Jarek Kobus committed
252
253
    setWidget(splitter);

254
255
256
257
258
    // Toolbar:
    m_toolBar = new QToolBar;
    m_toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
    const int size = m_views.at(0)->widget()->style()->pixelMetric(QStyle::PM_SmallIconSize);
    m_toolBar->setIconSize(QSize(size, size));
259

jkobus's avatar
jkobus committed
260
261
262
263
264
265
    m_entriesComboBox = new QComboBox;
    m_entriesComboBox->setMinimumContentsLength(20);
    // Make the combo box prefer to expand
    QSizePolicy policy = m_entriesComboBox->sizePolicy();
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
    m_entriesComboBox->setSizePolicy(policy);
266
267
    connect(m_entriesComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &DiffEditor::setCurrentDiffFileIndex);
268
    m_toolBar->addWidget(m_entriesComboBox);
269

Tobias Hunger's avatar
Tobias Hunger committed
270
    m_contextLabel = new QLabel(m_toolBar);
271

272
    m_contextLabel->setText(tr("Context lines:"));
Tobias Hunger's avatar
Tobias Hunger committed
273
274
    m_contextLabel->setContentsMargins(6, 0, 6, 0);
    m_toolBar->addWidget(m_contextLabel);
275

276
277
278
    m_contextSpinBox = new QSpinBox(m_toolBar);
    m_contextSpinBox->setRange(1, 100);
    m_contextSpinBox->setFrame(false);
Tobias Hunger's avatar
Tobias Hunger committed
279
280
    m_contextSpinBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); // Mac Qt5
    m_toolBar->addWidget(m_contextSpinBox);
Jarek Kobus's avatar
Jarek Kobus committed
281

Tobias Hunger's avatar
Tobias Hunger committed
282
283
284
285
286
287
288
289
290
291
    m_whitespaceButtonAction = m_toolBar->addAction(tr("Ignore Whitespace"));
    m_whitespaceButtonAction->setCheckable(true);

    m_toggleDescriptionAction = m_toolBar->addAction(QIcon(QLatin1String(Constants::ICON_TOP_BAR)),
                                               QString());
    m_toggleDescriptionAction->setCheckable(true);

    m_reloadAction = m_toolBar->addAction(QIcon(QLatin1String(Core::Constants::ICON_RELOAD_GRAY)),
                                          tr("Reload Diff"));
    m_reloadAction->setToolTip(tr("Reload Diff"));
292

Tobias Hunger's avatar
Tobias Hunger committed
293
294
295
    m_toggleSyncAction = m_toolBar->addAction(QIcon(QLatin1String(Core::Constants::ICON_LINK)),
                                        QString());
    m_toggleSyncAction->setCheckable(true);
jkobus's avatar
jkobus committed
296

Tobias Hunger's avatar
Tobias Hunger committed
297
    m_viewSwitcherAction = m_toolBar->addAction(QIcon(), QString());
jkobus's avatar
jkobus committed
298

Tobias Hunger's avatar
Tobias Hunger committed
299
    connect(m_whitespaceButtonAction, &QAction::toggled, this, &DiffEditor::ignoreWhitespaceHasChanged);
300
301
    connect(m_contextSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            this, &DiffEditor::contextLineCountHasChanged);
Tobias Hunger's avatar
Tobias Hunger committed
302
303
304
    connect(m_toggleSyncAction, &QAction::toggled, this, &DiffEditor::toggleSync);
    connect(m_toggleDescriptionAction, &QAction::toggled, this, &DiffEditor::toggleDescription);
    connect(m_viewSwitcherAction, &QAction::triggered, this, [this]() { showDiffView(nextView()); });
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
}

void DiffEditor::setDocument(QSharedPointer<DiffEditorDocument>(doc))
{
    QTC_ASSERT(m_document.isNull(), return);
    QTC_ASSERT(doc, return);

    m_document = QSharedPointer<DiffEditorDocument>(doc);

    connect(m_descriptionWidget, &DescriptionEditorWidget::requestBranchList,
            m_document.data(), &DiffEditorDocument::requestMoreInformation);
    connect(m_document.data(), &DiffEditorDocument::documentChanged,
            this, &DiffEditor::documentHasChanged);
    connect(m_document.data(), &DiffEditorDocument::descriptionChanged,
            this, &DiffEditor::updateDescription);
    connect(m_document.data(), &DiffEditorDocument::aboutToReload,
            this, &DiffEditor::prepareForReload);
    connect(m_document.data(), &DiffEditorDocument::reloadFinished,
            this, &DiffEditor::reloadHasFinished);

Tobias Hunger's avatar
Tobias Hunger committed
325
    connect(m_reloadAction, &QAction::triggered, this, [this]() { m_document->reload(); });
326
327
    connect(m_document.data(), &DiffEditorDocument::temporaryStateChanged,
            this, &DiffEditor::documentStateChanged);
jkobus's avatar
jkobus committed
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    m_contextSpinBox->setValue(m_document->contextLineCount());
    m_whitespaceButtonAction->setChecked(m_document->ignoreWhitespace());

    documentStateChanged();
    documentHasChanged();
}

DiffEditor::DiffEditor(DiffEditorDocument *doc) : DiffEditor()
{
    Guard guard(&m_ignoreChanges);
    setDocument(QSharedPointer<DiffEditorDocument>(doc));
    setupView(loadSettings());
}

DiffEditor::~DiffEditor()
{
    delete m_toolBar;
    delete m_widget;
}

Core::IEditor *DiffEditor::duplicate()
{
    DiffEditor *editor = new DiffEditor();
    Guard guard(&editor->m_ignoreChanges);

    editor->setDocument(m_document);
    editor->m_sync = m_sync;
    editor->m_showDescription = m_showDescription;

    Core::Id id = currentView()->id();
    IDiffView *view = Utils::findOr(editor->m_views, editor->m_views.at(0),
360
                                    Utils::equal(&IDiffView::id, id));
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    QTC_ASSERT(view, view = editor->currentView());
    editor->setupView(view);

    return editor;
}

Core::IDocument *DiffEditor::document()
{
    return m_document.data();
}

QWidget *DiffEditor::toolBar()
{
    QTC_ASSERT(m_toolBar, return 0);
375
    return m_toolBar;
jkobus's avatar
jkobus committed
376
377
}

378
void DiffEditor::documentHasChanged()
jkobus's avatar
jkobus committed
379
{
380
    int index = 0;
Tobias Hunger's avatar
Tobias Hunger committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    {
        Guard guard(&m_ignoreChanges);
        const QList<FileData> diffFileList = m_document->diffFiles();

        updateDescription();
        currentView()->setDiff(diffFileList, m_document->baseDirectory());

        m_entriesComboBox->clear();
        const int count = diffFileList.count();
        for (int i = 0; i < count; i++) {
            const DiffFileInfo leftEntry = diffFileList.at(i).leftFileInfo;
            const DiffFileInfo rightEntry = diffFileList.at(i).rightFileInfo;
            const QString leftShortFileName = Utils::FileName::fromString(leftEntry.fileName).fileName();
            const QString rightShortFileName = Utils::FileName::fromString(rightEntry.fileName).fileName();
            QString itemText;
            QString itemToolTip;
            if (leftEntry.fileName == rightEntry.fileName) {
jkobus's avatar
jkobus committed
398
399
                itemText = leftShortFileName;

Tobias Hunger's avatar
Tobias Hunger committed
400
401
402
403
404
405
406
407
                if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
                    itemToolTip = leftEntry.fileName;
                } else {
                    itemToolTip = tr("[%1] vs. [%2] %3")
                            .arg(leftEntry.typeInfo,
                                 rightEntry.typeInfo,
                                 leftEntry.fileName);
                }
jkobus's avatar
jkobus committed
408
            } else {
Tobias Hunger's avatar
Tobias Hunger committed
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
                if (leftShortFileName == rightShortFileName) {
                    itemText = leftShortFileName;
                } else {
                    itemText = tr("%1 vs. %2")
                            .arg(leftShortFileName,
                                 rightShortFileName);
                }

                if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
                    itemToolTip = tr("%1 vs. %2")
                            .arg(leftEntry.fileName,
                                 rightEntry.fileName);
                } else {
                    itemToolTip = tr("[%1] %2 vs. [%3] %4")
                            .arg(leftEntry.typeInfo,
                                 leftEntry.fileName,
                                 rightEntry.typeInfo,
                                 rightEntry.fileName);
                }
jkobus's avatar
jkobus committed
428
            }
Tobias Hunger's avatar
Tobias Hunger committed
429
430
431
432
433
434
435
436
437
438
            if (m_currentFileChunk.first == leftEntry.fileName
                    && m_currentFileChunk.second == rightEntry.fileName)
                index = i;
            m_entriesComboBox->addItem(itemText);
            m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
                                           leftEntry.fileName, Qt::UserRole);
            m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
                                           rightEntry.fileName, Qt::UserRole + 1);
            m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
                                           itemToolTip, Qt::ToolTipRole);
jkobus's avatar
jkobus committed
439
440
        }
    }
441
442

    setCurrentDiffFileIndex(m_entriesComboBox->count() > 0 ? index : -1);
jkobus's avatar
jkobus committed
443
444
}

445
void DiffEditor::toggleDescription()
jkobus's avatar
jkobus committed
446
{
Tobias Hunger's avatar
Tobias Hunger committed
447
448
449
    if (m_ignoreChanges > 0)
        return;

450
451
452
    m_showDescription = !m_showDescription;
    saveSetting(QLatin1String(descriptionVisibleKeyC), m_showDescription);
    updateDescription();
jkobus's avatar
jkobus committed
453
454
}

455
void DiffEditor::updateDescription()
Jarek Kobus's avatar
Jarek Kobus committed
456
{
Tobias Hunger's avatar
Tobias Hunger committed
457
458
    QTC_ASSERT(m_toolBar, return);

459
    QString description = m_document->description();
Jarek Kobus's avatar
Jarek Kobus committed
460
    m_descriptionWidget->setPlainText(description);
461
462
    m_descriptionWidget->setVisible(m_showDescription && !description.isEmpty());

Tobias Hunger's avatar
Tobias Hunger committed
463
464
465
466
467
468
    Guard guard(&m_ignoreChanges);
    m_toggleDescriptionAction->setChecked(m_showDescription);
    m_toggleDescriptionAction->setToolTip(m_showDescription ? tr("Hide Change Description")
                                                      : tr("Show Change Description"));
    m_toggleDescriptionAction->setText(m_showDescription ? tr("Hide Change Description")
                                                   : tr("Show Change Description"));
469
    m_toggleDescriptionAction->setVisible(!description.isEmpty());
Jarek Kobus's avatar
Jarek Kobus committed
470
471
}

472
void DiffEditor::contextLineCountHasChanged(int lines)
Jarek Kobus's avatar
Jarek Kobus committed
473
{
474
    QTC_ASSERT(!m_document->isContextLineCountForced(), return);
Tobias Hunger's avatar
Tobias Hunger committed
475
    if (m_ignoreChanges > 0 || lines == m_document->contextLineCount())
476
        return;
Jarek Kobus's avatar
Jarek Kobus committed
477

478
479
480
481
482
    m_document->setContextLineCount(lines);
    saveSetting(QLatin1String(contextLineCountKeyC), lines);

    m_document->reload();
}
Jarek Kobus's avatar
Jarek Kobus committed
483

Tobias Hunger's avatar
Tobias Hunger committed
484
void DiffEditor::ignoreWhitespaceHasChanged()
485
{
Tobias Hunger's avatar
Tobias Hunger committed
486
    const bool ignore = m_whitespaceButtonAction->isChecked();
Jarek Kobus's avatar
Jarek Kobus committed
487

Tobias Hunger's avatar
Tobias Hunger committed
488
489
    if (m_ignoreChanges > 0 || ignore == m_document->ignoreWhitespace())
        return;
490
491
    m_document->setIgnoreWhitespace(ignore);
    saveSetting(QLatin1String(ignoreWhitespaceKeyC), ignore);
Tobias Hunger's avatar
Tobias Hunger committed
492

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
    m_document->reload();
}

void DiffEditor::prepareForReload()
{
    documentStateChanged(); // To update actions...

    QTC_ASSERT(currentView(), return);

    if (m_entriesComboBox->count() > 0) {
        m_currentFileChunk
                = qMakePair(m_entriesComboBox->itemData(m_currentDiffFileIndex, Qt::UserRole).toString(),
                            m_entriesComboBox->itemData(m_currentDiffFileIndex, Qt::UserRole + 1).toString());
    } else {
        m_currentFileChunk = qMakePair(QString(), QString());
    }

Tobias Hunger's avatar
Tobias Hunger committed
510
511
512
513
514
    {
        Guard guard(&m_ignoreChanges);
        m_contextSpinBox->setValue(m_document->contextLineCount());
        m_whitespaceButtonAction->setChecked(m_document->ignoreWhitespace());
    }
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
    currentView()->beginOperation();
}

void DiffEditor::reloadHasFinished(bool success)
{
    if (!currentView())
        return;

    m_currentFileChunk = qMakePair(QString(), QString());

    currentView()->endOperation(success);
}

void DiffEditor::updateEntryToolTip()
{
    const QString &toolTip = m_entriesComboBox->itemData(
                m_entriesComboBox->currentIndex(), Qt::ToolTipRole).toString();
    m_entriesComboBox->setToolTip(toolTip);
}

void DiffEditor::setCurrentDiffFileIndex(int index)
{
Tobias Hunger's avatar
Tobias Hunger committed
537
    if (m_ignoreChanges > 0)
538
539
540
541
        return;

    QTC_ASSERT((index < 0) != (m_entriesComboBox->count() > 0), return);

Tobias Hunger's avatar
Tobias Hunger committed
542
    Guard guard(&m_ignoreChanges);
543
544
545
546
547
    m_currentDiffFileIndex = index;
    currentView()->setCurrentDiffFileIndex(index);

    m_entriesComboBox->setCurrentIndex(m_entriesComboBox->count() > 0 ? qMax(0, index) : -1);
    updateEntryToolTip();
jkobus's avatar
jkobus committed
548
549
}

550
void DiffEditor::documentStateChanged()
551
{
552
553
    const bool canReload = m_document->isTemporary();
    const bool contextVisible = !m_document->isContextLineCountForced();
554

555
    m_whitespaceButtonAction->setVisible(canReload);
Tobias Hunger's avatar
Tobias Hunger committed
556
557
    m_contextLabel->setVisible(canReload && contextVisible);
    m_contextSpinBox->setVisible(canReload && contextVisible);
558
    m_reloadAction->setVisible(canReload);
559
560
}

561
void DiffEditor::updateDiffEditorSwitcher()
jkobus's avatar
jkobus committed
562
{
Tobias Hunger's avatar
Tobias Hunger committed
563
    if (!m_viewSwitcherAction)
564
        return;
565
    IDiffView *next = nextView();
Tobias Hunger's avatar
Tobias Hunger committed
566
567
568
    m_viewSwitcherAction->setIcon(next->icon());
    m_viewSwitcherAction->setToolTip(next->toolTip());
    m_viewSwitcherAction->setText(next->toolTip());
569
570
571
572
}

void DiffEditor::toggleSync()
{
Tobias Hunger's avatar
Tobias Hunger committed
573
574
575
    if (m_ignoreChanges > 0)
        return;

576
577
578
579
580
581
    QTC_ASSERT(currentView(), return);
    m_sync = !m_sync;
    saveSetting(QLatin1String(horizontalScrollBarSynchronizationKeyC), m_sync);
    currentView()->setSync(m_sync);
}

582
IDiffView *DiffEditor::loadSettings()
583
{
584
    QTC_ASSERT(currentView(), return 0);
585
    QSettings *s = Core::ICore::settings();
jkobus's avatar
jkobus committed
586

587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
    // TODO: Remove in 3.6: Read legacy settings first:
    s->beginGroup(QLatin1String(legacySettingsGroupC));
    const bool legacyExists = s->contains(QLatin1String(useDiffEditorKeyC));
    const bool legacyEditor = s->value(
                QLatin1String(useDiffEditorKeyC), true).toBool();
    s->remove(QLatin1String(useDiffEditorKeyC));
    s->endGroup();

    // Save legacy settings to current settings:
    if (legacyExists) {
        saveSetting(QLatin1String(diffViewKeyC), legacyEditor ? m_views.at(0)->id().toSetting() :
                                                                m_views.at(1)->id().toSetting());
    }

    // Read current settings:
    s->beginGroup(QLatin1String(settingsGroupC));
Tobias Hunger's avatar
Tobias Hunger committed
603
604
    m_showDescription = s->value(QLatin1String(descriptionVisibleKeyC), true).toBool();
    m_sync = s->value(QLatin1String(horizontalScrollBarSynchronizationKeyC), true).toBool();
605
606
607
608
609
    m_document->setIgnoreWhitespace(s->value(QLatin1String(ignoreWhitespaceKeyC), false).toBool());
    m_document->setContextLineCount(s->value(QLatin1String(contextLineCountKeyC), 3).toInt());
    Core::Id id = Core::Id::fromSetting(s->value(QLatin1String(diffViewKeyC)));
    s->endGroup();

610
611
    IDiffView *view = Utils::findOr(m_views, m_views.at(0),
                                    Utils::equal(&IDiffView::id, id));
612
    QTC_CHECK(view);
613

614
    return view;
615
616
617
618
619
620
621
622
}

void DiffEditor::saveSetting(const QString &key, const QVariant &value) const
{
    QSettings *s = Core::ICore::settings();
    s->beginGroup(QLatin1String(settingsGroupC));
    s->setValue(key, value);
    s->endGroup();
jkobus's avatar
jkobus committed
623
624
}

625
void DiffEditor::addView(IDiffView *view)
jkobus's avatar
jkobus committed
626
{
627
628
629
    QTC_ASSERT(!m_views.contains(view), return);
    m_views.append(view);
    m_stackedWidget->addWidget(view->widget());
630
631
632
633
    if (m_views.count() == 1)
        setCurrentView(view);

    connect(view, &IDiffView::currentDiffFileIndexChanged, this, &DiffEditor::setCurrentDiffFileIndex);
634
}
jkobus's avatar
jkobus committed
635

636
637
IDiffView *DiffEditor::currentView() const
{
638
639
    if (m_currentViewIndex < 0)
        return 0;
640
641
    return m_views.at(m_currentViewIndex);
}
Jarek Kobus's avatar
Jarek Kobus committed
642

643
644
645
646
647
void DiffEditor::setCurrentView(IDiffView *view)
{
    const int pos = Utils::indexOf(m_views, [view](IDiffView *v) { return v == view; });
    QTC_ASSERT(pos >= 0 && pos < m_views.count(), return);
    m_currentViewIndex = pos;
jkobus's avatar
jkobus committed
648
649
}

650
IDiffView *DiffEditor::nextView()
jkobus's avatar
jkobus committed
651
{
652
653
654
    int pos = m_currentViewIndex + 1;
    if (pos >= m_views.count())
        pos = 0;
jkobus's avatar
jkobus committed
655

656
    return m_views.at(pos);
657
}
jkobus's avatar
jkobus committed
658

659
void DiffEditor::setupView(IDiffView *view)
660
{
661
662
    QTC_ASSERT(view, return);
    setCurrentView(view);
663

664
    saveSetting(QLatin1String(diffViewKeyC), currentView()->id().toSetting());
jkobus's avatar
jkobus committed
665

Tobias Hunger's avatar
Tobias Hunger committed
666
667
668
669
670
671
672
    {
        Guard guard(&m_ignoreChanges);
        m_toggleSyncAction->setVisible(currentView()->supportsSync());
        m_toggleSyncAction->setToolTip(currentView()->syncToolTip());
        m_toggleSyncAction->setText(currentView()->syncToolTip());
        m_toggleSyncAction->setChecked(m_sync);
    }
jkobus's avatar
jkobus committed
673

674
675
    view->setDocument(m_document.data());
    view->setSync(m_sync);
jkobus's avatar
jkobus committed
676

677
678
679
680
    view->beginOperation();
    view->setDiff(m_document->diffFiles(), m_document->baseDirectory());
    view->endOperation(true);
    view->setCurrentDiffFileIndex(m_currentDiffFileIndex);
681

682
    m_stackedWidget->setCurrentWidget(view->widget());
jkobus's avatar
jkobus committed
683

684
685
686
    updateDiffEditorSwitcher();
    if (widget())
        widget()->setFocusProxy(view->widget());
jkobus's avatar
jkobus committed
687
688
}

689
void DiffEditor::showDiffView(IDiffView *view)
jkobus's avatar
jkobus committed
690
{
691
692
    if (currentView() == view)
        return;
jkobus's avatar
jkobus committed
693

694
695
    if (currentView()) // during initialization
        currentView()->setDocument(0);
jkobus's avatar
jkobus committed
696

697
698
    QTC_ASSERT(view, return);
    setupView(view);
Jarek Kobus's avatar
Jarek Kobus committed
699
700
}

701
} // namespace Internal
702
} // namespace DiffEditor
Jarek Kobus's avatar
Jarek Kobus committed
703
704

#include "diffeditor.moc"