qtquickapp.cpp 16.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** 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
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

30
#include "qtquickapp.h"
31

32
#include <utils/qtcassert.h>
33
#include <utils/fileutils.h>
34
35
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
36
37

#include <QDebug>
38
39
40
#include <QDir>
#include <QFile>
#include <QTextStream>
41

42
43
44
45
#ifndef CREATORLESSTEST
#include <coreplugin/icore.h>
#endif // CREATORLESSTEST

46
namespace QmakeProjectManager {
47
48
namespace Internal {

jkobus's avatar
jkobus committed
49
50
51
52
53
static QString sharedDirectory()
{
    return Core::ICore::resourcePath() + QLatin1String("/templates/shared/");
}

54
55
static QString qtQuickApplicationViewerDirectory()
{
jkobus's avatar
jkobus committed
56
    return sharedDirectory() + QLatin1String("qtquickapplicationviewer/");
57
58
}

59
static QString templateRootDirectory()
60
{
61
    return Core::ICore::resourcePath() + QLatin1String("/templates/qtquick/");
62
63
}

64
static QStringList templateNames()
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
65
{
66
67
68
69
70
71
72
73
    QStringList templateNameList;
    const QDir templateRoot(templateRootDirectory());

    foreach (const QFileInfo &subDirectory,
             templateRoot.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
        templateNameList.append(subDirectory.fileName());

    return templateNameList;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
74
75
}

76
77
// Return locale language attribute "de_UTF8" -> "de", empty string for "C"
static QString languageSetting()
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
78
{
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#ifdef QT_CREATOR
    QString name = Core::ICore::userInterfaceLanguage();
    const int underScorePos = name.indexOf(QLatin1Char('_'));
    if (underScorePos != -1)
        name.truncate(underScorePos);
    if (name.compare(QLatin1String("C"), Qt::CaseInsensitive) == 0)
        name.clear();
    return name;
#else
    return QLocale::system().name();
#endif
}

static inline bool assignLanguageElementText(QXmlStreamReader &reader,
                                             const QString &desiredLanguage,
                                             QString *target)
{
    const QStringRef elementLanguage = reader.attributes().value(QLatin1String("xml:lang"));
    if (elementLanguage.isEmpty()) {
        // Try to find a translation for our Wizards
        *target = QCoreApplication::translate("QmakeProjectManager::QtQuickAppWizard",
                    reader.readElementText().toLatin1().constData());
        return true;
    }
    if (elementLanguage == desiredLanguage) {
        *target = reader.readElementText();
        return true;
    }
    return false;
}

static bool parseTemplateXml(QXmlStreamReader &reader, TemplateInfo *info)
{
    const QString locale = languageSetting();

    static const QLatin1String tag_template("template");
    static const QLatin1String tag_displayName("displayname");
    static const QLatin1String tag_description("description");
    static const QLatin1String attribute_featuresRequired("featuresRequired");
    static const QLatin1String attribute_openEditor("openeditor");
    static const QLatin1String attribute_priority("priority");
    static const QLatin1String attribute_viewerdir("viewerdir");
    static const QLatin1String attribute_viewerclassname("viewerclassname");
jkobus's avatar
jkobus committed
122
    static const QLatin1String attribute_qrcdeployment("qrcdeployment");
123
    static const QLatin1String attribute_stubversionminor("stubversionminor");
124
    static const QLatin1String attribute_requiredPlugins("requiredPlugins");
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

    while (!reader.atEnd() && !reader.hasError()) {
        reader.readNext();
        if (reader.tokenType() != QXmlStreamReader::StartElement)
            continue;

        if (reader.name() == tag_template) {
            info->openFile = reader.attributes().value(attribute_openEditor).toString();
            if (reader.attributes().hasAttribute(attribute_priority))
                info->priority = reader.attributes().value(attribute_priority).toString();

            if (reader.attributes().hasAttribute(attribute_featuresRequired))
                info->featuresRequired = reader.attributes().value(attribute_featuresRequired).toString();

            if (reader.attributes().hasAttribute(attribute_viewerdir))
                info->viewerDir = reader.attributes().value(attribute_viewerdir).toString();

            if (reader.attributes().hasAttribute(attribute_viewerclassname))
                info->viewerClassName = reader.attributes().value(attribute_viewerclassname).toString();

jkobus's avatar
jkobus committed
145
146
147
            if (reader.attributes().hasAttribute(attribute_qrcdeployment))
                info->qrcDeployment = reader.attributes().value(attribute_qrcdeployment).toString();

148
149
150
            if (reader.attributes().hasAttribute(attribute_stubversionminor))
                info->stubVersionMinor = reader.attributes().value(attribute_stubversionminor).toString().toInt();

151
152
153
154
155
156
            // This attribute is currently used in enterprise addons to filter out templates when the enterprise
            // addon is not installed. This applies to the Boot To Qt addon for example.
            if (reader.attributes().hasAttribute(attribute_requiredPlugins))
                info->requiredPlugins = reader.attributes().value(attribute_requiredPlugins).toString()
                    .split(QLatin1Char(','), QString::SkipEmptyParts);

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
        } else if (reader.name() == tag_displayName) {
            if (!assignLanguageElementText(reader, locale, &info->displayName))
                continue;
        } else if (reader.name() == tag_description) {
            if (!assignLanguageElementText(reader, locale, &info->description))
                continue;
        }
    }
    if (reader.hasError()) {
        qWarning() << reader.errorString();
        return false;
    }

    return true;
}

class TemplateInfoList
{
public:
    TemplateInfoList()
    {
178
179
180
181
182
183
        QSet<QString> availablePlugins;
        foreach (ExtensionSystem::PluginSpec *s, ExtensionSystem::PluginManager::plugins()) {
            if (s->state() == ExtensionSystem::PluginSpec::Running && !s->hasError())
                availablePlugins += s->name();
        }

184
185
186
187
188
189
190
191
192
193
194
195
        QMultiMap<QString, TemplateInfo> multiMap;
        foreach (const QString &templateName, templateNames()) {
            const QString templatePath = templateRootDirectory() + templateName;
            QFile xmlFile(templatePath + QLatin1String("/template.xml"));
            if (!xmlFile.open(QIODevice::ReadOnly)) {
                qWarning().nospace() << QString::fromLatin1("Cannot open %1").arg(QDir::toNativeSeparators(QFileInfo(xmlFile.fileName()).absoluteFilePath()));
                continue;
            }
            TemplateInfo info;
            info.templateName = templateName;
            info.templatePath = templatePath;
            QXmlStreamReader reader(&xmlFile);
196
197
198
199
200
201
202
203
204
205
206
            if (!parseTemplateXml(reader, &info))
                continue;

            bool ok = true;
            foreach (const QString &neededPlugin, info.requiredPlugins) {
                if (!availablePlugins.contains(neededPlugin)) {
                    ok = false;
                    break;
                }
            }
            if (ok)
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
                multiMap.insert(info.priority, info);
        }
        m_templateInfoList = multiMap.values();
    }
    QList<TemplateInfo> templateInfoList() const { return m_templateInfoList; }

private:
    QList<TemplateInfo> m_templateInfoList;
};

Q_GLOBAL_STATIC(TemplateInfoList, templateInfoList)

QList<TemplateInfo> QtQuickApp::templateInfos()
{
    return templateInfoList()->templateInfoList();
}

QtQuickApp::QtQuickApp()
    : AbstractMobileApp()
{
}

void QtQuickApp::setTemplateInfo(const TemplateInfo &templateInfo)
{
    m_templateInfo = templateInfo;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
232
233
}

234
QString QtQuickApp::pathExtended(int fileType) const
235
{
236
    const QString appViewerTargetSubDir = appViewerOriginSubDir();
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
237
238

    const QString mainQmlFile = QLatin1String("main.qml");
239
    const QString mainQrcFile = QLatin1String("qml.qrc");
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
240

jkobus's avatar
jkobus committed
241
242
    const QString qrcDeploymentFile = QLatin1String("deployment.pri");

243
    const QString pathBase = outputPathBase();
244

245
    switch (fileType) {
246
247
        case MainQml:                       return pathBase + mainQmlFile;
        case MainQmlOrigin:                 return originsRoot() + mainQmlFile;
248
249
        case MainQrc:                       return pathBase + mainQrcFile;
        case MainQrcOrigin:                 return originsRoot() + mainQrcFile;
jkobus's avatar
jkobus committed
250
251
        case QrcDeployment:                 return pathBase + qrcDeploymentFile;
        case QrcDeploymentOrigin:           return sharedDirectory() + qrcDeployment();
252
        case AppViewerPri:                  return pathBase + appViewerTargetSubDir + fileName(AppViewerPri);
253
        case AppViewerPriOrigin:            return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerPri);
254
        case AppViewerCpp:                  return pathBase + appViewerTargetSubDir + fileName(AppViewerCpp);
255
        case AppViewerCppOrigin:            return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerCpp);
256
        case AppViewerH:                    return pathBase + appViewerTargetSubDir + fileName(AppViewerH);
257
        case AppViewerHOrigin:              return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerH);
258
        default:                            qFatal("QtQuickApp::pathExtended() needs more work");
259
260
261
262
    }
    return QString();
}

