Commit 14148261 authored by Jarek Kobus's avatar Jarek Kobus Committed by Jaroslaw Kobus
Browse files

Implement diff on close, on revert and on external modification



Task-number: QTCREATORBUG-1531
Change-Id: I8c9a740d66eb7836b3df6850ac243260fd282b32
Reviewed-by: Tobias Hunger's avatarTobias Hunger <tobias.hunger@qt.io>
parent 72e19c48
......@@ -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
......
/****************************************************************************
**
** 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,7 +1085,8 @@ void DocumentManager::checkForReload()
} else {
// Ask about content change
previousReloadAnswer = reloadPrompt(document->filePath(), document->isModified(),
ICore::dialogParent());
ExtensionSystem::PluginManager::getObject<DiffService>(),
ICore::dialogParent());
switch (previousReloadAnswer) {
case ReloadAll:
case ReloadCurrent:
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment