Commit 95a028e8 authored by Thomas Hartmann's avatar Thomas Hartmann Committed by Alessandro Portale

Wizards: changes the structure of Wizards

This patch introduces platforms as a top level topic when choosing
a wizard. Also I changed the categories and priorities.

Details:

1. I did change the way the dialog/view is structured in newdialog.cpp

2. I added platformName() and supportsPlatform() to BaseQtVersion.
   I needed two functions because the Simulator does not provide a platform
   and therefore has no platformName but supports two platforms.
   I still have to turn the platform names into proper constants.

3. I changed the categories and priorities to get the layout that was
   discussed. (I had to touch quite alot of files but this is mostly trivial)

4. I added a combobox that allows filtering for platforms.

5. I added flags() to IWizard to indicate that a wizard is platform independent.

Change-Id: I86c7ad628a431ad06505c76580885c6e6c3ddc23
Reviewed-by: default avatarAlessandro Portale <alessandro.portale@nokia.com>
parent ea234868
......@@ -37,7 +37,7 @@ the project file goes last.
The "class" and "firstpage" attributes specify that it is a Qt 4 wizard and
leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project" firstpage="10" id="S.Plain C (CMake)" category="I.Projects" featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint">
<wizard version="1" kind="project" firstpage="10" id="S.Plain C (CMake)" category="I.Projects" featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint" platformIndependent="true">
<icon>console.png</icon>
<description>Creates a plain C project using CMake, not using the Qt library.</description>
<displayname>Plain C Project (CMake Build)</displayname>;
......
......@@ -39,12 +39,12 @@ leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project"
class="qt4project" firstpage="10"
id="R.Plain C" category="I.Projects"
id="R.Plain C" category="H.QtProjects"
featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint">
<icon>console.png</icon>
<description>Creates a plain C project using qmake, not using the Qt library.</description>
<displayname>Plain C Project</displayname>;
<displaycategory>Non-Qt Project</displaycategory>
<displaycategory>Other Projects</displaycategory>
<files>
<file source="main.c" openeditor="true"/>
<file source="project.pro" target="%ProjectName%.pro" openproject="true"/>
......
......@@ -37,7 +37,7 @@ the project file goes last.
The "class" and "firstpage" attributes specify that it is a Qt 4 wizard and
leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project" firstpage="10" id="S.Plain C++ (CMake)" category="I.Projects" featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint">
<wizard version="1" kind="project" firstpage="10" id="S.Plain C++ (CMake)" category="I.Projects" featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint" platformIndependent="true">
<icon>console.png</icon>
<description>Creates a plain C++ project using CMake, not using the Qt library.</description>
<displayname>Plain C++ Project (CMake Build)</displayname>;
......
......@@ -39,12 +39,12 @@ leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project"
class="qt4project" firstpage="10"
id="R.Plain C++" category="I.Projects"
id="R.Plain C++" category="H.QtProjects"
featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint">
<icon>console.png</icon>
<description>Creates a plain C++ project using qmake, not using the Qt library.</description>
<displayname>Plain C++ Project</displayname>;
<displaycategory>Non-Qt Project</displaycategory>
<displaycategory>Other Projects</displaycategory>
<files>
<file source="main.cpp" openeditor="true"/>
<file source="project.pro" target="%ProjectName%.pro" openproject="true"/>
......
......@@ -39,12 +39,12 @@ leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project"
class="qt4project" firstpage="10"
id="QmlExtensionPlugin" category="C.Projects"
id="QmlExtensionPlugin" category="G.Libraries"
featuresRequired="QtSupport.Wizards.FeatureQtQuick,QtSupport.Wizards.FeatureQtQuick.1">
<icon>lib.png</icon>
<description>Creates a C++ plugin that makes it possible to offer extensions that can be loaded dynamically into applications using the QDeclarativeEngine class.</description>
<displayname>Custom QML Extension Plugin</displayname>
<displaycategory>QML Extension Plugin</displaycategory>
<displaycategory>Libraries</displaycategory>
<files>
<file source="qmldir" target="qmldir"/>
<file source="plugin.h" target="%ProjectName:l%_plugin.%CppHeaderSuffix%"/>
......
......@@ -39,12 +39,12 @@ leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project"
class="qt4project" firstpage="10"
id="R.QtCreatorPlugin" category="F.QtProjects"
featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint,QtSupport.Wizards.FeatureQt">
id="R.QtCreatorPlugin" category="G.Libraries"
featuresRequired="QtSupport.Wizards.FeatureGenericCppEntryPoint,QtSupport.Wizards.FeatureQt,QtSupport.Wizards.FeatureDesktop">
<icon>qtcreator_logo_24.png</icon>
<description>Creates a custom Qt Creator plugin.</description>
<displayname>Qt Creator Plugin</displayname>;
<displaycategory>Other Qt Project</displaycategory>
<displaycategory>Libraries</displaycategory>
<files>
<file source="myplugin.pro" target="%PluginName:l%.pro" openproject="true"/>
<file source="MyPlugin.pluginspec.in" target="%PluginName%.pluginspec.in" openeditor="true"/>
......
......@@ -87,6 +87,7 @@ public:
QString category;
QString displayCategory;
Core::FeatureSet requiredFeatures;
Core::IWizard::WizardFlags flags;
};
BaseFileWizardParameterData::BaseFileWizardParameterData(IWizard::WizardKind k) :
......@@ -232,6 +233,15 @@ void BaseFileWizardParameters::setDisplayCategory(const QString &v)
m_d->displayCategory = v;
}
Core::IWizard::WizardFlags BaseFileWizardParameters::flags() const
{
return m_d->flags;
}
void BaseFileWizardParameters::setFlags(Core::IWizard::WizardFlags flags)
{
m_d->flags = flags;
}
/*!
\class Core::Internal::WizardEventLoop
\brief Special event loop that runs a QWizard and terminates if the page changes.
......@@ -407,7 +417,7 @@ QString BaseFileWizard::displayCategory() const
return d->m_parameters.displayCategory();
}
void BaseFileWizard::runWizard(const QString &path, QWidget *parent)
void BaseFileWizard::runWizard(const QString &path, QWidget *parent, const QString &platform)
{
QTC_ASSERT(!path.isEmpty(), return);
......@@ -437,10 +447,9 @@ void BaseFileWizard::runWizard(const QString &path, QWidget *parent)
// Create dialog and run it. Ensure that the dialog is deleted when
// leaving the func, but not before the IFileWizardExtension::process
// has been called
const QScopedPointer<QWizard> wizard(createWizardDialog(parent,
WizardDialogParameters(path,
const QScopedPointer<QWizard> wizard(createWizardDialog(parent, WizardDialogParameters(path,
allExtensionPages,
QString(),
platform,
requiredFeatures())));
QTC_ASSERT(!wizard.isNull(), return);
......@@ -525,6 +534,11 @@ Core::FeatureSet BaseFileWizard::requiredFeatures() const
return d->m_parameters.requiredFeatures();
}
Core::IWizard::WizardFlags BaseFileWizard::flags() const
{
return d->m_parameters.flags();
}
/*!
\fn virtual QWizard *Core::BaseFileWizard::createWizardDialog(QWidget *parent,
const QString &defaultPath,
......
......@@ -94,6 +94,8 @@ public:
Core::FeatureSet requiredFeatures() const;
void setRequiredFeatures(Core::FeatureSet features);
Core::IWizard::WizardFlags flags() const;
void setFlags(Core::IWizard::WizardFlags flags);
private:
QSharedDataPointer<BaseFileWizardParameterData> m_d;
};
......@@ -148,8 +150,9 @@ public:
virtual QString category() const;
virtual QString displayCategory() const;
virtual void runWizard(const QString &path, QWidget *parent);
virtual void runWizard(const QString &path, QWidget *parent, const QString &platform);
virtual Core::FeatureSet requiredFeatures() const;
virtual WizardFlags flags() const;
static QString buildFileName(const QString &path, const QString &baseName, const QString &extension);
static void setupWizard(QWizard *);
......
......@@ -178,14 +178,52 @@ QList<IWizard*> IWizard::wizardsOfKind(WizardKind kind)
return findWizards(WizardKindPredicate(kind));
}
bool IWizard::isAvailable() const
bool IWizard::isAvailable(const QString &platformName) const
{
FeatureSet availableFeatures;
const QList<Core::IFeatureProvider*> featureManagers = ExtensionSystem::PluginManager::instance()->getObjects<Core::IFeatureProvider>();
foreach (const Core::IFeatureProvider *featureManager, featureManagers)
availableFeatures |= featureManager->availableFeatures();
availableFeatures |= featureManager->availableFeatures(platformName);
return availableFeatures.contains(requiredFeatures());
}
QStringList IWizard::supportedPlatforms() const
{
QStringList stringList;
foreach (const QString &platform, allAvailablePlatforms()) {
if (isAvailable(platform))
stringList.append(platform);
}
return stringList;
}
QStringList IWizard::allAvailablePlatforms()
{
QStringList platforms;
const QList<Core::IFeatureProvider*> featureManagers =
ExtensionSystem::PluginManager::instance()->getObjects<Core::IFeatureProvider>();
foreach (const Core::IFeatureProvider *featureManager, featureManagers)
platforms.append(featureManager->availablePlatforms());
return platforms;
}
QString IWizard::displayNameForPlatform(const QString &string)
{
const QList<Core::IFeatureProvider*> featureManagers =
ExtensionSystem::PluginManager::instance()->getObjects<Core::IFeatureProvider>();
foreach (const Core::IFeatureProvider *featureManager, featureManagers) {
QString displayName = featureManager->displayNameForPlatform(string);
if (!displayName.isEmpty())
return displayName;
}
return QString();
}
......@@ -55,6 +55,10 @@ public:
ProjectWizard = 0x04
};
Q_DECLARE_FLAGS(WizardKinds, WizardKind)
enum WizardFlag {
PlatformIndependent = 0x01
};
Q_DECLARE_FLAGS(WizardFlags, WizardFlag)
IWizard(QObject *parent = 0) : QObject(parent) {}
virtual ~IWizard() {}
......@@ -69,19 +73,24 @@ public:
virtual QString displayCategory() const = 0;
virtual FeatureSet requiredFeatures() const = 0;
virtual WizardFlags flags() const = 0;
virtual void runWizard(const QString &path, QWidget *parent) = 0;
virtual void runWizard(const QString &path, QWidget *parent, const QString &platform) = 0;
bool isAvailable() const;
bool isAvailable(const QString &platformName) const;
QStringList supportedPlatforms() const;
// Utility to find all registered wizards
static QList<IWizard*> allWizards();
// Utility to find all registered wizards of a certain kind
static QList<IWizard*> wizardsOfKind(WizardKind kind);
static QStringList allAvailablePlatforms();
static QString displayNameForPlatform(const QString &string);
};
} // namespace Core
Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IWizard::WizardKinds)
Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IWizard::WizardFlags)
#endif // IWIZARD_H
......@@ -40,6 +40,7 @@
#include <coreplugin/featureprovider.h>
#include <QtGui/QAbstractProxyModel>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QItemSelectionModel>
#include <QtGui/QHeaderView>
#include <QtGui/QPushButton>
......@@ -55,6 +56,49 @@ namespace {
const int ICON_SIZE = 22;
struct WizardContainer
{
WizardContainer() : wizard(0), wizardOption(0) {}
WizardContainer(Core::IWizard *w, int i): wizard(w), wizardOption(i) {}
Core::IWizard *wizard;
int wizardOption;
};
inline Core::IWizard *wizardOfItem(const QStandardItem *item = 0)
{
if (!item)
return 0;
return item->data(Qt::UserRole).value<WizardContainer>().wizard;
}
class PlatformFilterProxyModel : public QSortFilterProxyModel
{
// Q_OBJECT
public:
PlatformFilterProxyModel(QObject *parent = 0): QSortFilterProxyModel(parent) {}
void setPlatform(const QString& platform)
{
m_platform = platform;
invalidateFilter();
}
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (!sourceParent.isValid())
return true;
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
Core::IWizard *wizard = wizardOfItem(qobject_cast<QStandardItemModel*>(sourceModel())->itemFromIndex(sourceIndex));
if (wizard)
return wizard->isAvailable(m_platform);
return true;
}
private:
QString m_platform;
};
class TwoLevelProxyModel : public QAbstractProxyModel
{
// Q_OBJECT
......@@ -139,15 +183,9 @@ public:
}
};
inline Core::IWizard *wizardOfItem(const QStandardItem *item = 0)
{
if (!item)
return 0;
return item->data(Qt::UserRole).value<Core::IWizard*>();
}
}
Q_DECLARE_METATYPE(WizardContainer)
using namespace Core;
using namespace Core::Internal;
......@@ -166,9 +204,12 @@ NewDialog::NewDialog(QWidget *parent) :
m_okButton->setText(tr("&Choose..."));
m_model = new QStandardItemModel(this);
m_proxyModel = new TwoLevelProxyModel(this);
m_proxyModel->setSourceModel(m_model);
m_ui->templateCategoryView->setModel(m_proxyModel);
m_twoLevelProxyModel = new TwoLevelProxyModel(this);
m_twoLevelProxyModel->setSourceModel(m_model);
m_filterProxyModel = new PlatformFilterProxyModel(this);
m_filterProxyModel->setSourceModel(m_model);
m_ui->templateCategoryView->setModel(m_twoLevelProxyModel);
m_ui->templateCategoryView->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_ui->templateCategoryView->setItemDelegate(new FancyTopLevelDelegate);
......@@ -188,6 +229,8 @@ NewDialog::NewDialog(QWidget *parent) :
connect(m_okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(m_ui->comboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setSelectedPlatform(QString)));
}
// Sort by category. id
......@@ -204,32 +247,44 @@ void NewDialog::setWizards(QList<IWizard*> wizards)
qStableSort(wizards.begin(), wizards.end(), wizardLessThan);
CategoryItemMap categories;
CategoryItemMap platformMap;
m_model->clear();
QStandardItem *parentItem = m_model->invisibleRootItem();
QStandardItem *projectKindItem = new QStandardItem(tr("Projects"));
projectKindItem->setData(IWizard::ProjectWizard, Qt::UserRole);
projectKindItem->setFlags(0); // disable item to prevent focus
QStandardItem *filesClassesKindItem = new QStandardItem(tr("Files and Classes"));
filesClassesKindItem->setData(IWizard::FileWizard, Qt::UserRole);
filesClassesKindItem->setFlags(0); // disable item to prevent focus
QStandardItem *parentItem = m_model->invisibleRootItem();
parentItem->appendRow(projectKindItem);
parentItem->appendRow(filesClassesKindItem);
if (m_dummyIcon.isNull()) {
m_dummyIcon = QPixmap(ICON_SIZE, ICON_SIZE);
m_dummyIcon.fill(Qt::transparent);
m_dummyIcon = QIcon(QLatin1String(Core::Constants::ICON_NEWFILE));
}
foreach (IWizard *wizard, wizards) {
// ensure category root
const QString categoryName = wizard->category();
QStringList availablePlatforms = IWizard::allAvailablePlatforms();
CategoryItemMap::iterator cit = categories.find(categoryName);
if (cit == categories.end()) {
QStandardItem *categoryItem = new QStandardItem();
if (availablePlatforms.count() > 1) {
m_ui->comboBox->addItem(tr("All templates"), QString());
foreach (const QString &platform, availablePlatforms) {
m_ui->comboBox->addItem(tr("%1 templates").arg(platform), platform);
}
} else {
if (availablePlatforms.isEmpty()) {
m_ui->comboBox->addItem(tr("All templates"), QString());
} else {
const QString platform = availablePlatforms.first();
m_ui->comboBox->addItem(tr("%1 templates").arg(platform), platform);
}
m_ui->comboBox->setDisabled(true);
}
foreach (IWizard *wizard, wizards) {
if (wizard->isAvailable(selectedPlatform())) {
QStandardItem *kindItem;
switch (wizard->kind()) {
case IWizard::ProjectWizard:
......@@ -241,39 +296,8 @@ void NewDialog::setWizards(QList<IWizard*> wizards)
kindItem = filesClassesKindItem;
break;
}
kindItem->appendRow(categoryItem);
m_categoryItems.append(categoryItem);
categoryItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
categoryItem->setText(wizard->displayCategory());
categoryItem->setData(wizard->category(), Qt::UserRole);
cit = categories.insert(categoryName, categoryItem);
}
// add item
if (wizard->isAvailable()) {
QStandardItem *wizardItem = new QStandardItem(wizard->displayName());
QIcon wizardIcon;
// spacing hack. Add proper icons instead
if (wizard->icon().isNull()) {
wizardIcon = m_dummyIcon;
} else {
wizardIcon = wizard->icon();
}
wizardItem->setIcon(wizardIcon);
wizardItem->setData(QVariant::fromValue(wizard), Qt::UserRole);
wizardItem->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
cit.value()->appendRow(wizardItem);
}
}
if (!projectKindItem->hasChildren()) {
QModelIndex idx = projectKindItem->index();
m_model->removeRow(idx.row());
addItem(kindItem, wizard);
}
if (!filesClassesKindItem->hasChildren()) {
QModelIndex idx = filesClassesKindItem->index();
m_model->removeRow(idx.row());
}
}
......@@ -285,19 +309,19 @@ Core::IWizard *NewDialog::showDialog()
if (!lastCategory.isEmpty())
foreach(QStandardItem* item, m_categoryItems) {
if (item->data(Qt::UserRole) == lastCategory) {
idx = m_proxyModel->mapToSource(m_model->indexFromItem(item));
idx = m_twoLevelProxyModel->mapToSource(m_model->indexFromItem(item));
}
}
if (!idx.isValid())
idx = m_proxyModel->index(0,0, m_proxyModel->index(0,0));
idx = m_twoLevelProxyModel->index(0,0, m_twoLevelProxyModel->index(0,0));
m_ui->templateCategoryView->setCurrentIndex(idx);
// We need to set ensure that the category has default focus
m_ui->templateCategoryView->setFocus(Qt::NoFocusReason);
for (int row = 0; row < m_proxyModel->rowCount(); ++row)
m_ui->templateCategoryView->setExpanded(m_proxyModel->index(row, 0), true);
for (int row = 0; row < m_twoLevelProxyModel->rowCount(); ++row)
m_ui->templateCategoryView->setExpanded(m_twoLevelProxyModel->index(row, 0), true);
// Ensure that item description is visible on first show
currentItemChanged(m_ui->templatesView->rootIndex().child(0,0));
......@@ -307,7 +331,7 @@ Core::IWizard *NewDialog::showDialog()
const int retVal = exec();
idx = m_ui->templateCategoryView->currentIndex();
lastCategory = m_model->itemFromIndex(m_proxyModel->mapToSource(idx))->data(Qt::UserRole).toString();
lastCategory = m_model->itemFromIndex(m_twoLevelProxyModel->mapToSource(idx))->data(Qt::UserRole).toString();
if (retVal != Accepted)
return 0;
......@@ -315,6 +339,19 @@ Core::IWizard *NewDialog::showDialog()
return currentWizard();
}
QString NewDialog::selectedPlatform() const
{
int index = m_ui->comboBox->currentIndex();
return m_ui->comboBox->itemData(index).toString();
}
int NewDialog::selectedWizardOption() const
{
QStandardItem *item = m_model->itemFromIndex(m_ui->templatesView->currentIndex());
return item->data(Qt::UserRole).value<WizardContainer>().wizardOption;
}
NewDialog::~NewDialog()
{
delete m_ui;
......@@ -322,15 +359,50 @@ NewDialog::~NewDialog()
IWizard *NewDialog::currentWizard() const
{
return wizardOfItem(m_model->itemFromIndex(m_ui->templatesView->currentIndex()));
QModelIndex index = m_filterProxyModel->mapToSource(m_ui->templatesView->currentIndex());
return wizardOfItem(m_model->itemFromIndex(index));
}
void NewDialog::addItem(QStandardItem *topLEvelCategoryItem, IWizard *wizard)
{
const QString categoryName = wizard->category();
QStandardItem *categoryItem = 0;
for (int i = 0; i < topLEvelCategoryItem->rowCount(); i++) {
if (topLEvelCategoryItem->child(i, 0)->data(Qt::UserRole) == categoryName)
categoryItem = topLEvelCategoryItem->child(i, 0);
}
if (!categoryItem) {
categoryItem = new QStandardItem();
topLEvelCategoryItem->appendRow(categoryItem);
m_categoryItems.append(categoryItem);
categoryItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
categoryItem->setText(QLatin1String(" ") + wizard->displayCategory());
categoryItem->setData(wizard->category(), Qt::UserRole);
}
QStandardItem *wizardItem = new QStandardItem(wizard->displayName());
QIcon wizardIcon;
// spacing hack. Add proper icons instead
if (wizard->icon().isNull()) {
wizardIcon = m_dummyIcon;
} else {
wizardIcon = wizard->icon();
}
wizardItem->setIcon(wizardIcon);
wizardItem->setData(QVariant::fromValue(WizardContainer(wizard, 0)), Qt::UserRole);
wizardItem->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
categoryItem->appendRow(wizardItem);
}
void NewDialog::currentCategoryChanged(const QModelIndex &index)
{
if (index.parent() != m_model->invisibleRootItem()->index()) {
m_ui->templatesView->setModel(m_model);
m_ui->templatesView->setRootIndex(m_proxyModel->mapToSource(index));
m_ui->templatesView->setModel(m_filterProxyModel);
QModelIndex sourceIndex = m_twoLevelProxyModel->mapToSource(index);
sourceIndex = m_filterProxyModel->mapFromSource(sourceIndex);
m_ui->templatesView->setRootIndex(sourceIndex);
// Focus the first item by default
m_ui->templatesView->setCurrentIndex(m_ui->templatesView->rootIndex().child(0,0));
......@@ -342,11 +414,26 @@ void NewDialog::currentCategoryChanged(const QModelIndex &index)
void NewDialog::currentItemChanged(const QModelIndex &index)
{
QStandardItem* cat = m_model->itemFromIndex(index);
if (const IWizard *wizard = wizardOfItem(cat))
m_ui->templateDescription->setText(wizard->description());
QModelIndex sourceIndex = m_filterProxyModel->mapToSource(index);
QStandardItem* cat = (m_model->itemFromIndex(sourceIndex));
if (const IWizard *wizard = wizardOfItem(cat)) {
QString desciption = wizard->description();
if (!Qt::mightBeRichText(desciption))
desciption.replace(QLatin1Char('\n'), QLatin1String("<br>"));
desciption += QLatin1String("<br>&l