iwizardfactory.cpp 11.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30

Tobias Hunger's avatar
Tobias Hunger committed
31
#include "iwizardfactory.h"
32 33 34 35 36

#include "actionmanager/actionmanager.h"
#include "documentmanager.h"
#include "icore.h"
#include "featureprovider.h"
con's avatar
con committed
37

38
#include <extensionsystem/pluginspec.h>
39 40
#include <extensionsystem/pluginmanager.h>

41
#include <utils/algorithm.h>
42
#include <utils/qtcassert.h>
43

44
#include <QAction>
45

con's avatar
con committed
46
/*!
Tobias Hunger's avatar
Tobias Hunger committed
47
    \class Core::IWizardFactory
con's avatar
con committed
48 49
    \mainclass

Tobias Hunger's avatar
Tobias Hunger committed
50
    \brief The class IWizardFactory is the base class for all wizard factories
con's avatar
con committed
51 52 53 54 55 56 57 58 59 60
    (for example shown in \gui {File | New}).

    The wizard interface is a very thin abstraction for the \gui{New...} wizards.
    Basically it defines what to show to the user in the wizard selection dialogs,
    and a hook that is called if the user selects the wizard.

    Wizards can then perform any operations they like, including showing dialogs and
    creating files. Often it is not necessary to create your own wizard from scratch,
    instead use one of the predefined wizards and adapt it to your needs.

Tobias Hunger's avatar
Tobias Hunger committed
61
    To make your wizard known to the system, add your IWizardFactory instance to the
62
    plugin manager's object pool in your plugin's initialize function:
con's avatar
con committed
63 64 65 66
    \code
        bool MyPlugin::initialize(const QStringList &arguments, QString *errorString)
        {
            // ... do setup
Tobias Hunger's avatar
Tobias Hunger committed
67
            addAutoReleasedObject(new MyWizardFactory);
con's avatar
con committed
68 69 70 71 72 73 74 75
            // ... do more setup
        }
    \endcode
    \sa Core::BaseFileWizard
    \sa Core::StandardFileWizard
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
76
    \enum Core::IWizardFactory::WizardKind
con's avatar
con committed
77 78 79 80 81 82 83 84 85 86 87 88
    Used to specify what kind of objects the wizard creates. This information is used
    to show e.g. only wizards that create projects when selecting a \gui{New Project}
    menu item.
    \value FileWizard
        The wizard creates one or more files.
    \value ClassWizard
        The wizard creates a new class (e.g. source+header files).
    \value ProjectWizard
        The wizard creates a new project.
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
89
    \fn IWizardFactory::IWizardFactory(QObject *parent)
con's avatar
con committed
90 91 92 93
    \internal
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
94
    \fn IWizardFactory::~IWizardFactory()
con's avatar
con committed
95 96 97 98
    \internal
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
99
    \fn Kind IWizardFactory::kind() const
con's avatar
con committed
100 101 102 103 104
    Returns what kind of objects are created by the wizard.
    \sa Kind
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
105
    \fn QIcon IWizardFactory::icon() const
con's avatar
con committed
106 107 108 109
    Returns an icon to show in the wizard selection dialog.
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
110
    \fn QString IWizardFactory::description() const
con's avatar
con committed
111 112 113 114 115
    Returns a translated description to show when this wizard is selected
    in the dialog.
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
116
    \fn QString IWizardFactory::displayName() const
con's avatar
con committed
117 118 119 120
    Returns the translated name of the wizard, how it should appear in the
    dialog.
*/

Friedemann Kleint's avatar
Friedemann Kleint committed
121
/*!
Tobias Hunger's avatar
Tobias Hunger committed
122
    \fn QString IWizardFactory::id() const
Friedemann Kleint's avatar
Friedemann Kleint committed
123 124 125 126
    Returns an arbitrary id that is used for sorting within the category.
*/


con's avatar
con committed
127
/*!
Tobias Hunger's avatar
Tobias Hunger committed
128
    \fn QString IWizardFactory::category() const
con's avatar
con committed
129 130 131 132
    Returns a category ID to add the wizard to.
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
133
    \fn QString IWizardFactory::displayCategory() const
con's avatar
con committed
134 135 136 137 138
    Returns the translated string of the category, how it should appear
    in the dialog.
*/

