Wizards: Let user decide which project file to use

When importing projects, it can happen that several files are
encountered that could serve as the main project file. Until now, we
basically opened a random one, which was less than ideal. Now the user
can choose.

Fixes: QTCREATORBUG-17828
Change-Id: Iec08c942d0f9ff349c9752503c8157556f07b416
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
This commit is contained in:
Christian Kandeler
2019-04-10 18:15:47 +02:00
parent a1944d800f
commit 31517f00bb
4 changed files with 119 additions and 11 deletions

View File

@@ -981,9 +981,6 @@
as the top level directory. This setting defaults to an empty list
and no subdirectories will be scanned.
\li \c firstProjectOnly is a boolean value, which will determine whether
all project files that were found will be opened as a project or
only the first one. This setting defaults to \c true.
\endlist
*/

View File

@@ -36,11 +36,19 @@
#include <coreplugin/messagemanager.h>
#include <utils/algorithm.h>
#include <utils/itemviews.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/wizardpage.h>
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QVariant>
#ifdef WITH_TESTS
@@ -49,6 +57,92 @@
namespace ProjectExplorer {
namespace Internal {
class ProjectFileTreeItem : public Utils::TreeItem
{
public:
ProjectFileTreeItem(JsonWizard::GeneratorFile *candidate) : m_candidate(candidate)
{
toggleProjectFileStatus(false);
}
void toggleProjectFileStatus(bool on)
{
m_candidate->file.setAttributes(m_candidate->file.attributes()
.setFlag(Core::GeneratedFile::OpenProjectAttribute, on));
}
private:
QVariant data(int column, int role) const override
{
if (column != 0 || role != Qt::DisplayRole)
return QVariant();
return QDir::toNativeSeparators(m_candidate->file.path());
}
JsonWizard::GeneratorFile * const m_candidate;
};
class ProjectFilesModel : public Utils::TreeModel<Utils::TreeItem, ProjectFileTreeItem>
{
public:
ProjectFilesModel(const QList<JsonWizard::GeneratorFile *> &candidates, QObject *parent)
: TreeModel(parent)
{
setHeader({QCoreApplication::translate("ProjectExplorer::JsonWizard", "Project File")});
for (JsonWizard::GeneratorFile * const candidate : candidates)
rootItem()->appendChild(new ProjectFileTreeItem(candidate));
}
};
class ProjectFileChooser : public QDialog
{
public:
ProjectFileChooser(const QList<JsonWizard::GeneratorFile *> &candidates, QWidget *parent)
: QDialog(parent), m_view(new Utils::TreeView(this))
{
setWindowTitle(QCoreApplication::translate("ProjectExplorer::JsonWizard",
"Choose project file"));
const auto model = new ProjectFilesModel(candidates, this);
m_view->setSelectionMode(Utils::TreeView::ExtendedSelection);
m_view->setSelectionBehavior(Utils::TreeView::SelectRows);
m_view->setModel(model);
const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
const auto updateOkButton = [buttonBox, this] {
buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(m_view->selectionModel()->hasSelection());
};
connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged,
this, updateOkButton);
updateOkButton();
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
const auto layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(QCoreApplication::translate("ProjectExplorer::JsonWizard",
"The project contains more than one project file. "
"Please select the one you would like to use.")));
layout->addWidget(m_view);
layout->addWidget(buttonBox);
}
private:
void accept() override
{
const QModelIndexList selected = m_view->selectionModel()->selectedRows();
const auto * const model = static_cast<ProjectFilesModel *>(m_view->model());
for (const QModelIndex &index : selected) {
const auto item = static_cast<ProjectFileTreeItem *>(model->itemForIndex(index));
QTC_ASSERT(item, continue);
item->toggleProjectFileStatus(true);
}
QDialog::accept();
}
Utils::TreeView * const m_view;
};
} // namespace Internal
JsonWizard::JsonWizard(QWidget *parent) : Utils::Wizard(parent)
{
setMinimumSize(800, 500);
@@ -113,6 +207,14 @@ JsonWizard::GeneratorFiles JsonWizard::generateFileList()
return GeneratorFiles();
}
QList<GeneratorFile *> projectFiles;
for (JsonWizard::GeneratorFile &f : list) {
if (f.file.attributes().testFlag(Core::GeneratedFile::OpenProjectAttribute))
projectFiles << &f;
}
if (projectFiles.count() > 1)
Internal::ProjectFileChooser(projectFiles, this).exec();
return list;
}

View File

@@ -42,6 +42,8 @@
#include <QDir>
#include <QVariant>
#include <limits>
namespace ProjectExplorer {
namespace Internal {
@@ -70,8 +72,6 @@ bool JsonWizardScannerGenerator::setup(const QVariant &data, QString *errorMessa
m_subDirectoryExpressions << regexp;
}
m_firstProjectOnly = gen.value(QLatin1String("firstProjectOnly"), QLatin1String("true")).toString();
return true;
}
@@ -97,17 +97,28 @@ Core::GeneratedFiles JsonWizardScannerGenerator::fileList(Utils::MacroExpander *
}
}
bool onlyFirst = JsonWizard::boolFromVariant(m_firstProjectOnly, expander);
result = scan(project.absolutePath(), project);
int projectCount = 0;
static const auto getDepth = [](const QString &filePath) { return filePath.count('/'); };
int minDepth = std::numeric_limits<int>::max();
for (auto it = result.begin(); it != result.end(); ++it) {
const QString relPath = project.relativeFilePath(it->path());
it->setBinary(binaryPattern.match(relPath).hasMatch());
bool found = ProjectManager::canOpenProjectForMimeType(Utils::mimeTypeForFile(relPath));
if (found && !(onlyFirst && projectCount++))
if (found) {
it->setAttributes(it->attributes() | Core::GeneratedFile::OpenProjectAttribute);
minDepth = std::min(minDepth, getDepth(it->path()));
}
}
// Project files that appear on a lower level in the file system hierarchy than
// other project files are not candidates for opening.
for (Core::GeneratedFile &f : result) {
if (f.attributes().testFlag(Core::GeneratedFile::OpenProjectAttribute)
&& getDepth(f.path()) > minDepth) {
f.setAttributes(f.attributes().setFlag(Core::GeneratedFile::OpenProjectAttribute,
false));
}
}
return result;

View File

@@ -43,13 +43,11 @@ public:
Core::GeneratedFiles fileList(Utils::MacroExpander *expander,
const QString &wizardDir, const QString &projectDir,
QString *errorMessage) override;
private:
Core::GeneratedFiles scan(const QString &dir, const QDir &base);
bool matchesSubdirectoryPattern(const QString &path);
QString m_binaryPattern;
QString m_firstProjectOnly;
QList<QRegularExpression> m_subDirectoryExpressions;
};