263
QString QtQuickApp::originsRoot() const
264
{
265
    return m_templateInfo.templatePath + QLatin1Char('/');
266
267
}

268
QString QtQuickApp::mainWindowClassName() const
269
{
270
    return m_templateInfo.viewerClassName;
271
272
}

273
bool QtQuickApp::adaptCurrentMainCppTemplateLine(QString &line) const
274
{
275
    Q_UNUSED(line)
276
    return true;
277
278
}

279
void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line,
280
    QTextStream &proFileTemplate, QTextStream &proFile,
281
    bool &commentOutNextLine) const
282
{
Alessandro Portale's avatar
Alessandro Portale committed
283
284
    Q_UNUSED(commentOutNextLine)
    if (line.contains(QLatin1String("# QML_IMPORT_PATH"))) {
285
286
287
        QString nextLine = proFileTemplate.readLine(); // eats 'QML_IMPORT_PATH ='
        if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH =")))
            return;
288
        proFile << nextLine << endl;
289
    }
290
291
}

292
#ifndef CREATORLESSTEST
293
Core::GeneratedFiles QtQuickApp::generateFiles(QString *errorMessage) const
294
{
295
    Core::GeneratedFiles files = AbstractMobileApp::generateFiles(errorMessage);
296
    if (!useExistingMainQml()) {
297
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQmlFile, errorMessage), path(MainQml)));
298
299
        files.last().setAttributes(Core::GeneratedFile::OpenEditorAttribute);
    }
