Editor: Added Dialog for read only files.

Task-number: QTCREATORBUG-2851
Change-Id: Ic47a5a1833650e31b4e27d0a01259d6b398a6415
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: Eike Ziller <eike.ziller@digia.com>
This commit is contained in:
David Schulz
2012-10-15 11:53:22 +02:00
parent f9c31b4c6b
commit 6c12a06029
14 changed files with 836 additions and 119 deletions

View File

@@ -218,6 +218,12 @@ QString FileUtils::shortNativePath(const FileName &path)
return path.toUserOutput(); return path.toUserOutput();
} }
bool FileUtils::makeWritable(const FileName &path)
{
const QString fileName = path.toString();
return QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
}
QByteArray FileReader::fetchQrc(const QString &fileName) QByteArray FileReader::fetchQrc(const QString &fileName)
{ {
QTC_ASSERT(fileName.startsWith(QLatin1Char(':')), return QByteArray()); QTC_ASSERT(fileName.startsWith(QLatin1Char(':')), return QByteArray());

View File

@@ -96,6 +96,7 @@ public:
static bool isFileNewerThan(const FileName &filePath, const QDateTime &timeStamp); static bool isFileNewerThan(const FileName &filePath, const QDateTime &timeStamp);
static FileName resolveSymlinks(const FileName &path); static FileName resolveSymlinks(const FileName &path);
static QString shortNativePath(const FileName &path); static QString shortNativePath(const FileName &path);
static bool makeWritable(const FileName &path);
}; };
class QTCREATOR_UTILS_EXPORT FileReader class QTCREATOR_UTILS_EXPORT FileReader

View File

@@ -49,6 +49,7 @@ SOURCES += mainwindow.cpp \
dialogs/settingsdialog.cpp \ dialogs/settingsdialog.cpp \
actionmanager/commandmappings.cpp \ actionmanager/commandmappings.cpp \
dialogs/shortcutsettings.cpp \ dialogs/shortcutsettings.cpp \
dialogs/readonlyfilesdialog.cpp \
dialogs/openwithdialog.cpp \ dialogs/openwithdialog.cpp \
progressmanager/progressmanager.cpp \ progressmanager/progressmanager.cpp \
progressmanager/progressview.cpp \ progressmanager/progressview.cpp \
@@ -134,6 +135,7 @@ HEADERS += mainwindow.h \
dialogs/newdialog.h \ dialogs/newdialog.h \
dialogs/settingsdialog.h \ dialogs/settingsdialog.h \
actionmanager/commandmappings.h \ actionmanager/commandmappings.h \
dialogs/readonlyfilesdialog.h \
dialogs/shortcutsettings.h \ dialogs/shortcutsettings.h \
dialogs/openwithdialog.h \ dialogs/openwithdialog.h \
dialogs/iwizard.h \ dialogs/iwizard.h \
@@ -202,6 +204,7 @@ HEADERS += mainwindow.h \
FORMS += dialogs/newdialog.ui \ FORMS += dialogs/newdialog.ui \
actionmanager/commandmappings.ui \ actionmanager/commandmappings.ui \
dialogs/saveitemsdialog.ui \ dialogs/saveitemsdialog.ui \
dialogs/readonlyfilesdialog.ui \
dialogs/openwithdialog.ui \ dialogs/openwithdialog.ui \
generalsettings.ui \ generalsettings.ui \
dialogs/externaltoolconfig.ui \ dialogs/externaltoolconfig.ui \

View File

