Commit b2b8b867 authored by Tobias Hunger's avatar Tobias Hunger

DiffEditor: Refactor the user-facing parts

* Move all data handling into DiffEditorDocument
* Move much of the logic of how to update views into the
  DiffEditor.
* Introduce a base class for the different views on the diff
  to implement.
* Remove DiffEditorGuiController
* Make DiffEditorController smaller and merge the DiffEditorReloader
  into the class
* Simplify communication between the classes involved
* Make much of the implementation private to the plugin

Change-Id: I7ccb9df6061923bcb34cf3090d6d8331895e83c7
Reviewed-by: Orgad Shaneh's avatarOrgad Shaneh <orgads@gmail.com>
Reviewed-by: default avatarJarek Kobus <jaroslaw.kobus@theqtcompany.com>
parent 59640aa7
This diff is collapsed.
......@@ -38,6 +38,7 @@
QT_BEGIN_NAMESPACE
class QComboBox;
class QSpinBox;
class QToolBar;
class QToolButton;
class QStackedWidget;
......@@ -50,7 +51,6 @@ namespace DiffEditor {
namespace Internal {
class DescriptionEditorWidget;
class DiffEditorDocument;
class DiffEditorGuiController;
class IDiffView;
class DiffEditor : public Core::IEditor
......@@ -62,8 +62,6 @@ public:
~DiffEditor();
public:
DiffEditorController *controller() const;
Core::IEditor *duplicate();
bool open(QString *errorString,
......@@ -73,44 +71,52 @@ public:
QWidget *toolBar();
public slots:
void activateEntry(int index);
private slots:
void slotCleared(const QString &message);
void slotDiffFilesChanged(const QList<FileData> &diffFileList,
const QString &workingDirectory);
void entryActivated(int index);
void slotDescriptionChanged(const QString &description);
void slotDescriptionVisibilityChanged();
void slotReloaderChanged();
void documentHasChanged();
void toggleDescription();
void updateDescription();
void contextLineCountHasChanged(int lines);
void ignoreWhitespaceHasChanged(bool ignore);
void prepareForReload();
void reloadHasFinished(bool success);
void setCurrentDiffFileIndex(int index);
void documentStateChanged();
void toggleSync();
private:
void loadSettings();
void saveSetting(const QString &key, const QVariant &value) const;
void updateEntryToolTip();
void showDiffView(IDiffView *newEditor);
void showDiffView(IDiffView *view);
void updateDiffEditorSwitcher();
void addView(IDiffView *view);
IDiffView *currentView() const;
void setCurrentView(IDiffView *view);
IDiffView *nextView();
IDiffView *readLegacyCurrentDiffEditorSetting();
IDiffView *readCurrentDiffEditorSetting();
void writeCurrentDiffEditorSetting(IDiffView *currentEditor);
void setupView(IDiffView *view);
QSharedPointer<DiffEditorDocument> m_document;
DescriptionEditorWidget *m_descriptionWidget;
QStackedWidget *m_stackedWidget;
QVector<IDiffView *> m_views;
int m_currentViewIndex;
DiffEditorGuiController *m_guiController;
QToolBar *m_toolBar;
QComboBox *m_entriesComboBox;
QToolButton *m_whitespaceButton;
QSpinBox *m_contextSpinBox;
QAction *m_toggleSyncAction;
QAction *m_whitespaceButtonAction;
QAction *m_contextLabelAction;
QAction *m_contextSpinBoxAction;
QAction *m_toggleDescriptionAction;
QAction *m_reloadAction;
QToolButton *m_diffEditorSwitcher;
QPair<QString, QString> m_currentFileChunk;
int m_currentViewIndex;
int m_currentDiffFileIndex;
bool m_sync;
bool m_showDescription;
bool m_ignoreChanges;
};
} // namespace Internal
......
......@@ -7,10 +7,8 @@ HEADERS += diffeditor_global.h \
diffeditorcontroller.h \
diffeditordocument.h \
diffeditorfactory.h \
diffeditorguicontroller.h \
diffeditormanager.h \
diffeditorplugin.h \
diffeditorreloader.h \
differ.h \
diffutils.h \
diffview.h \
......@@ -22,10 +20,8 @@ SOURCES += diffeditor.cpp \
diffeditorcontroller.cpp \
diffeditordocument.cpp \
diffeditorfactory.cpp \
diffeditorguicontroller.cpp \
diffeditormanager.cpp \
diffeditorplugin.cpp \
diffeditorreloader.cpp \
differ.cpp \
diffutils.cpp \
diffview.cpp \
......
......@@ -22,14 +22,10 @@ QtcPlugin {
"diffeditordocument.h",
"diffeditorfactory.cpp",
"diffeditorfactory.h",
"diffeditorguicontroller.cpp",
"diffeditorguicontroller.h",
"diffeditormanager.cpp",
"diffeditormanager.h",
"diffeditorplugin.cpp",
"diffeditorplugin.h",
"diffeditorreloader.cpp",
"diffeditorreloader.h",
"differ.cpp",
"differ.h",
"diffutils.cpp",
......
......@@ -36,81 +36,60 @@
#include <QObject>
namespace DiffEditor {
namespace Core { class IDocument; }
class DiffEditorReloader;
namespace DiffEditor {
namespace Internal { class DiffEditorDocument; }
class DIFFEDITOR_EXPORT DiffEditorController : public QObject
{
Q_OBJECT
public:
DiffEditorController(QObject *parent = 0);
~DiffEditorController();
explicit DiffEditorController(Core::IDocument *document);
QString clearMessage() const;
void requestReload();
bool isReloading() const;
QList<FileData> diffFiles() const;
QString workingDirectory() const;
QString description() const;
bool isDescriptionEnabled() const;
int contextLinesNumber() const;
bool isContextLinesNumberEnabled() const;
bool isIgnoreWhitespace() const;
QString baseDirectory() const;
int contextLineCount() const;
bool ignoreWhitespace() const;
QString makePatch(bool revert, bool addPrefix = false) const;
QString contents() const;
QString revisionFromDescription() const;
DiffEditorReloader *reloader() const;
void setReloader(DiffEditorReloader *reloader);
QString makePatch(bool revert, bool addPrefix = false) const;
public slots:
void clear();
void clear(const QString &message);
void setDiffFiles(const QList<FileData> &diffFileList,
const QString &workingDirectory = QString());
void setDescription(const QString &description);
void setDescriptionEnabled(bool on);
void setContextLinesNumber(int lines);
void setContextLinesNumberEnabled(bool on);
void setIgnoreWhitespace(bool ignore);
void requestReload();
void requestChunkActions(QMenu *menu,
int diffFileIndex,
int chunkIndex);
void requestSaveState();
void requestRestoreState();
void branchesForCommitReceived(const QString &output);
void expandBranchesRequested();
void informationForCommitReceived(const QString &output);
signals:
void cleared(const QString &message);
void diffFilesChanged(const QList<FileData> &diffFileList,
const QString &workingDirectory);
void descriptionChanged(const QString &description);
void descriptionEnablementChanged(bool on);
void contextLinesNumberChanged(int lines);
void contextLinesNumberEnablementChanged(bool on);
void ignoreWhitespaceChanged(bool ignore);
void chunkActionsRequested(QMenu *menu, bool isValid);
void saveStateRequested();
void restoreStateRequested();
void requestBranchList(const QString &revision);
void reloaderChanged();
void requestInformationForCommit(const QString &revision);
protected:
// reloadFinished() should be called
// inside reload() (for synchronous reload)
// or later (for asynchronous reload)
virtual void reload() = 0;
void reloadFinished(bool success);
void setDiffFiles(const QList<FileData> &diffFileList,
const QString &baseDirectory = QString());
void setDescription(const QString &description);
void forceContextLineCount(int lines);
private:
void requestMoreInformation();
void requestChunkActions(QMenu *menu, int diffFileIndex, int chunkIndex);
QString prepareBranchesForCommit(const QString &output);
QString m_clearMessage;
QList<FileData> m_diffFiles;
QString m_workingDirectory;
QString m_description;
DiffEditorReloader *m_reloader;
int m_contextLinesNumber;
Internal::DiffEditorDocument *const m_document;
bool m_isReloading;
int m_diffFileIndex;
int m_chunkIndex;
bool m_descriptionEnabled;
bool m_contextLinesNumberEnabled;
bool m_ignoreWhitespace;
friend class Internal::DiffEditorDocument;
};
} // namespace DiffEditor
......
......@@ -31,6 +31,8 @@
#ifndef DIFFEDITORDOCUMENT_H
#define DIFFEDITORDOCUMENT_H
#include "diffutils.h"
#include <coreplugin/textdocument.h>
namespace DiffEditor {
......@@ -45,9 +47,26 @@ class DiffEditorDocument : public Core::BaseTextDocument
Q_PROPERTY(QString plainText READ plainText STORED false) // For access by code pasters
public:
DiffEditorDocument();
~DiffEditorDocument();
DiffEditorController *controller() const;
QString makePatch(int fileIndex, int chunkIndex, bool revert, bool addPrefix = false) const;
void setDiffFiles(const QList<FileData> &data, const QString &directory);
QList<FileData> diffFiles() const;
QString baseDirectory() const;
void setDescription(const QString &description);
QString description() const;
void setContextLineCount(int lines);
int contextLineCount() const;
void forceContextLineCount(int lines);
bool isContextLineCountForced() const;
void setIgnoreWhitespace(bool ignore);
bool ignoreWhitespace() const;
bool setContents(const QByteArray &contents);
QString defaultPath() const;
QString suggestedFileName() const Q_DECL_OVERRIDE;
......@@ -55,13 +74,35 @@ public:
bool isModified() const { return false; }
bool isSaveAsAllowed() const { return true; }
bool save(QString *errorString, const QString &fileName, bool autoSave);
void reload();
bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
bool open(QString *errorString, const QString &fileName);
QString plainText() const;
signals:
void temporaryStateChanged();
void documentChanged();
void descriptionChanged();
void chunkActionsRequested(QMenu *menu, int diffFileIndex, int chunkIndex);
void requestMoreInformation();
public slots:
void beginReload();
void endReload(bool success);
private:
DiffEditorController *const m_controller;
void setController(DiffEditorController *controller);
DiffEditorController *m_controller;
QList<FileData> m_diffFiles;
QString m_baseDirectory;
QString m_description;
int m_contextLineCount;
bool m_isContextLineCountForced;
bool m_ignoreWhitespace;
friend class ::DiffEditor::DiffEditorController;
};
} // namespace Internal
......
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** 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
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** 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.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "diffeditorguicontroller.h"
#include "diffeditorcontroller.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
static const char settingsGroupC[] = "DiffEditor";
static const char descriptionVisibleKeyC[] = "DescriptionVisible";
static const char horizontalScrollBarSynchronizationKeyC[] =
"HorizontalScrollBarSynchronization";
namespace DiffEditor {
namespace Internal {
DiffEditorGuiController::DiffEditorGuiController(
DiffEditorController *controller,
QObject *parent)
: QObject(parent),
m_controller(controller),
m_descriptionVisible(true),
m_syncScrollBars(true),
m_currentDiffFileIndex(-1)
{
QTC_ASSERT(m_controller, return);
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
m_descriptionVisible = s->value(QLatin1String(descriptionVisibleKeyC),
m_descriptionVisible).toBool();
m_syncScrollBars = s->value(QLatin1String(horizontalScrollBarSynchronizationKeyC),
m_syncScrollBars).toBool();
s->endGroup();
connect(m_controller, &DiffEditorController::cleared, this,
&DiffEditorGuiController::slotUpdateDiffFileIndex);
connect(m_controller, &DiffEditorController::diffFilesChanged, this,
&DiffEditorGuiController::slotUpdateDiffFileIndex);
slotUpdateDiffFileIndex();
}
DiffEditorController *DiffEditorGuiController::controller() const
{
return m_controller;
}
bool DiffEditorGuiController::isDescriptionVisible() const
{
return m_descriptionVisible;
}
bool DiffEditorGuiController::horizontalScrollBarSynchronization() const
{
return m_syncScrollBars;
}
int DiffEditorGuiController::currentDiffFileIndex() const
{
return m_currentDiffFileIndex;
}
void DiffEditorGuiController::slotUpdateDiffFileIndex()
{
m_currentDiffFileIndex = (m_controller->diffFiles().isEmpty() ? -1 : 0);
}
void DiffEditorGuiController::setDescriptionVisible(bool on)
{
if (m_descriptionVisible == on)
return;
m_descriptionVisible = on;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(descriptionVisibleKeyC), m_descriptionVisible);
s->endGroup();
emit descriptionVisibilityChanged(on);
}
void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
{
if (m_syncScrollBars == on)
return;
m_syncScrollBars = on;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(horizontalScrollBarSynchronizationKeyC),
m_syncScrollBars);
s->endGroup();
emit horizontalScrollBarSynchronizationChanged(on);
}
void DiffEditorGuiController::setCurrentDiffFileIndex(int diffFileIndex)
{
if (m_controller->diffFiles().isEmpty())
return; // -1 is the only valid value in this case
const int newIndex = qBound(0, diffFileIndex,
m_controller->diffFiles().count() - 1);
if (m_currentDiffFileIndex == newIndex)
return;
m_currentDiffFileIndex = newIndex;
emit currentDiffFileIndexChanged(newIndex);
}
} // namespace Internal
} // namespace DiffEditor
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** 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
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** 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.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef DIFFEDITORGUICONTROLLER_H
#define DIFFEDITORGUICONTROLLER_H
#include <QObject>
namespace DiffEditor {
class DiffEditorController;
namespace Internal {
class DiffEditorGuiController : public QObject
{
Q_OBJECT
public:
DiffEditorGuiController(DiffEditorController *controller,
QObject *parent = 0);
DiffEditorController *controller() const;
bool isDescriptionVisible() const;
bool horizontalScrollBarSynchronization() const;
int currentDiffFileIndex() const;
public slots:
void setDescriptionVisible(bool on);
void setHorizontalScrollBarSynchronization(bool on);
void setCurrentDiffFileIndex(int diffFileIndex);
signals:
void descriptionVisibilityChanged(bool on);
void horizontalScrollBarSynchronizationChanged(bool on);
void currentDiffFileIndexChanged(int diffFileIndex);
private slots:
void slotUpdateDiffFileIndex();
private:
DiffEditorController *const m_controller;
bool m_descriptionVisible;
bool m_syncScrollBars;
int m_currentDiffFileIndex;
};
} // namespace Internal
} // namespace DiffEditor
#endif // DIFFEDITORGUICONTROLLER_H
......@@ -49,10 +49,6 @@ DiffEditorManager::DiffEditorManager(QObject *parent)
{
QTC_ASSERT(!m_instance, return);
m_instance = this;
Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::editorsClosed,
this, &DiffEditorManager::slotEditorsClosed);
}
DiffEditorManager::~DiffEditorManager()
......@@ -60,27 +56,11 @@ DiffEditorManager::~DiffEditorManager()
m_instance = 0;
}
void DiffEditorManager::slotEditorsClosed(const QList<Core::IEditor *> &editors)
{
QMap<Core::IDocument *, int> editorsForDocument;
for (int i = 0; i < editors.count(); i++) {
DiffEditor *diffEditor = qobject_cast<DiffEditor *>(editors.at(i));
if (diffEditor) {
Core::IDocument *document = diffEditor->document();
editorsForDocument[document]++;
}
}
QMapIterator<Core::IDocument *, int> it(editorsForDocument);
while (it.hasNext()) {
it.next();
if (Core::DocumentModel::editorsForDocument(it.key()).count() == 0) // no other editors use that document
removeDocument(it.key());
}
}
Core::IDocument *DiffEditorManager::find(const QString &vcsId)
{
return m_instance->m_idToDocument.value(vcsId);
Core::IDocument *document = m_instance->m_idToDocument.value(vcsId);
QTC_ASSERT(!document || document->isTemporary(), return 0);
return document;
}
Core::IDocument *DiffEditorManager::findOrCreate(const QString &vcsId, const QString &displayName)
......@@ -89,10 +69,8 @@ Core::IDocument *DiffEditorManager::findOrCreate(const QString &vcsId, const QSt
if (document)
return document;
const QString msgWait = tr("Waiting for data...");
DiffEditor *diffEditor = qobject_cast<DiffEditor *>(
Core::EditorManager::openEditorWithContents(Constants::DIFF_EDITOR_ID,
0, msgWait.toUtf8()));
Core::EditorManager::openEditorWithContents(Constants::DIFF_EDITOR_ID, 0));
QTC_ASSERT(diffEditor, return 0);
document = qobject_cast<Internal::DiffEditorDocument *>(diffEditor->document());
......
......@@ -53,17 +53,16 @@ public:
explicit DiffEditorManager(QObject *parent);
virtual ~DiffEditorManager();
static Core::IDocument *find(const QString &vcsId);
static Core::IDocument *findOrCreate(const QString &vcsId, const QString &displayName);
static DiffEditorController *controller(Core::IDocument *document);
private:
static Core::IDocument *find(const QString &vcsId);
static void removeDocument(Core::IDocument *document);
private slots:
void slotEditorsClosed(const QList<Core::IEditor *> &editors);
private:
QMap<QString, Internal::DiffEditorDocument *> m_idToDocument;
friend class Internal::DiffEditorDocument;
};
} // namespace DiffEditor
......
......@@ -31,10 +31,10 @@
#include "diffeditorplugin.h"
#include "diffe