forked from qt-creator/qt-creator
Implement diff on close, on revert and on external modification
Task-number: QTCREATORBUG-1531 Change-Id: I8c9a740d66eb7836b3df6850ac243260fd282b32 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -35,6 +35,7 @@ namespace Utils {
|
||||
|
||||
QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName,
|
||||
bool modified,
|
||||
bool enableDiffOption,
|
||||
QWidget *parent)
|
||||
{
|
||||
|
||||
@@ -50,12 +51,13 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName,
|
||||
"The file <i>%1</i> has changed outside Qt Creator. Do you want to reload it?");
|
||||
}
|
||||
msg = msg.arg(fileName.fileName());
|
||||
return reloadPrompt(title, msg, fileName.toUserOutput(), parent);
|
||||
return reloadPrompt(title, msg, fileName.toUserOutput(), enableDiffOption, parent);
|
||||
}
|
||||
|
||||
QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title,
|
||||
const QString &prompt,
|
||||
const QString &details,
|
||||
bool enableDiffOption,
|
||||
QWidget *parent)
|
||||
{
|
||||
QMessageBox msg(parent);
|
||||
@@ -69,7 +71,19 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title,
|
||||
msg.button(QMessageBox::Close)->setText(QCoreApplication::translate("Utils::reloadPrompt",
|
||||
"&Close"));
|
||||
|
||||
switch (msg.exec()) {
|
||||
QPushButton *diffButton = nullptr;
|
||||
if (enableDiffOption) {
|
||||
diffButton = msg.addButton(QCoreApplication::translate(
|
||||
"Utils::reloadPrompt", "No to All && &Diff"),
|
||||
QMessageBox::NoRole);
|
||||
}
|
||||
|
||||
const int result = msg.exec();
|
||||
|
||||
if (msg.clickedButton() == diffButton)
|
||||
return ReloadNoneAndDiff;
|
||||
|
||||
switch (result) {
|
||||
case QMessageBox::Yes:
|
||||
return ReloadCurrent;
|
||||
case QMessageBox::YesToAll:
|
||||
|
@@ -40,15 +40,19 @@ enum ReloadPromptAnswer {
|
||||
ReloadAll,
|
||||
ReloadSkipCurrent,
|
||||
ReloadNone,
|
||||
ReloadNoneAndDiff,
|
||||
CloseCurrent
|
||||
};
|
||||
|
||||
QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName,
|
||||
bool modified,
|
||||
bool enableDiffOption,
|
||||
QWidget *parent);
|
||||
QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title,
|
||||
const QString &prompt,
|
||||
const QString &details, QWidget *parent);
|
||||
const QString &details,
|
||||
bool enableDiffOption,
|
||||
QWidget *parent);
|
||||
|
||||
enum FileDeletedPromptAnswer {
|
||||
FileDeletedClose,
|
||||
|
@@ -218,7 +218,8 @@ HEADERS += corejsextensions.h \
|
||||
iwelcomepage.h \
|
||||
systemsettings.h \
|
||||
coreicons.h \
|
||||
editormanager/documentmodel_p.h
|
||||
editormanager/documentmodel_p.h \
|
||||
diffservice.h
|
||||
|
||||
FORMS += dialogs/newdialog.ui \
|
||||
dialogs/saveitemsdialog.ui \
|
||||
|
@@ -41,6 +41,7 @@ Project {
|
||||
"corejsextensions.cpp", "corejsextensions.h",
|
||||
"coreplugin.cpp", "coreplugin.h",
|
||||
"designmode.cpp", "designmode.h",
|
||||
"diffservice.h",
|
||||
"documentmanager.cpp", "documentmanager.h",
|
||||
"editmode.cpp", "editmode.h",
|
||||
"editortoolbar.cpp", "editortoolbar.h",
|
||||
|
@@ -25,12 +25,15 @@
|
||||
|
||||
#include "saveitemsdialog.h"
|
||||
|
||||
#include <coreplugin/diffservice.h>
|
||||
#include <coreplugin/fileiconprovider.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPushButton>
|
||||
@@ -51,6 +54,12 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent,
|
||||
// QDialogButtonBox's behavior for "destructive" is wrong, the "do not save" should be left-aligned
|
||||
const QDialogButtonBox::ButtonRole discardButtonRole = Utils::HostOsInfo::isMacHost()
|
||||
? QDialogButtonBox::ResetRole : QDialogButtonBox::DestructiveRole;
|
||||
|
||||
if (ExtensionSystem::PluginManager::getObject<Core::DiffService>()) {
|
||||
m_diffButton = m_ui.buttonBox->addButton(tr("&Diff"), discardButtonRole);
|
||||
connect(m_diffButton, &QAbstractButton::clicked, this, &SaveItemsDialog::collectFilesToDiff);
|
||||
}
|
||||
|
||||
QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Do not Save"), discardButtonRole);
|
||||
m_ui.buttonBox->button(QDialogButtonBox::Save)->setDefault(true);
|
||||
m_ui.treeWidget->setFocus();
|
||||
@@ -80,13 +89,13 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent,
|
||||
if (Utils::HostOsInfo::isMacHost())
|
||||
m_ui.treeWidget->setAlternatingRowColors(true);
|
||||
adjustButtonWidths();
|
||||
updateSaveButton();
|
||||
updateButtons();
|
||||
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Save), &QAbstractButton::clicked,
|
||||
this, &SaveItemsDialog::collectItemsToSave);
|
||||
connect(discardButton, &QAbstractButton::clicked, this, &SaveItemsDialog::discardAll);
|
||||
connect(m_ui.treeWidget, &QTreeWidget::itemSelectionChanged,
|
||||
this, &SaveItemsDialog::updateSaveButton);
|
||||
this, &SaveItemsDialog::updateButtons);
|
||||
}
|
||||
|
||||
void SaveItemsDialog::setMessage(const QString &msg)
|
||||
@@ -94,19 +103,27 @@ void SaveItemsDialog::setMessage(const QString &msg)
|
||||
m_ui.msgLabel->setText(msg);
|
||||
}
|
||||
|
||||
void SaveItemsDialog::updateSaveButton()
|
||||
void SaveItemsDialog::updateButtons()
|
||||
{
|
||||
int count = m_ui.treeWidget->selectedItems().count();
|
||||
QPushButton *button = m_ui.buttonBox->button(QDialogButtonBox::Save);
|
||||
QPushButton *saveButton = m_ui.buttonBox->button(QDialogButtonBox::Save);
|
||||
bool buttonsEnabled = true;
|
||||
QString saveText = tr("Save");
|
||||
QString diffText = tr("&Diff && Cancel");
|
||||
if (count == m_ui.treeWidget->topLevelItemCount()) {
|
||||
button->setEnabled(true);
|
||||
button->setText(tr("Save All"));
|
||||
saveText = tr("Save All");
|
||||
diffText = tr("&Diff All && Cancel");
|
||||
} else if (count == 0) {
|
||||
button->setEnabled(false);
|
||||
button->setText(tr("Save"));
|
||||
buttonsEnabled = false;
|
||||
} else {
|
||||
button->setEnabled(true);
|
||||
button->setText(tr("Save Selected"));
|
||||
saveText = tr("Save Selected");
|
||||
diffText = tr("&Diff Selected && Cancel");
|
||||
}
|
||||
saveButton->setEnabled(buttonsEnabled);
|
||||
saveButton->setText(saveText);
|
||||
if (m_diffButton) {
|
||||
m_diffButton->setEnabled(buttonsEnabled);
|
||||
m_diffButton->setText(diffText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +162,16 @@ void SaveItemsDialog::collectItemsToSave()
|
||||
accept();
|
||||
}
|
||||
|
||||
void SaveItemsDialog::collectFilesToDiff()
|
||||
{
|
||||
m_filesToDiff.clear();
|
||||
foreach (QTreeWidgetItem *item, m_ui.treeWidget->selectedItems()) {
|
||||
if (IDocument *doc = item->data(0, Qt::UserRole).value<IDocument*>())
|
||||
m_filesToDiff.append(doc->filePath().toString());
|
||||
}
|
||||
reject();
|
||||
}
|
||||
|
||||
void SaveItemsDialog::discardAll()
|
||||
{
|
||||
m_ui.treeWidget->clearSelection();
|
||||
@@ -156,6 +183,11 @@ QList<IDocument*> SaveItemsDialog::itemsToSave() const
|
||||
return m_itemsToSave;
|
||||
}
|
||||
|
||||
QStringList SaveItemsDialog::filesToDiff() const
|
||||
{
|
||||
return m_filesToDiff;
|
||||
}
|
||||
|
||||
void SaveItemsDialog::setAlwaysSaveMessage(const QString &msg)
|
||||
{
|
||||
m_ui.saveBeforeBuildCheckBox->setText(msg);
|
||||
|
@@ -54,15 +54,19 @@ public:
|
||||
void setAlwaysSaveMessage(const QString &msg);
|
||||
bool alwaysSaveChecked();
|
||||
QList<IDocument *> itemsToSave() const;
|
||||
QStringList filesToDiff() const;
|
||||
|
||||
private:
|
||||
void collectItemsToSave();
|
||||
void collectFilesToDiff();
|
||||
void discardAll();
|
||||
void updateSaveButton();
|
||||
void updateButtons();
|
||||
void adjustButtonWidths();
|
||||
|
||||
Ui::SaveItemsDialog m_ui;
|
||||
QList<IDocument*> m_itemsToSave;
|
||||
QStringList m_filesToDiff;
|
||||
QPushButton *m_diffButton = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
47
src/plugins/coreplugin/diffservice.h
Normal file
47
src/plugins/coreplugin/diffservice.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://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 https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core_global.h"
|
||||
#include <QObject>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QStringList)
|
||||
|
||||
namespace Core {
|
||||
|
||||
class CORE_EXPORT DiffService
|
||||
{
|
||||
public:
|
||||
virtual ~DiffService() {}
|
||||
|
||||
virtual void diffModifiedFiles(const QStringList &fileNames) = 0;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
Q_DECLARE_INTERFACE(Core::DiffService, "Core::DiffService")
|
||||
QT_END_NAMESPACE
|
@@ -29,6 +29,7 @@
|
||||
#include "idocument.h"
|
||||
#include "coreconstants.h"
|
||||
|
||||
#include <coreplugin/diffservice.h>
|
||||
#include <coreplugin/dialogs/readonlyfilesdialog.h>
|
||||
#include <coreplugin/dialogs/saveitemsdialog.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
@@ -38,6 +39,8 @@
|
||||
#include <coreplugin/editormanager/ieditorfactory.h>
|
||||
#include <coreplugin/editormanager/iexternaleditor.h>
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
@@ -606,6 +609,11 @@ static bool saveModifiedFilesHelper(const QList<IDocument *> &documents,
|
||||
(*alwaysSave) = dia.alwaysSaveChecked();
|
||||
if (failedToSave)
|
||||
(*failedToSave) = modifiedDocuments;
|
||||
const QStringList filesToDiff = dia.filesToDiff();
|
||||
if (!filesToDiff.isEmpty()) {
|
||||
if (auto diffService = ExtensionSystem::PluginManager::getObject<DiffService>())
|
||||
diffService->diffModifiedFiles(filesToDiff);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (alwaysSave)
|
||||
@@ -978,6 +986,7 @@ void DocumentManager::checkForReload()
|
||||
|
||||
// handle the IDocuments
|
||||
QStringList errorStrings;
|
||||
QStringList filesToDiff;
|
||||
foreach (IDocument *document, changedIDocuments) {
|
||||
IDocument::ChangeTrigger trigger = IDocument::TriggerInternal;
|
||||
IDocument::ChangeType type = IDocument::TypePermissions;
|
||||
@@ -1067,7 +1076,7 @@ void DocumentManager::checkForReload()
|
||||
// IDocument wants us to ask
|
||||
} else if (type == IDocument::TypeContents) {
|
||||
// content change, IDocument wants to ask user
|
||||
if (previousReloadAnswer == ReloadNone) {
|
||||
if (previousReloadAnswer == ReloadNone || previousReloadAnswer == ReloadNoneAndDiff) {
|
||||
// answer already given, ignore
|
||||
success = document->reload(&errorString, IDocument::FlagIgnore, IDocument::TypeContents);
|
||||
} else if (previousReloadAnswer == ReloadAll) {
|
||||
@@ -1076,6 +1085,7 @@ void DocumentManager::checkForReload()
|
||||
} else {
|
||||
// Ask about content change
|
||||
previousReloadAnswer = reloadPrompt(document->filePath(), document->isModified(),
|
||||
ExtensionSystem::PluginManager::getObject<DiffService>(),
|
||||
ICore::dialogParent());
|
||||
switch (previousReloadAnswer) {
|
||||
case ReloadAll:
|
||||
@@ -1084,6 +1094,7 @@ void DocumentManager::checkForReload()
|
||||
break;
|
||||
case ReloadSkipCurrent:
|
||||
case ReloadNone:
|
||||
case ReloadNoneAndDiff:
|
||||
success = document->reload(&errorString, IDocument::FlagIgnore, IDocument::TypeContents);
|
||||
break;
|
||||
case CloseCurrent:
|
||||
@@ -1091,6 +1102,9 @@ void DocumentManager::checkForReload()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (previousReloadAnswer == ReloadNoneAndDiff)
|
||||
filesToDiff.append(document->filePath().toString());
|
||||
|
||||
// IDocument wants us to ask, and it's the TypeRemoved case
|
||||
} else {
|
||||
// Ask about removed file
|
||||
@@ -1134,6 +1148,12 @@ void DocumentManager::checkForReload()
|
||||
|
||||
d->m_blockedIDocument = 0;
|
||||
}
|
||||
|
||||
if (!filesToDiff.isEmpty()) {
|
||||
if (auto diffService = ExtensionSystem::PluginManager::getObject<DiffService>())
|
||||
diffService->diffModifiedFiles(filesToDiff);
|
||||
}
|
||||
|
||||
if (!errorStrings.isEmpty())
|
||||
QMessageBox::critical(ICore::dialogParent(), tr("File Error"),
|
||||
errorStrings.join(QLatin1Char('\n')));
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/dialogs/openwithdialog.h>
|
||||
#include <coreplugin/dialogs/readonlyfilesdialog.h>
|
||||
#include <coreplugin/diffservice.h>
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/editormanager/ieditorfactory.h>
|
||||
#include <coreplugin/editormanager/iexternaleditor.h>
|
||||
@@ -2168,11 +2169,21 @@ void EditorManagerPrivate::revertToSaved(IDocument *document)
|
||||
QMessageBox::Yes|QMessageBox::No, ICore::mainWindow());
|
||||
msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
|
||||
msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
|
||||
|
||||
QPushButton *diffButton = nullptr;
|
||||
auto diffService = ExtensionSystem::PluginManager::getObject<DiffService>();
|
||||
if (diffService)
|
||||
diffButton = msgBox.addButton(tr("Cancel && &Diff"), QMessageBox::RejectRole);
|
||||
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
msgBox.setEscapeButton(QMessageBox::No);
|
||||
if (msgBox.exec() == QMessageBox::No)
|
||||
return;
|
||||
|
||||
if (diffService && msgBox.clickedButton() == diffButton) {
|
||||
diffService->diffModifiedFiles(QStringList() << fileName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
QString errorString;
|
||||
if (!document->reload(&errorString, IDocument::FlagReload, IDocument::TypeContents))
|
||||
|
@@ -211,6 +211,66 @@ void DiffAllModifiedFilesController::reload()
|
||||
|
||||
/////////////////
|
||||
|
||||
class DiffModifiedFilesController : public DiffFilesController
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiffModifiedFilesController(Core::IDocument *document, const QStringList &fileNames);
|
||||
|
||||
protected:
|
||||
void reload();
|
||||
|
||||
private:
|
||||
QStringList m_fileNames;
|
||||
};
|
||||
|
||||
DiffModifiedFilesController::DiffModifiedFilesController(Core::IDocument *document, const QStringList &fileNames) :
|
||||
DiffFilesController(document), m_fileNames(fileNames)
|
||||
{ }
|
||||
|
||||
void DiffModifiedFilesController::reload()
|
||||
{
|
||||
QList<FileData> fileDataList;
|
||||
|
||||
foreach (const QString fileName, m_fileNames) {
|
||||
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(
|
||||
Core::DocumentModel::documentForFilePath(fileName));
|
||||
|
||||
if (textDocument && textDocument->isModified()) {
|
||||
QString errorString;
|
||||
Utils::TextFileFormat format = textDocument->format();
|
||||
|
||||
QString leftText;
|
||||
bool leftFileExists = true;
|
||||
const QString fileName = textDocument->filePath().toString();
|
||||
if (Utils::TextFileFormat::readFile(fileName,
|
||||
format.codec,
|
||||
&leftText, &format, &errorString)
|
||||
!= Utils::TextFileFormat::ReadSuccess) {
|
||||
leftFileExists = false;
|
||||
}
|
||||
|
||||
const QString rightText = textDocument->plainText();
|
||||
|
||||
FileData fileData = diffFiles(leftText, rightText);
|
||||
fileData.leftFileInfo.fileName = fileName;
|
||||
fileData.rightFileInfo.fileName = fileName;
|
||||
fileData.leftFileInfo.typeInfo = tr("Saved");
|
||||
fileData.rightFileInfo.typeInfo = tr("Modified");
|
||||
|
||||
if (!leftFileExists)
|
||||
fileData.fileOperation = FileData::NewFile;
|
||||
|
||||
fileDataList << fileData;
|
||||
}
|
||||
}
|
||||
|
||||
setDiffFiles(fileDataList);
|
||||
reloadFinished(true);
|
||||
}
|
||||
|
||||
/////////////////
|
||||
|
||||
class DiffExternalFilesController : public DiffFilesController
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -273,6 +333,26 @@ void DiffExternalFilesController::reload()
|
||||
|
||||
/////////////////
|
||||
|
||||
DiffEditorServiceImpl::DiffEditorServiceImpl(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void DiffEditorServiceImpl::diffModifiedFiles(const QStringList &fileNames)
|
||||
{
|
||||
const QString documentId = QLatin1String("Diff Modified Files");
|
||||
const QString title = tr("Diff Modified Files");
|
||||
auto const document = qobject_cast<DiffEditorDocument *>(
|
||||
DiffEditorController::findOrCreateDocument(documentId, title));
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
if (!DiffEditorController::controller(document))
|
||||
new DiffModifiedFilesController(document, fileNames);
|
||||
Core::EditorManager::activateEditorForDocument(document);
|
||||
document->reload();
|
||||
}
|
||||
|
||||
bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
@@ -309,6 +389,7 @@ bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
|
||||
updateActions();
|
||||
|
||||
addAutoReleasedObject(new DiffEditorFactory(this));
|
||||
addAutoReleasedObject(new DiffEditorServiceImpl(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
|
||||
#include <coreplugin/diffservice.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
@@ -37,6 +38,16 @@ namespace Core { class IEditor; }
|
||||
namespace DiffEditor {
|
||||
namespace Internal {
|
||||
|
||||
class DiffEditorServiceImpl : public QObject, public Core::DiffService
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(Core::DiffService)
|
||||
public:
|
||||
explicit DiffEditorServiceImpl(QObject *parent = nullptr);
|
||||
|
||||
void diffModifiedFiles(const QStringList &fileNames) override;
|
||||
};
|
||||
|
||||
class DiffEditorPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
Reference in New Issue
Block a user