forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
||||
*/
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user