/*!
Tobias Hunger's avatar
Tobias Hunger committed
139
    \fn void IWizardFactory::runWizard(const QString &path,
140 141 142 143
                                      QWidget *parent,
                                      const QString &platform,
                                      const QVariantMap &variables)

144
    This function is executed when the wizard has been selected by the user
con's avatar
con committed
145 146
    for execution. Any dialogs the wizard opens should use the given \a parent.
    The \a path argument is a suggestion for the location where files should be
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
147
    created. The wizard should fill this in its path selection elements as a
con's avatar
con committed
148 149
    default path.
*/
150 151 152

using namespace Core;

153

154 155
namespace {
static QList<IFeatureProvider *> s_providerList;
156 157 158
QList<IWizardFactory *> s_allFactories;
QList<IWizardFactory::FactoryCreator> s_factoryCreators;
bool s_areFactoriesLoaded = false;
159 160
}

161 162
/* A utility to find all wizards supporting a view mode and matching a predicate */
template <class Predicate>
Tobias Hunger's avatar
Tobias Hunger committed
163
    QList<IWizardFactory*> findWizardFactories(Predicate predicate)
164 165
{
    // Filter all wizards
Tobias Hunger's avatar
Tobias Hunger committed
166 167 168 169
    const QList<IWizardFactory*> allFactories = IWizardFactory::allWizardFactories();
    QList<IWizardFactory*> rc;
    const QList<IWizardFactory*>::const_iterator cend = allFactories.constEnd();
    for (QList<IWizardFactory*>::const_iterator it = allFactories.constBegin(); it != cend; ++it)
170 171 172 173 174
        if (predicate(*(*it)))
            rc.push_back(*it);
    return rc;
}

175 176 177 178 179
static Id actionId(const IWizardFactory *factory)
{
    return factory->id().withPrefix("Wizard.Impl.");
}

Tobias Hunger's avatar
Tobias Hunger committed
180
QList<IWizardFactory*> IWizardFactory::allWizardFactories()
181
{
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    if (!s_areFactoriesLoaded) {
        QTC_ASSERT(s_allFactories.isEmpty(), return s_allFactories);

        s_areFactoriesLoaded = true;

        QHash<Id, IWizardFactory *> sanityCheck;
        foreach (const FactoryCreator &fc, s_factoryCreators) {
            QList<IWizardFactory *> tmp = fc();
            foreach (IWizardFactory *newFactory, tmp) {
                QTC_ASSERT(newFactory, continue);
                IWizardFactory *existingFactory = sanityCheck.value(newFactory->id());

                QTC_ASSERT(existingFactory != newFactory, continue);
                if (existingFactory) {
                    qWarning("%s", qPrintable(tr("Factory with id=\"%1\" already registered. Deleting.")
                                              .arg(existingFactory->id().toString())));
                    delete newFactory;
                    continue;
                }

202 203 204 205 206 207 208 209 210
                QTC_ASSERT(!newFactory->m_action, continue);
                newFactory->m_action = new QAction(newFactory->displayName(), newFactory);
                ActionManager::registerAction(newFactory->m_action, actionId(newFactory));

                connect(newFactory->m_action, &QAction::triggered, newFactory, [newFactory]() {
                    QString path = newFactory->runPath(QString());
                    newFactory->runWizard(path, ICore::dialogParent(), QString(), QVariantMap());
                });

211 212 213 214 215 216 217
                sanityCheck.insert(newFactory->id(), newFactory);
                s_allFactories << newFactory;
            }
        }
    }

    return s_allFactories;
218 219 220 221 222 223
}

// Utility to find all registered wizards of a certain kind

class WizardKindPredicate {
public:
Tobias Hunger's avatar
Tobias Hunger committed
224 225
    WizardKindPredicate(IWizardFactory::WizardKind kind) : m_kind(kind) {}
    bool operator()(const IWizardFactory &w) const { return w.kind() == m_kind; }
226
private:
Tobias Hunger's avatar
Tobias Hunger committed
227
    const IWizardFactory::WizardKind m_kind;
228 229
};