300
301
302
    if (QFileInfo(path(MainQrcOrigin)).exists()) {
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQrcFile, errorMessage), path(MainQrc)));
    }
jkobus's avatar
jkobus committed
303
304
305
    if (!qrcDeployment().isEmpty()) {
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::QrcDeploymentFile, errorMessage), path(QrcDeployment)));
    }
306
307
308
309
310
    if (!appViewerBaseName().isEmpty()) {
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerPriFile, errorMessage), path(AppViewerPri)));
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerCppFile, errorMessage), path(AppViewerCpp)));
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerHFile, errorMessage), path(AppViewerH)));
    }
311
312
313
314
315

    return files;
}
#endif // CREATORLESSTEST

316
bool QtQuickApp::useExistingMainQml() const
317
318
319
320
{
    return !m_mainQmlFile.filePath().isEmpty();
}

321
322
QString QtQuickApp::appViewerBaseName() const
{
323
    return m_templateInfo.viewerDir;
324
325
}

jkobus's avatar
jkobus committed
326
327
328
329
330
QString QtQuickApp::qrcDeployment() const
{
    return m_templateInfo.qrcDeployment;
}

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
QString QtQuickApp::fileName(QtQuickApp::ExtendedFileType type) const
{
    switch (type) {
        case AppViewerPri:      return appViewerBaseName() + QLatin1String(".pri");
        case AppViewerH:        return appViewerBaseName() + QLatin1String(".h");
        case AppViewerCpp:      return appViewerBaseName() + QLatin1String(".cpp");
        default:                return QString();
    }
}