@@ -187,6 +187,9 @@ QtcPlugin {
"dialogs/openwithdialog.ui", "dialogs/openwithdialog.ui",
"dialogs/promptoverwritedialog.cpp", "dialogs/promptoverwritedialog.cpp",
"dialogs/promptoverwritedialog.h", "dialogs/promptoverwritedialog.h",
"dialogs/readonlyfilesdialog.cpp",
"dialogs/readonlyfilesdialog.h",
"dialogs/readonlyfilesdialog.ui",
"dialogs/saveitemsdialog.cpp", "dialogs/saveitemsdialog.cpp",
"dialogs/saveitemsdialog.h", "dialogs/saveitemsdialog.h",
"dialogs/saveitemsdialog.ui", "dialogs/saveitemsdialog.ui",

View File

@@ -0,0 +1,491 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** 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 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.
**
** 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 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "fileiconprovider.h"
#include "readonlyfilesdialog.h"
#include "ui_readonlyfilesdialog.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <QDir>
#include <QFileInfo>
#include <QMap>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
namespace Core {
namespace Internal {
class ReadOnlyFilesDialogPrivate
{
public:
ReadOnlyFilesDialogPrivate(IDocument *document = 0, bool useSaveAs = false);
~ReadOnlyFilesDialogPrivate();
// Buttongroups containing the operation for one file.
struct ButtonGroupForFile
{
QString fileName;
QButtonGroup *group;
};
QList <ButtonGroupForFile> buttonGroups;
QMap <int, int> setAllIndexForOperation;
// The version control systems for every file, if the file isn't in VCS the value is 0.
QHash <QString, IVersionControl*> versionControls;
// Define if some specific operations should be allowed to make the files writable.
const bool useSaveAs;
bool useVCS;
// Define if an error should be displayed when an operation fails.
bool showWarnings;
QString failWarning;
// The document is necessary for the Save As operation.
IDocument *document;
// Operation text for the tree widget header and combo box entries for
// modifying operations for all files.
const QString mixedText;
QString makeWritableText;
QString versionControlOpenText;
const QString saveAsText;
};
ReadOnlyFilesDialogPrivate::ReadOnlyFilesDialogPrivate(IDocument *document, bool displaySaveAs)
: useSaveAs(displaySaveAs)
, useVCS(false)
, showWarnings(false)
, document(document)
, mixedText(QApplication::translate("ReadOnlyFilesDialog", "Mixed"))
, makeWritableText(QApplication::translate("ReadOnlyFilesDialog", "Make Writable"))
, versionControlOpenText(QApplication::translate("ReadOnlyFilesDialog", "Open With VCS"))
, saveAsText(QApplication::translate("ReadOnlyFilesDialog", "Save As"))
{}
ReadOnlyFilesDialogPrivate::~ReadOnlyFilesDialogPrivate()
{
foreach (const ButtonGroupForFile &groupForFile, buttonGroups)
delete groupForFile.group;
}
/*!
* \class ReadOnlyFilesDialog
* \brief Dialog to show a set of files which are classified as not writable.
*
* Automatically checks which operations are allowed to make the file writable. These operations
* are Make Writable which tries to set the file permissions in the file system,
* Open With Version Control System if the open operation is allowed by the version control system
* and Save As which is used to save the changes to a document in another file.
*/
ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QList<QString> &fileNames, QWidget *parent)
: QDialog(parent)
, d(new ReadOnlyFilesDialogPrivate)
, ui(new Ui::ReadOnlyFilesDialog)
{
initDialog(fileNames);
}
ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QString &fileName, QWidget *parent)
: QDialog(parent)
, d(new ReadOnlyFilesDialogPrivate)
, ui(new Ui::ReadOnlyFilesDialog)
{
initDialog(QStringList() << fileName);
}
ReadOnlyFilesDialog::ReadOnlyFilesDialog(IDocument *document, QWidget *parent,
bool displaySaveAs)
: QDialog(parent)
, d(new ReadOnlyFilesDialogPrivate(document, displaySaveAs))
, ui(new Ui::ReadOnlyFilesDialog)
{
initDialog(QStringList() << document->fileName());
}
ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QList<IDocument *> documents, QWidget *parent)
: QDialog(parent)
, d(new ReadOnlyFilesDialogPrivate)
, ui(new Ui::ReadOnlyFilesDialog)
{
QStringList files;
foreach (IDocument *document, documents)
files << document->fileName();
initDialog(files);
}
ReadOnlyFilesDialog::~ReadOnlyFilesDialog()
{
delete ui;
delete d;
}
/*!
* \brief Set a user defined message in the dialog.
* \internal
*/
void ReadOnlyFilesDialog::setMessage(const QString &message)
{
ui->msgLabel->setText(message);
}
/*!
* \brief Enable the error output to the user via a messageBox.
* \param warning Added to the dialog, should show possible consequences if the file is still read only.
* \internal
*/
void ReadOnlyFilesDialog::setShowFailWarning(bool show, const QString &warning)
{
d->showWarnings = show;
d->failWarning = warning;
}
/*!
* \brief Opens a message box with an error description according to the type.
* \internal
*/
void ReadOnlyFilesDialog::promptFailWarning(const QStringList &files, ReadOnlyResult type) const
{
if (files.isEmpty())
return;
QString title;
QString message;
QString details;
if (files.count() == 1) {
const QString file = files.first();
switch (type) {
case RO_OpenVCS: {
if (IVersionControl *vc = d->versionControls[file]) {
const QString openText = vc->vcsOpenText().remove(QLatin1Char('&'));
title = tr("Failed To: %1 File").arg(openText);
message = tr("%1 file %2 from version control system %3 failed.\n")
.arg(openText)
.arg(QDir::toNativeSeparators(file))
.arg(vc->displayName());
message += d->failWarning;
} else {
title = tr("No Version Control System Found");
message = tr("Cannot open file %1 from version control system.\n"
"No version control system found.\n")
.arg(QDir::toNativeSeparators(file));
message += d->failWarning;
}
break;
}
case RO_MakeWritable:
title = tr("Cannot Set Permissions");
message = tr("Cannot set permissions for %1 to writable.\n")
.arg(QDir::toNativeSeparators(file));
message += d->failWarning;
break;
case RO_SaveAs:
title = tr("Cannot Save File");
message = tr("Cannot save file %1\n").arg(QDir::toNativeSeparators(file));
message += d->failWarning;
break;
default:
title = tr("Canceled Changing Permissions!");
message = d->failWarning;
break;
}
} else {
title = tr("Could Not Change Permissions On Some Files!");
message = d->failWarning;
message += tr("\nSee details for a complete list of files.");
details = files.join(QLatin1String("\n"));
}
QMessageBox msgBox(QMessageBox::Warning, title, message);
msgBox.setDetailedText(details);
msgBox.exec();
}
/*!
* \brief Executes the dialog.
* \return ReadOnlyResult which gives information about the operation which was used to make the files writable.
* \internal
*
* Also displays an error dialog when some operations can't be executed and the function
* setShowFailWarning was called.
*/
int ReadOnlyFilesDialog::exec()
{
if (QDialog::exec() != QDialog::Accepted)
return RO_Cancel;
ReadOnlyResult result;
QStringList failedToMakeWritable;
foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile buttengroup, d->buttonGroups) {
result = static_cast<ReadOnlyResult>(buttengroup.group->checkedId());
switch (result) {
case RO_MakeWritable:
if (!Utils::FileUtils::makeWritable(Utils::FileName(QFileInfo(buttengroup.fileName)))) {
failedToMakeWritable << buttengroup.fileName;
continue;
}
break;
case RO_OpenVCS:
if (!d->versionControls[buttengroup.fileName]->vcsOpen(buttengroup.fileName)) {
failedToMakeWritable << buttengroup.fileName;
continue;
}
break;
case RO_SaveAs:
if (!EditorManager::instance()->saveDocumentAs(d->document)) {
failedToMakeWritable << buttengroup.fileName;
continue;
}
break;
default:
failedToMakeWritable << buttengroup.fileName;
continue;
}
if (!QFileInfo(buttengroup.fileName).isWritable())
failedToMakeWritable << buttengroup.fileName;
}
if (!failedToMakeWritable.isEmpty()) {
if (d->showWarnings)
promptFailWarning(failedToMakeWritable, result);
}
return failedToMakeWritable.isEmpty() ? result : RO_Cancel;
}
/*!
* \brief Creates a radio button in the column specified with type.
* \param group the created button will be added to this group.
* \return the created button.
* \internal
*/
QRadioButton* ReadOnlyFilesDialog::createRadioButtonForItem(QTreeWidgetItem *item, QButtonGroup *group,
ReadOnlyFilesDialog::ReadOnlyFilesTreeColumn type)
{
QRadioButton *radioButton = new QRadioButton(this);
group->addButton(radioButton, type);
item->setTextAlignment(type, Qt::AlignHCenter);
ui->treeWidget->setItemWidget(item, type, radioButton);
return radioButton;
}
/*!
* \brief Checks the type of the select all combo box and change the user selection per file accordingly.
* \internal
*/
void ReadOnlyFilesDialog::setAll(int index)
{
// If mixed is the current index, no need to change the user selection.
if (index == d->setAllIndexForOperation[-1/*mixed*/])
return;
// Get the selected type from the select all combo box.
ReadOnlyFilesTreeColumn type;
if (index == d->setAllIndexForOperation[MakeWritable])
type = MakeWritable;
else if (index == d->setAllIndexForOperation[OpenWithVCS])
type = OpenWithVCS;
else if (index == d->setAllIndexForOperation[SaveAs])
type = SaveAs;
// Check for every file if the selected operation is available and change it to the operation.
foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile groupForFile, d->buttonGroups) {
QRadioButton *radioButton = qobject_cast<QRadioButton*> (groupForFile.group->button(type));
if (radioButton)
radioButton->setChecked(true);
}
}
/*!
* \brief Updates the select all combo box depending on the selection in the tree widget the user made.
* \internal
*/
void ReadOnlyFilesDialog::updateSelectAll()
{
int selectedOperation = -1;
foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile groupForFile, d->buttonGroups) {
if (selectedOperation == -1) {
selectedOperation = groupForFile.group->checkedId();
} else if (selectedOperation != groupForFile.group->checkedId()) {
ui->setAll->setCurrentIndex(0);
return;
}
}
ui->setAll->setCurrentIndex(d->setAllIndexForOperation[selectedOperation]);
}
/*!
* \brief Adds files to the dialog and check for possible operation to make the file writable.
* \param fileNames A List of files which should be added to the dialog.
* \internal
*/
void ReadOnlyFilesDialog::initDialog(const QStringList &fileNames)
{
ui->setupUi(this);
ui->buttonBox->addButton(tr("&Change Permission"), QDialogButtonBox::AcceptRole);
ui->buttonBox->addButton(QDialogButtonBox::Cancel);
QString vcsOpenTextForAll;
QString vcsMakeWritableTextForAll;
bool useMakeWritable = false;
foreach (const QString &fileName, fileNames) {
const QFileInfo info = QFileInfo(fileName);
const QString visibleName = info.fileName();
const QString directory = info.absolutePath();
// Setup a default entry with filename folder and make writable radio button.
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
item->setText(FileName, visibleName);
item->setIcon(FileName, FileIconProvider::instance()->icon(QFileInfo(fileName)));
item->setText(Folder, Utils::FileUtils::shortNativePath(Utils::FileName(QFileInfo(directory))));
QButtonGroup *radioButtonGroup = new QButtonGroup;
// Add a button for opening the file with a version control system
// if the file is managed by an version control system which allows opening files.
IVersionControl *versionControlForFile =
ICore::vcsManager()->findVersionControlForDirectory(directory);
const bool fileManagedByVCS = versionControlForFile
&& versionControlForFile->openSupportMode() != IVersionControl::NoOpen;
if (fileManagedByVCS) {
const QString vcsOpenTextForFile =
versionControlForFile->vcsOpenText().remove(QLatin1Char('&'));
const QString vcsMakeWritableTextforFile =
versionControlForFile->vcsMakeWritableText().remove(QLatin1Char('&'));
if (!d->useVCS) {
vcsOpenTextForAll = vcsOpenTextForFile;
vcsMakeWritableTextForAll = vcsMakeWritableTextforFile;
d->useVCS = true;
} else {
// If there are different open or make writable texts choose the default one.
if (vcsOpenTextForFile != vcsOpenTextForAll)
vcsOpenTextForAll.clear();
if (vcsMakeWritableTextforFile != vcsMakeWritableTextForAll)
vcsMakeWritableTextForAll.clear();
}
// Add make writable if it is supported by the reposetory.
if (versionControlForFile->openSupportMode() == IVersionControl::OpenOptional) {
useMakeWritable = true;
createRadioButtonForItem(item, radioButtonGroup, MakeWritable);
}
createRadioButtonForItem(item, radioButtonGroup, OpenWithVCS)->setChecked(true);
} else {
useMakeWritable = true;
createRadioButtonForItem(item, radioButtonGroup, MakeWritable)->setChecked(true);
}
// Add a Save As radio button if requested.
if (d->useSaveAs)
createRadioButtonForItem(item, radioButtonGroup, SaveAs);
// If the file is managed by a version control system save the vcs for this file.
d->versionControls[fileName] = fileManagedByVCS ? versionControlForFile : 0;
// Also save the buttongroup for every file to get the result for each entry.
ReadOnlyFilesDialogPrivate::ButtonGroupForFile groupForFile;
groupForFile.fileName = fileName;
groupForFile.group = radioButtonGroup;
d->buttonGroups.append(groupForFile);
connect(radioButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(updateSelectAll()));
}
// Apply the Mac file dialog style.
if (Utils::HostOsInfo::isMacHost())
ui->treeWidget->setAlternatingRowColors(true);
// Do not show any options to the user if he has no choice.
if (!d->useSaveAs && (!d->useVCS || !useMakeWritable)) {
ui->treeWidget->setColumnHidden(MakeWritable, true);
ui->treeWidget->setColumnHidden(OpenWithVCS, true);
ui->treeWidget->setColumnHidden(SaveAs, true);
ui->treeWidget->resizeColumnToContents(FileName);
ui->treeWidget->resizeColumnToContents(Folder);
ui->setAll->setVisible(false);
ui->setAllLabel->setVisible(false);
ui->verticalLayout->removeItem(ui->setAllLayout);
if (d->useVCS)
ui->msgLabel->setText(tr("The following files are not checked out by now.\n"
"Do you want to check out these files now?"));
return;
}
// If there is just one file entry, there is no need to show the select all combo box
if (fileNames.count() < 2) {
ui->setAll->setVisible(false);
ui->setAllLabel->setVisible(false);
ui->verticalLayout->removeItem(ui->setAllLayout);
}
// Add items to the Set all combo box.
ui->setAll->addItem(d->mixedText);
d->setAllIndexForOperation[-1/*mixed*/] = ui->setAll->count() - 1;
if (d->useVCS) {
// If the files are managed by just one version control system, the Open and Make Writable
// text for the specific system is used.
if (!vcsOpenTextForAll.isEmpty() && vcsOpenTextForAll != d->versionControlOpenText) {
d->versionControlOpenText = vcsOpenTextForAll;
ui->treeWidget->headerItem()->setText(OpenWithVCS, d->versionControlOpenText);
}
if (!vcsMakeWritableTextForAll.isEmpty() && vcsMakeWritableTextForAll != d->makeWritableText) {
d->makeWritableText = vcsMakeWritableTextForAll;
ui->treeWidget->headerItem()->setText(MakeWritable, d->makeWritableText);
}
ui->setAll->addItem(d->versionControlOpenText);
ui->setAll->setCurrentIndex(ui->setAll->count() - 1);
d->setAllIndexForOperation[OpenWithVCS] = ui->setAll->count() - 1;
}
if (useMakeWritable) {
ui->setAll->addItem(d->makeWritableText);
d->setAllIndexForOperation[MakeWritable] = ui->setAll->count() - 1;
if (ui->setAll->currentIndex() == -1)
ui->setAll->setCurrentIndex(ui->setAll->count() - 1);
}
if (d->useSaveAs) {
ui->setAll->addItem(d->saveAsText);
d->setAllIndexForOperation[SaveAs] = ui->setAll->count() - 1;
}
connect(ui->setAll, SIGNAL(activated(int)), this, SLOT(setAll(int)));
// Filter which columns should be visible and resize them to content.
for (int i = 0; i < NumberOfColumns; ++i) {
if ((i == SaveAs && !d->useSaveAs) || (i == OpenWithVCS && !d->useVCS)
|| (i == MakeWritable && !useMakeWritable)) {
ui->treeWidget->setColumnHidden(i, true);
continue;
}
ui->treeWidget->resizeColumnToContents(i);
}
}
}// namespace Internal
}// namespace Core