Tobias Hunger's avatar
Tobias Hunger committed
230
QList<IWizardFactory*> IWizardFactory::wizardFactoriesOfKind(WizardKind kind)
231
{
Tobias Hunger's avatar
Tobias Hunger committed
232
    return findWizardFactories(WizardKindPredicate(kind));
233 234
}

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
QString IWizardFactory::runPath(const QString &defaultPath)
{
    QString path = defaultPath;
    if (path.isEmpty()) {
        switch (kind()) {
        case IWizardFactory::ProjectWizard:
            // Project wizards: Check for projects directory or
            // use last visited directory of file dialog. Never start
            // at current.
            path = DocumentManager::useProjectsDirectory() ?
                       DocumentManager::projectsDirectory() :
                       DocumentManager::fileDialogLastVisitedDirectory();
            break;
        default:
            path = DocumentManager::fileDialogInitialDirectory();
            break;
        }
    }
    return path;
}

Tobias Hunger's avatar
Tobias Hunger committed
256
bool IWizardFactory::isAvailable(const QString &platformName) const
257
{
258 259 260
    if (platformName.isEmpty())
        return true;

261
    return availableFeatures(platformName).contains(requiredFeatures());
262
}
263

Tobias Hunger's avatar
Tobias Hunger committed
264
QStringList IWizardFactory::supportedPlatforms() const
265 266 267 268 269 270 271 272 273 274 275
{
    QStringList stringList;

    foreach (const QString &platform, allAvailablePlatforms()) {
        if (isAvailable(platform))
            stringList.append(platform);
    }

    return stringList;
}

276 277 278 279 280
void IWizardFactory::registerFactoryCreator(const IWizardFactory::FactoryCreator &creator)
{
    s_factoryCreators << creator;
}

Tobias Hunger's avatar
Tobias Hunger committed
281
QStringList IWizardFactory::allAvailablePlatforms()
282 283 284
{
    QStringList platforms;

Orgad Shaneh's avatar
Orgad Shaneh committed
285
    foreach (const IFeatureProvider *featureManager, s_providerList)
286 287 288 289 290
        platforms.append(featureManager->availablePlatforms());

    return platforms;
}

Tobias Hunger's avatar
Tobias Hunger committed
291
QString IWizardFactory::displayNameForPlatform(const QString &string)
292
{
Orgad Shaneh's avatar
Orgad Shaneh committed
293
    foreach (const IFeatureProvider *featureManager, s_providerList) {
294 295 296 297 298 299
        QString displayName = featureManager->displayNameForPlatform(string);
        if (!displayName.isEmpty())
            return displayName;
    }
    return QString();
}
300 301 302 303 304 305 306 307 308 309 310 311

void IWizardFactory::registerFeatureProvider(IFeatureProvider *provider)
{
    QTC_ASSERT(!s_providerList.contains(provider), return);
    s_providerList.append(provider);
}

void IWizardFactory::destroyFeatureProvider()
{
    qDeleteAll(s_providerList);
    s_providerList.clear();
}
312

313 314 315 316 317 318 319 320 321 322 323
void IWizardFactory::clearWizardFactories()
{
    foreach (IWizardFactory *factory, s_allFactories)
        ActionManager::unregisterAction(factory->m_action, actionId(factory));

    qDeleteAll(s_allFactories);
    s_allFactories.clear();

    s_areFactoriesLoaded = false;
}

324 325 326 327 328 329 330 331
FeatureSet IWizardFactory::pluginFeatures() const
{
    static FeatureSet plugins;
    if (plugins.isEmpty()) {
        QStringList list;
        // Implicitly create a feature for each plugin loaded:
        foreach (ExtensionSystem::PluginSpec *s, ExtensionSystem::PluginManager::plugins()) {
            if (s->state() == ExtensionSystem::PluginSpec::Running)
332
                list.append(s->name());
333 334 335 336 337
        }
        plugins = FeatureSet::fromStringList(list);
    }
    return plugins;
}
338

339 340
FeatureSet IWizardFactory::availableFeatures(const QString &platformName) const
{
341
    FeatureSet availableFeatures;
342 343 344 345 346 347 348

    foreach (const IFeatureProvider *featureManager, s_providerList)
        availableFeatures |= featureManager->availableFeatures(platformName);

    return availableFeatures;
}

349 350
void IWizardFactory::initialize()
{
351
    connect(ICore::instance(), &ICore::coreAboutToClose, &IWizardFactory::clearWizardFactories);
352 353 354 355 356

    auto resetAction = new QAction(tr("Reload All Wizards"), ActionManager::instance());
    ActionManager::registerAction(resetAction, "Wizard.Factory.Reset");

    connect(resetAction, &QAction::triggered, &IWizardFactory::clearWizardFactories);
357
}