QString QtQuickApp::appViewerOriginSubDir() const
{
    return appViewerBaseName() + QLatin1Char('/');
}

346
347
348
349
QByteArray QtQuickApp::generateProFile(QString *errorMessage) const
{
    QByteArray proFileContent = AbstractMobileApp::generateProFile(errorMessage);
    proFileContent.replace("../../shared/qtquickapplicationviewer/", "");
jkobus's avatar
jkobus committed
350
    proFileContent.replace("../../shared/qrc", ""); // fix a path to qrcdeployment.pri
351
352
353
    return proFileContent;
}

354
QByteArray QtQuickApp::generateFileExtended(int fileType,
355
    bool *versionAndCheckSum, QString *comment, QString *errorMessage) const
356
357
{
    QByteArray data;
358
    switch (fileType) {
359
        case QtQuickAppGeneratedFileInfo::MainQmlFile:
360
            data = readBlob(path(MainQmlOrigin), errorMessage);
361
            break;
362
363
364
        case QtQuickAppGeneratedFileInfo::MainQrcFile:
            data = readBlob(path(MainQrcOrigin), errorMessage);
            break;
jkobus's avatar
jkobus committed
365
366
367
        case QtQuickAppGeneratedFileInfo::QrcDeploymentFile:
            data = readBlob(path(QrcDeploymentOrigin), errorMessage);
            break;
368
        case QtQuickAppGeneratedFileInfo::AppViewerPriFile:
369
370
371
            data = readBlob(path(AppViewerPriOrigin), errorMessage);
            *comment = ProFileComment;
            *versionAndCheckSum = true;
372
            break;
373
        case QtQuickAppGeneratedFileInfo::AppViewerCppFile:
374
375
            data = readBlob(path(AppViewerCppOrigin), errorMessage);
            *versionAndCheckSum = true;
376
            break;
377
        case QtQuickAppGeneratedFileInfo::AppViewerHFile:
378
        default:
379
380
            data = readBlob(path(AppViewerHOrigin), errorMessage);
            *versionAndCheckSum = true;
381
382
            break;
    }
383
    return data;
384
385
}

386
int QtQuickApp::stubVersionMinor() const
387
{
388
    return m_templateInfo.stubVersionMinor;
389
390
}

391
QList<AbstractGeneratedFileInfo> QtQuickApp::updateableFiles(const QString &mainProFile) const
392
{
393
    QList<AbstractGeneratedFileInfo> result;
394
    static const struct {
395
        int fileType;
396
397
        QString fileName;
    } files[] = {
398
399
400
        {QtQuickAppGeneratedFileInfo::AppViewerPriFile, fileName(AppViewerPri)},
        {QtQuickAppGeneratedFileInfo::AppViewerHFile, fileName(AppViewerH)},
        {QtQuickAppGeneratedFileInfo::AppViewerCppFile, fileName(AppViewerCpp)}
401
402
    };
    const QFileInfo mainProFileInfo(mainProFile);
403
404
    const int size = sizeof(files) / sizeof(files[0]);
    for (int i = 0; i < size; ++i) {
405
        const QString fileName = mainProFileInfo.dir().absolutePath()
406
                + QLatin1Char('/') + appViewerOriginSubDir() + files[i].fileName;
407
408
        if (!QFile::exists(fileName))
            continue;
409
        QtQuickAppGeneratedFileInfo file;
410
        file.fileType = files[i].fileType;
411
        file.fileInfo = QFileInfo(fileName);
412
        file.currentVersion = AbstractMobileApp::makeStubVersion(stubVersionMinor());
413
414
        result.append(file);
    }
415
416
    if (result.count() != size)
        result.clear(); // All files must be found. No wrong/partial updates, please.
417
418
419
    return result;
}

420
421
QList<DeploymentFolder> QtQuickApp::deploymentFolders() const
{
422
    return QList<DeploymentFolder>();
423
424
}

425
} // namespace Internal
426
} // namespace QmakeProjectManager