View File

@@ -0,0 +1,109 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** 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 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.
**
** 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 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef READONLYFILESDIALOG_H
#define READONLYFILESDIALOG_H
#include <coreplugin/core_global.h>
#include <QDialog>
#include <QHash>
QT_BEGIN_NAMESPACE
class QButtonGroup;
class QTreeWidgetItem;
class QRadioButton;
QT_END_NAMESPACE
namespace Core {
class IVersionControl;
class IDocument;
namespace Internal {
namespace Ui { class ReadOnlyFilesDialog; }
class CORE_EXPORT ReadOnlyFilesDialog : public QDialog
{
Q_OBJECT
private:
enum ReadOnlyFilesTreeColumn {
MakeWritable = 0,
OpenWithVCS = 1,
SaveAs = 2,
FileName = 3,
Folder = 4,
NumberOfColumns
};
public:
enum ReadOnlyResult {
RO_Cancel = -1,
RO_OpenVCS = OpenWithVCS,
RO_MakeWritable = MakeWritable,
RO_SaveAs = SaveAs
};
explicit ReadOnlyFilesDialog(const QList<QString> &fileNames,
QWidget *parent = 0);
explicit ReadOnlyFilesDialog(const QString &fileName,
QWidget * parent = 0);
explicit ReadOnlyFilesDialog(IDocument *document,
QWidget * parent = 0,
bool displaySaveAs = false);
explicit ReadOnlyFilesDialog(const QList<IDocument *> documents,
QWidget * parent = 0);
~ReadOnlyFilesDialog();
void setMessage(const QString &message);
void setShowFailWarning(bool show, const QString &warning = QString());
int exec();
private:
void initDialog(const QStringList &fileNames);
void promptFailWarning(const QStringList &files, ReadOnlyResult type) const;
QRadioButton *createRadioButtonForItem(QTreeWidgetItem *item, QButtonGroup *group,
ReadOnlyFilesDialog::ReadOnlyFilesTreeColumn type);
private slots:
void setAll(int index);
void updateSelectAll();
private:
class ReadOnlyFilesDialogPrivate *d;
Ui::ReadOnlyFilesDialog *ui;
};
} // namespace Internal
} // namespace Core
#endif // READONLYFILESDIALOG_H

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Core::Internal::ReadOnlyFilesDialog</class>
<widget class="QDialog" name="Core::Internal::ReadOnlyFilesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>639</width>
<height>217</height>
</rect>
</property>
<property name="windowTitle">
<string>Files Without Write Permissions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="msgLabel">
<property name="text">
<string>The following files have no write permissions. Do you want to change the permissions?</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideLeft</enum>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="columnCount">
<number>5</number>
</property>
<column>
<property name="text">
<string>Make Writeable</string>
</property>
</column>
<column>
<property name="text">
<string>Open With VCS</string>
</property>
</column>
<column>
<property name="text">
<string>Save As</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">File Name</string>
</property>
</column>
<column>
<property name="text">
<string>Path</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="setAllLayout">
<item>
<widget class="QLabel" name="setAllLabel">
<property name="text">
<string>Select all, if possible: </string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="setAll"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Core::Internal::ReadOnlyFilesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Core::Internal::ReadOnlyFilesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -35,11 +35,12 @@
#include "ieditorfactory.h" #include "ieditorfactory.h"
#include "iexternaleditor.h" #include "iexternaleditor.h"
#include "idocument.h" #include "idocument.h"
#include "iversioncontrol.h"
#include "mimedatabase.h" #include "mimedatabase.h"
#include "saveitemsdialog.h" #include "saveitemsdialog.h"
#include "coreconstants.h" #include "coreconstants.h"
#include "dialogs/readonlyfilesdialog.h"
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
@@ -58,7 +59,6 @@
#include <QMainWindow> #include <QMainWindow>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
/*! /*!
\class Core::DocumentManager \class Core::DocumentManager
@@ -565,7 +565,9 @@ void DocumentManager::unexpectFileChange(const QString &fileName)
\fn QList<IDocument*> DocumentManager::saveModifiedFilesSilently(const QList<IDocument*> &documents) \fn QList<IDocument*> DocumentManager::saveModifiedFilesSilently(const QList<IDocument*> &documents)
Tries to save the files listed in \a documents. The \a cancelled argument is set to true Tries to save the files listed in \a documents. The \a cancelled argument is set to true
if the user cancelled the dialog. Returns the files that could not be saved. if the user cancelled the dialog. Returns the files that could not be saved. If the files
listed in documents have no write permissions an additional dialog will be prompted to
query the user for these permissions.
*/ */
QList<IDocument *> DocumentManager::saveModifiedDocumentsSilently(const QList<IDocument *> &documents, bool *cancelled) QList<IDocument *> DocumentManager::saveModifiedDocumentsSilently(const QList<IDocument *> &documents, bool *cancelled)
{ {
@@ -581,7 +583,8 @@ QList<IDocument *> DocumentManager::saveModifiedDocumentsSilently(const QList<ID
of modified files (in this context). of modified files (in this context).
The \a cancelled argument is set to true if the user cancelled the dialog, The \a cancelled argument is set to true if the user cancelled the dialog,
\a alwaysSave is set to match the selection of the user, if files should \a alwaysSave is set to match the selection of the user, if files should
always automatically be saved. always automatically be saved. If the files listed in documents have no write
permissions an additional dialog will be prompted to query the user for these permissions.
Returns the files that have not been saved. Returns the files that have not been saved.
*/ */
QList<IDocument *> DocumentManager::saveModifiedDocuments(const QList<IDocument *> &documents, QList<IDocument *> DocumentManager::saveModifiedDocuments(const QList<IDocument *> &documents,
@@ -642,7 +645,25 @@ static QList<IDocument *> saveModifiedFilesHelper(const QList<IDocument *> &docu
*alwaysSave = dia.alwaysSaveChecked(); *alwaysSave = dia.alwaysSaveChecked();
documentsToSave = dia.itemsToSave(); documentsToSave = dia.itemsToSave();
} }
// Check for files without write permissions.
QList<IDocument *> roDocuments;
foreach (IDocument *document, documentsToSave) {
if (document->isFileReadOnly())
roDocuments << document;
}
if (!roDocuments.isEmpty()) {
Core::Internal::ReadOnlyFilesDialog roDialog(roDocuments, d->m_mainWindow);
roDialog.setShowFailWarning(true, QCoreApplication::translate(
"saveModifiedFilesHelper",
"Could not save the files.",
"error message"));
if (roDialog.exec() == Core::Internal::ReadOnlyFilesDialog::RO_Cancel) {
if (cancelled)
(*cancelled) = true;
notSaved = modifiedDocuments;
return notSaved;
}
}
foreach (IDocument *document, documentsToSave) { foreach (IDocument *document, documentsToSave) {
if (!EditorManager::instance()->saveDocument(document)) { if (!EditorManager::instance()->saveDocument(document)) {
if (cancelled) if (cancelled)
@@ -742,7 +763,7 @@ QString DocumentManager::getSaveFileNameWithExtension(const QString &title, cons
Asks the user for a new file name (Save File As) for /arg document. Asks the user for a new file name (Save File As) for /arg document.
*/ */
QString DocumentManager::getSaveAsFileName(IDocument *document, const QString &filter, QString *selectedFilter) QString DocumentManager::getSaveAsFileName(const IDocument *document, const QString &filter, QString *selectedFilter)
{ {
if (!document) if (!document)
return QLatin1String(""); return QLatin1String("");
@@ -804,61 +825,6 @@ QStringList DocumentManager::getOpenFileNames(const QString &filters,
return files; return files;
} }
DocumentManager::ReadOnlyAction
DocumentManager::promptReadOnlyFile(const QString &fileName,
const IVersionControl *versionControl,
QWidget *parent,
bool displaySaveAsButton)
{
// Version Control: If automatic open is desired, open right away.
bool promptVCS = false;
if (versionControl && versionControl->openSupportMode() != IVersionControl::NoOpen) {
if (versionControl->settingsFlags() & IVersionControl::AutoOpen)
return RO_OpenVCS;
promptVCS = true;
}
// Create message box.
QMessageBox msgBox(QMessageBox::Question, tr("File Is Read Only"),
tr("The file <i>%1</i> is read only.").arg(QDir::toNativeSeparators(fileName)),
QMessageBox::Cancel, parent);
QPushButton *vcsButton = 0;
if (promptVCS) {
vcsButton = msgBox.addButton(versionControl->vcsOpenText(), QMessageBox::AcceptRole);
}
QString makeWritableText;
QPushButton *makeWritableButton = 0;
// If the VCS has OpenMandatory we don't show "Make Writable"
if (versionControl->openSupportMode() != IVersionControl::OpenMandatory) {
makeWritableText = versionControl->vcsMakeWritableText();
if (makeWritableText.isEmpty())
makeWritableText = tr("Make &Writable");
makeWritableButton = msgBox.addButton(makeWritableText, QMessageBox::AcceptRole);
}
QPushButton *saveAsButton = 0;
if (displaySaveAsButton)
saveAsButton = msgBox.addButton(tr("&Save As..."), QMessageBox::ActionRole);
if (vcsButton)
msgBox.setDefaultButton(vcsButton);
else if (makeWritableButton)
msgBox.setDefaultButton(makeWritableButton);
msgBox.exec();
QAbstractButton *clickedButton = msgBox.clickedButton();
if (clickedButton == vcsButton)
return RO_OpenVCS;
if (clickedButton == makeWritableButton)
return RO_MakeWriteable;
if (displaySaveAsButton && clickedButton == saveAsButton)
return RO_SaveAs;
return RO_Cancel;
}
void DocumentManager::changedFile(const QString &fileName) void DocumentManager::changedFile(const QString &fileName)
{ {
const bool wasempty = d->m_changedFiles.isEmpty(); const bool wasempty = d->m_changedFiles.isEmpty();

View File

@@ -46,7 +46,6 @@ namespace Core {
class IContext; class IContext;
class IDocument; class IDocument;
class IVersionControl;
class CORE_EXPORT DocumentManager : public QObject class CORE_EXPORT DocumentManager : public QObject
{ {
@@ -98,7 +97,7 @@ public:
const QString &filter = QString(), QString *selectedFilter = 0); const QString &filter = QString(), QString *selectedFilter = 0);
static QString getSaveFileNameWithExtension(const QString &title, const QString &pathIn, static QString getSaveFileNameWithExtension(const QString &title, const QString &pathIn,
const QString &filter); const QString &filter);
static QString getSaveAsFileName(IDocument *document, const QString &filter = QString(), static QString getSaveAsFileName(const IDocument *document, const QString &filter = QString(),
QString *selectedFilter = 0); QString *selectedFilter = 0);
static QList<IDocument *> saveModifiedDocumentsSilently(const QList<IDocument *> &documents, bool *cancelled = 0); static QList<IDocument *> saveModifiedDocumentsSilently(const QList<IDocument *> &documents, bool *cancelled = 0);
@@ -108,15 +107,6 @@ public:
const QString &alwaysSaveMessage = QString(), const QString &alwaysSaveMessage = QString(),
bool *alwaysSave = 0); bool *alwaysSave = 0);
// Helper to display a message dialog when encountering a read-only
// file, prompting the user about how to make it writeable.
enum ReadOnlyAction { RO_Cancel, RO_OpenVCS, RO_MakeWriteable, RO_SaveAs };
static ReadOnlyAction promptReadOnlyFile(const QString &fileName,
const IVersionControl *versionControl,
QWidget *parent,
bool displaySaveAsButton = false);
static QString fileDialogLastVisitedDirectory(); static QString fileDialogLastVisitedDirectory();
static void setFileDialogLastVisitedDirectory(const QString &); static void setFileDialogLastVisitedDirectory(const QString &);

View File

@@ -52,6 +52,7 @@
#include <coreplugin/modemanager.h> #include <coreplugin/modemanager.h>
#include <coreplugin/settingsdatabase.h> #include <coreplugin/settingsdatabase.h>
#include <coreplugin/variablemanager.h> #include <coreplugin/variablemanager.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
@@ -1578,33 +1579,17 @@ MakeWritableResult EditorManager::makeFileWritable(IDocument *document)
{ {
if (!document) if (!document)
return Failed; return Failed;
QString directory = QFileInfo(document->fileName()).absolutePath();
IVersionControl *versionControl = ICore::vcsManager()->findVersionControlForDirectory(directory);
const QString &fileName = document->fileName();
switch (DocumentManager::promptReadOnlyFile(fileName, versionControl, ICore::mainWindow(), document->isSaveAsAllowed())) { ReadOnlyFilesDialog roDialog(document, ICore::mainWindow(), document->isSaveAsAllowed());
case DocumentManager::RO_OpenVCS: switch (roDialog.exec()) {
if (!versionControl->vcsOpen(fileName)) { case ReadOnlyFilesDialog::RO_MakeWritable:
QMessageBox::warning(ICore::mainWindow(), tr("Cannot Open File"), tr("Cannot open the file for editing with SCC.")); case ReadOnlyFilesDialog::RO_OpenVCS:
return Failed;
}
document->checkPermissions();
return OpenedWithVersionControl;
case DocumentManager::RO_MakeWriteable: {
const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
if (!permsOk) {
QMessageBox::warning(ICore::mainWindow(), tr("Cannot Set Permissions"), tr("Cannot set permissions to writable."));
return Failed;
}
}
document->checkPermissions();
return MadeWritable; return MadeWritable;
case DocumentManager::RO_SaveAs : case ReadOnlyFilesDialog::RO_SaveAs:
return saveDocumentAs(document) ? SavedAs : Failed; return SavedAs;
case DocumentManager::RO_Cancel: default:
break; return Failed;
} }
return Failed;
} }
bool EditorManager::saveDocumentAs(IDocument *documentParam) bool EditorManager::saveDocumentAs(IDocument *documentParam)

View File

@@ -163,7 +163,7 @@ QString PerforceVersionControl::vcsGetRepositoryURL(const QString &)
QString PerforceVersionControl::vcsOpenText() const QString PerforceVersionControl::vcsOpenText() const
{ {
return tr("&Edit (%1)").arg(displayName()); return tr("&Edit");
} }
QString PerforceVersionControl::vcsMakeWritableText() const QString PerforceVersionControl::vcsMakeWritableText() const

View File

@@ -43,6 +43,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h> #include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h> #include <coreplugin/vcsmanager.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <projectexplorer/buildmanager.h> #include <projectexplorer/buildmanager.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
@@ -1023,28 +1024,9 @@ bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath
bool Qt4PriFileNode::priFileWritable(const QString &path) bool Qt4PriFileNode::priFileWritable(const QString &path)
{ {
const QString dir = QFileInfo(path).dir().path(); Core::Internal::ReadOnlyFilesDialog roDialog(path, Core::ICore::mainWindow());
Core::IVersionControl *versionControl = Core::ICore::vcsManager()->findVersionControlForDirectory(dir); roDialog.setShowFailWarning(true);
switch (Core::DocumentManager::promptReadOnlyFile(path, versionControl, Core::ICore::mainWindow(), false)) { return roDialog.exec() != Core::Internal::ReadOnlyFilesDialog::RO_Cancel;
case Core::DocumentManager::RO_OpenVCS:
if (!versionControl->vcsOpen(path)) {
QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Open File"), tr("Cannot open the file for editing with VCS."));
return false;
}
break;
case Core::DocumentManager::RO_MakeWriteable: {
const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
if (!permsOk) {
QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Set Permissions"), tr("Cannot set permissions to writable."));
return false;
}
break;
}
case Core::DocumentManager::RO_SaveAs:
case Core::DocumentManager::RO_Cancel:
return false;
}
return true;
} }
bool Qt4PriFileNode::saveModifiedEditors() bool Qt4PriFileNode::saveModifiedEditors()

View File

@@ -34,6 +34,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <texteditor/basetexteditor.h> #include <texteditor/basetexteditor.h>
#include <texteditor/refactoringchanges.h> #include <texteditor/refactoringchanges.h>
@@ -363,7 +364,26 @@ QStringList BaseFileFind::replaceAll(const QString &text,
foreach (const Find::SearchResultItem &item, items) foreach (const Find::SearchResultItem &item, items)
changes[QDir::fromNativeSeparators(item.path.first())].append(item); changes[QDir::fromNativeSeparators(item.path.first())].append(item);
// Checking for files without write permissions
QHashIterator<QString, QList<Find::SearchResultItem> > it(changes); QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
QSet<QString> roFiles;
while (it.hasNext()) {
it.next();
const QFileInfo fileInfo(it.key());
if (!fileInfo.isWritable())
roFiles.insert(it.key());
}
// Query the user for permissions
if (!roFiles.isEmpty()) {
Core::Internal::ReadOnlyFilesDialog roDialog(roFiles.toList(),
Core::ICore::instance()->mainWindow());
roDialog.setShowFailWarning(true, tr("Aborting replace."));
if (roDialog.exec() == Core::Internal::ReadOnlyFilesDialog::RO_Cancel)
return QStringList();
}
it.toFront();
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
const QString fileName = it.key(); const QString fileName = it.key();

View File

@@ -30,13 +30,18 @@
#include "refactoringchanges.h" #include "refactoringchanges.h"
#include "basetexteditor.h" #include "basetexteditor.h"
#include <coreplugin/icore.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/fileutils.h>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QTextBlock> #include <QTextBlock>
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocument> #include <QTextDocument>
#include <QDebug> #include <QDebug>
#include <QApplication>
using namespace TextEditor; using namespace TextEditor;
@@ -319,6 +324,17 @@ void RefactoringFile::setOpenEditor(bool activate, int pos)
void RefactoringFile::apply() void RefactoringFile::apply()
{ {
// test file permissions
if (!QFileInfo(fileName()).isWritable()) {
const QString &path = fileName();
Core::Internal::ReadOnlyFilesDialog roDialog(path, Core::ICore::mainWindow());
const QString &failDetailText = QApplication::translate("RefactoringFile::apply",
"Refactoring cannot be applied.");
roDialog.setShowFailWarning(true, failDetailText);
if (roDialog.exec() == Core::Internal::ReadOnlyFilesDialog::RO_Cancel)
return;
}
// open / activate / goto position // open / activate / goto position
if (m_openEditor && !m_fileName.isEmpty()) { if (m_openEditor && !m_fileName.isEmpty()) {
unsigned line = unsigned(-1), column = unsigned(-1); unsigned line = unsigned(-1), column = unsigned(-1);