diff --git a/src/plugins/coreplugin/basefilewizard.cpp b/src/plugins/coreplugin/basefilewizard.cpp index ebd8f3db7f4..521b359ba99 100644 --- a/src/plugins/coreplugin/basefilewizard.cpp +++ b/src/plugins/coreplugin/basefilewizard.cpp @@ -37,7 +37,7 @@ #include "ifilewizardextension.h" #include "mimedatabase.h" #include "editormanager/editormanager.h" - +#include "dialogs/promptoverwritedialog.h" #include #include #include @@ -208,6 +208,15 @@ void GeneratedFile::setAttributes(Attributes a) m_d->attributes = a; } +static int indexOfFile(const GeneratedFiles &f, const QString &path) +{ + const int size = f.size(); + for (int i = 0; i < size; ++i) + if (f.at(i).path() == path) + return i; + return -1; +} + // ------------ BaseFileWizardParameterData class BaseFileWizardParameterData : public QSharedData { @@ -589,11 +598,7 @@ void BaseFileWizard::runWizard(const QString &path, QWidget *parent) if (files.empty()) return; // Compile result list and prompt for overwrite - QStringList result; - foreach (const GeneratedFile &generatedFile, files) - result.push_back(generatedFile.path()); - - switch (promptOverwrite(result, &errorMessage)) { + switch (promptOverwrite(&files, &errorMessage)) { case OverwriteCanceled: return; case OverwriteError: @@ -602,7 +607,6 @@ void BaseFileWizard::runWizard(const QString &path, QWidget *parent) case OverwriteOk: break; } - // Write if (!writeFiles(files, &errorMessage)) { QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage); @@ -655,8 +659,10 @@ void BaseFileWizard::runWizard(const QString &path, QWidget *parent) bool BaseFileWizard::writeFiles(const GeneratedFiles &files, QString *errorMessage) { + const GeneratedFile::Attributes noWriteAttributes + = GeneratedFile::CustomGeneratorAttribute|GeneratedFile::KeepExistingFileAttribute; foreach (const GeneratedFile &generatedFile, files) - if (!(generatedFile.attributes() & GeneratedFile::CustomGeneratorAttribute)) + if (!(generatedFile.attributes() & noWriteAttributes )) if (!generatedFile.write(errorMessage)) return false; return true; @@ -728,7 +734,7 @@ bool BaseFileWizard::postGenerateOpenEditors(const GeneratedFiles &l, QString *e the file exists, can be overwritten at all and prompts the user with a summary. */ -BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(const QStringList &files, +BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(GeneratedFiles *files, QString *errorMessage) const { if (debugWizard) @@ -738,17 +744,20 @@ BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(const QStringLis bool oddStuffFound = false; static const QString readOnlyMsg = tr(" [read only]"); - static const QString directoryMsg = tr(" [directory]"); + static const QString directoryMsg = tr(" [folder]"); static const QString symLinkMsg = tr(" [symbolic link]"); - foreach (const QString &fileName, files) { - const QFileInfo fi(fileName); + foreach (const GeneratedFile &file, *files) { + const QFileInfo fi(file.path()); if (fi.exists()) - existingFiles.append(fileName); + existingFiles.append(file.path()); } - // Note: Generated files are using native separators, no need to convert. + if (existingFiles.isEmpty()) + return OverwriteOk; + // Before prompting to overwrite existing files, loop over files and check + // if there is anything blocking overwriting them (like them being links or folders). + // Format a file list message as ( " [readonly], [folder]"). const QString commonExistingPath = Utils::commonPath(existingFiles); - // Format a file list message as ( " [readonly], [directory]"). QString fileNamesMsgPart; foreach (const QString &fileName, existingFiles) { const QFileInfo fi(fileName); @@ -775,24 +784,31 @@ BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(const QStringLis } } - if (existingFiles.isEmpty()) - return OverwriteOk; - if (oddStuffFound) { *errorMessage = tr("The project directory %1 contains files which cannot be overwritten:\n%2.") .arg(QDir::toNativeSeparators(commonExistingPath)).arg(fileNamesMsgPart); return OverwriteError; } - - const QString messageFormat = tr("The following files already exist in the directory %1:\n" - "%2.\nWould you like to overwrite them?"); - const QString message = messageFormat.arg(QDir::toNativeSeparators(commonExistingPath)).arg(fileNamesMsgPart); - const bool yes = (QMessageBox::question(Core::ICore::instance()->mainWindow(), - tr("Existing files"), message, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) - == QMessageBox::Yes); - return yes ? OverwriteOk : OverwriteCanceled; + // Prompt to overwrite existing files. + Internal::PromptOverwriteDialog overwriteDialog; + // Scripts cannot handle overwrite + overwriteDialog.setFiles(existingFiles); + foreach (const GeneratedFile &file, *files) + if (file.attributes() & GeneratedFile::CustomGeneratorAttribute) + overwriteDialog.setFileEnabled(file.path(), false); + if (overwriteDialog.exec() != QDialog::Accepted) + return OverwriteCanceled; + const QStringList existingFilesToKeep = overwriteDialog.uncheckedFiles(); + if (existingFilesToKeep.size() == files->size()) // All exist & all unchecked->Cancel. + return OverwriteCanceled; + // Set 'keep' attribute in files + foreach (const QString &keepFile, existingFilesToKeep) { + const int i = indexOfFile(*files, keepFile); + QTC_ASSERT(i != -1, return OverwriteCanceled; ) + GeneratedFile &file = (*files)[i]; + file.setAttributes(file.attributes() | GeneratedFile::KeepExistingFileAttribute); + } + return OverwriteOk; } /*! diff --git a/src/plugins/coreplugin/basefilewizard.h b/src/plugins/coreplugin/basefilewizard.h index 38c0786c1a0..dfadd40844e 100644 --- a/src/plugins/coreplugin/basefilewizard.h +++ b/src/plugins/coreplugin/basefilewizard.h @@ -69,7 +69,9 @@ public: OpenProjectAttribute = 0x02, /* File is generated by external scripts, do not write out, * see BaseFileWizard::writeFiles() */ - CustomGeneratorAttribute = 0x4 + CustomGeneratorAttribute = 0x4, + /* File exists and the user indicated that he wants to keep it */ + KeepExistingFileAttribute = 0x8 }; Q_DECLARE_FLAGS(Attributes, Attribute) @@ -188,7 +190,7 @@ protected: static QString preferredSuffix(const QString &mimeType); enum OverwriteResult { OverwriteOk, OverwriteError, OverwriteCanceled }; - OverwriteResult promptOverwrite(const QStringList &files, + OverwriteResult promptOverwrite(GeneratedFiles *files, QString *errorMessage) const; static bool postGenerateOpenEditors(const GeneratedFiles &l, QString *errorMessage = 0); diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index a3b0f4928c8..2f9beb37d64 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -88,7 +88,8 @@ SOURCES += mainwindow.cpp \ toolsettings.cpp \ variablechooser.cpp \ mimetypemagicdialog.cpp \ - mimetypesettings.cpp + mimetypesettings.cpp \ + dialogs/promptoverwritedialog.cpp HEADERS += mainwindow.h \ editmode.h \ @@ -179,7 +180,8 @@ HEADERS += mainwindow.h \ toolsettings.h \ variablechooser.h \ mimetypemagicdialog.h \ - mimetypesettings.h + mimetypesettings.h \ + dialogs/promptoverwritedialog.h FORMS += dialogs/newdialog.ui \ actionmanager/commandmappings.ui \ diff --git a/src/plugins/coreplugin/dialogs/promptoverwritedialog.cpp b/src/plugins/coreplugin/dialogs/promptoverwritedialog.cpp new file mode 100644 index 00000000000..c5ce87f8b1f --- /dev/null +++ b/src/plugins/coreplugin/dialogs/promptoverwritedialog.cpp @@ -0,0 +1,163 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "promptoverwritedialog.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +enum { FileNameRole = Qt::UserRole + 1 }; + +/*! + \class Core::Internal::PromptOverwriteDialog + \brief Prompts the user to overwrite a list of files, which he can check. + + Displays the common folder and the files in a checkable list. +*/ + +static inline QString fileNameOfItem(const QStandardItem *item) +{ + return item->data(FileNameRole).toString(); +} + +namespace Core { +namespace Internal { + +PromptOverwriteDialog::PromptOverwriteDialog(QWidget *parent) : + QDialog(parent), + m_label(new QLabel), + m_view(new QTreeView), + m_model(new QStandardItemModel(0, 1, this)) +{ + setWindowTitle(tr("Overwrite Existing Files")); + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_label); + m_view->setRootIsDecorated(false); + m_view->setUniformRowHeights(true); + m_view->setHeaderHidden(true); + m_view->setSelectionMode(QAbstractItemView::NoSelection); + m_view->setModel(m_model); + mainLayout->addWidget(m_view); + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + connect(bb, SIGNAL(accepted()), this, SLOT(accept())); + connect(bb, SIGNAL(rejected()), this, SLOT(reject())); + mainLayout->addWidget(bb); +} + +void PromptOverwriteDialog::setFiles(const QStringList &l) +{ + // Format checkable list excluding common path + const QString nativeCommonPath = QDir::toNativeSeparators(Utils::commonPath(l)); + foreach (const QString &fileName, l) { + const QString nativeFileName = QDir::toNativeSeparators(fileName); + const int length = nativeFileName.size() - nativeCommonPath.size() - 1; + QStandardItem *item = new QStandardItem(nativeFileName.right(length)); + item->setData(QVariant(fileName), FileNameRole); + item->setFlags(Qt::ItemIsEnabled); + item->setCheckable(true); + item->setCheckState(Qt::Checked); + m_model->appendRow(item); + } + const QString message = + tr("The following files already exist in the folder\n%1.\n" + "Would you like to overwrite them?").arg(nativeCommonPath); + m_label->setText(message); +} + +QStandardItem *PromptOverwriteDialog::itemForFile(const QString &f) const +{ + const int rowCount = m_model->rowCount(); + for (int r = 0; r < rowCount; ++r) { + QStandardItem *item = m_model->item(r, 0); + if (fileNameOfItem(item) == f) + return item; + } + return 0; +} + +QStringList PromptOverwriteDialog::files(Qt::CheckState cs) const +{ + QStringList result; + const int rowCount = m_model->rowCount(); + for (int r = 0; r < rowCount; ++r) { + const QStandardItem *item = m_model->item(r, 0); + if (item->checkState() == cs) + result.push_back(fileNameOfItem(item)); + } + return result; +} + +void PromptOverwriteDialog::setFileEnabled(const QString &f, bool e) +{ + if (QStandardItem *item = itemForFile(f)) { + Qt::ItemFlags flags = item->flags(); + if (e) { + flags |= Qt::ItemIsEnabled; + } else { + flags &= ~Qt::ItemIsEnabled; + } + item->setFlags(flags); + } +} + +bool PromptOverwriteDialog::isFileEnabled(const QString &f) const +{ + if (const QStandardItem *item = itemForFile(f)) + return (item->flags() & Qt::ItemIsEnabled); + return false; +} + +void PromptOverwriteDialog::setFileChecked(const QString &f, bool e) +{ + if (QStandardItem *item = itemForFile(f)) + item->setCheckState(e ? Qt::Checked : Qt::Unchecked); +} + +bool PromptOverwriteDialog::isFileChecked(const QString &f) const +{ + if (const QStandardItem *item = itemForFile(f)) + return item->checkState() == Qt::Checked; + return false; +} + +} // namespace Internal +} // namespace Core diff --git a/src/plugins/coreplugin/dialogs/promptoverwritedialog.h b/src/plugins/coreplugin/dialogs/promptoverwritedialog.h new file mode 100644 index 00000000000..17b3736b0df --- /dev/null +++ b/src/plugins/coreplugin/dialogs/promptoverwritedialog.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef PROMPTOVERWRITEDIALOG_H +#define PROMPTOVERWRITEDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +class QTreeView; +class QStandardItemModel; +class QStandardItem; +class QLabel; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +// Documentation inside. +class PromptOverwriteDialog : public QDialog +{ + Q_OBJECT +public: + explicit PromptOverwriteDialog(QWidget *parent = 0); + + void setFiles(const QStringList &); + + void setFileEnabled(const QString &f, bool e); + bool isFileEnabled(const QString &f) const; + + void setFileChecked(const QString &f, bool e); + bool isFileChecked(const QString &f) const; + + QStringList checkedFiles() const { return files(Qt::Checked); } + QStringList uncheckedFiles() const { return files(Qt::Unchecked); } + +private: + QStandardItem *itemForFile(const QString &f) const; + QStringList files(Qt::CheckState cs) const; + + QLabel *m_label; + QTreeView *m_view; + QStandardItemModel *m_model; +}; + +} // namespace Internal +} // namespace Core + +#endif // PROMPTOVERWRITEDIALOG_H diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp index a9e0dde744b..e11aa38d382 100644 --- a/src/plugins/projectexplorer/customwizard/customwizard.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp @@ -262,10 +262,11 @@ bool CustomWizard::writeFiles(const Core::GeneratedFiles &files, QString *errorM return false; if (d->m_parameters->filesGeneratorScript.isEmpty()) return true; - // Prepare run of the custom script to generate. In the case of a // project wizard that is entirely created by a script, // the target project directory might not exist. + // Known issue: By nature, the script does not honor + // Core::GeneratedFile::KeepExistingFileAttribute. const CustomWizardContextPtr ctx = context(); const QString scriptWorkingDir = scriptWorkingDirectory(ctx, d->m_parameters); const QDir scriptWorkingDirDir(scriptWorkingDir); diff --git a/src/plugins/qt4projectmanager/wizards/abstractmobileappwizard.cpp b/src/plugins/qt4projectmanager/wizards/abstractmobileappwizard.cpp index 388d300b01b..ee4584ab96f 100644 --- a/src/plugins/qt4projectmanager/wizards/abstractmobileappwizard.cpp +++ b/src/plugins/qt4projectmanager/wizards/abstractmobileappwizard.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -212,9 +213,9 @@ Core::GeneratedFiles AbstractMobileAppWizard::generateFiles(const QWizard *wizar // contains relative path segments ("../"). inline static QString fileInCurrentProject(const QString &file) { - const QStringList filesInProject = - ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject()->files( - ProjectExplorer::Project::ExcludeGeneratedFiles); + const ProjectExplorer::Project *p = ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject(); + QTC_ASSERT(p, return QString(); ) + const QStringList filesInProject = p->files(ProjectExplorer::Project::ExcludeGeneratedFiles); foreach (const QString &uncleanFile, filesInProject) if (QDir::cleanPath(uncleanFile) == file) return uncleanFile;