qtquickapp.cpp 15.5 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 <QDebug>
36 37 38
#include <QDir>
#include <QFile>
#include <QTextStream>
39

40 41 42 43
#ifndef CREATORLESSTEST
#include <coreplugin/icore.h>
#endif // CREATORLESSTEST

44
namespace QmakeProjectManager {
45 46
namespace Internal {

47 48 49 50 51
static QString sharedDirectory()
{
    return Core::ICore::resourcePath() + QLatin1String("/templates/shared/");
}

52 53
static QString qtQuickApplicationViewerDirectory()
{
54
    return sharedDirectory() + QLatin1String("qtquickapplicationviewer/");
55 56
}

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

62
static QStringList templateNames()
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
63
{
64 65 66 67 68 69 70 71
    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
72 73
}

74 75
// Return locale language attribute "de_UTF8" -> "de", empty string for "C"
static QString languageSetting()
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
76
{
77 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
#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");
120
    static const QLatin1String attribute_qrcdeployment("qrcdeployment");
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    static const QLatin1String attribute_stubversionminor("stubversionminor");

    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();

142 143 144
            if (reader.attributes().hasAttribute(attribute_qrcdeployment))
                info->qrcDeployment = reader.attributes().value(attribute_qrcdeployment).toString();

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
            if (reader.attributes().hasAttribute(attribute_stubversionminor))
                info->stubVersionMinor = reader.attributes().value(attribute_stubversionminor).toString().toInt();

        } 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()
    {
        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);
            if (parseTemplateXml(reader, &info))
                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
207 208
}

209
QString QtQuickApp::pathExtended(int fileType) const
210
{
211
    const QString qmlSubDir = QLatin1String("qml/");
212
    const QString appViewerTargetSubDir = appViewerOriginSubDir();
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
213 214

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

217 218
    const QString qrcDeploymentFile = QLatin1String("deployment.pri");

219
    const QString qmlOriginDir = originsRoot() + QLatin1String("qml/");
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
220

221
    const QString pathBase = outputPathBase();
222

223
    switch (fileType) {
224 225
        case MainQml:                       return pathBase + qmlSubDir + mainQmlFile;
        case MainQmlDeployed:               return qmlSubDir + mainQmlFile;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
226
        case MainQmlOrigin:                 return qmlOriginDir + mainQmlFile;
227 228
        case MainQrc:                       return pathBase + mainQrcFile;
        case MainQrcOrigin:                 return originsRoot() + mainQrcFile;
229 230
        case QrcDeployment:                 return pathBase + qrcDeploymentFile;
        case QrcDeploymentOrigin:           return sharedDirectory() + qrcDeployment();
231
        case AppViewerPri:                  return pathBase + appViewerTargetSubDir + fileName(AppViewerPri);
232
        case AppViewerPriOrigin:            return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerPri);
233
        case AppViewerCpp:                  return pathBase + appViewerTargetSubDir + fileName(AppViewerCpp);
234
        case AppViewerCppOrigin:            return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerCpp);
235
        case AppViewerH:                    return pathBase + appViewerTargetSubDir + fileName(AppViewerH);
236
        case AppViewerHOrigin:              return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerH);
237
        case QmlDirProFileRelative:         return QString(qmlSubDir).remove(qmlSubDir.length() - 1, 1);
238
        default:                            qFatal("QtQuickApp::pathExtended() needs more work");
239 240 241 242
    }
    return QString();
}

243
QString QtQuickApp::originsRoot() const
244
{
245
    return m_templateInfo.templatePath + QLatin1Char('/');
246 247
}

248
QString QtQuickApp::mainWindowClassName() const
249
{
250
    return m_templateInfo.viewerClassName;
251 252
}

253
bool QtQuickApp::adaptCurrentMainCppTemplateLine(QString &line) const
254
{
255
    const QLatin1Char quote('"');
256

257
    if (line.contains(QLatin1String("// MAINQML")))
258
        insertParameter(line, quote + path(MainQmlDeployed) + quote);
259

260
    return true;
261 262
}

263
void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line,
264
    QTextStream &proFileTemplate, QTextStream &proFile,
265
    bool &commentOutNextLine) const
266
{
Alessandro Portale's avatar
Alessandro Portale committed
267 268
    Q_UNUSED(commentOutNextLine)
    if (line.contains(QLatin1String("# QML_IMPORT_PATH"))) {
269 270 271
        QString nextLine = proFileTemplate.readLine(); // eats 'QML_IMPORT_PATH ='
        if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH =")))
            return;
272
        proFile << nextLine << endl;
273
    }
274 275
}

276
#ifndef CREATORLESSTEST
277
Core::GeneratedFiles QtQuickApp::generateFiles(QString *errorMessage) const
278
{
279
    Core::GeneratedFiles files = AbstractMobileApp::generateFiles(errorMessage);
280
    if (!useExistingMainQml()) {
281
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQmlFile, errorMessage), path(MainQml)));
282 283
        files.last().setAttributes(Core::GeneratedFile::OpenEditorAttribute);
    }
284 285 286
    if (QFileInfo(path(MainQrcOrigin)).exists()) {
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQrcFile, errorMessage), path(MainQrc)));
    }
287 288 289
    if (!qrcDeployment().isEmpty()) {
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::QrcDeploymentFile, errorMessage), path(QrcDeployment)));
    }
290 291 292 293 294
    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)));
    }
295 296 297 298 299

    return files;
}
#endif // CREATORLESSTEST

300
bool QtQuickApp::useExistingMainQml() const
301 302 303 304
{
    return !m_mainQmlFile.filePath().isEmpty();
}

305 306
QString QtQuickApp::appViewerBaseName() const
{
307
    return m_templateInfo.viewerDir;
308 309
}

310 311 312 313 314
QString QtQuickApp::qrcDeployment() const
{
    return m_templateInfo.qrcDeployment;
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
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('/');
}

330 331 332 333
QByteArray QtQuickApp::generateProFile(QString *errorMessage) const
{
    QByteArray proFileContent = AbstractMobileApp::generateProFile(errorMessage);
    proFileContent.replace("../../shared/qtquickapplicationviewer/", "");
334
    proFileContent.replace("../../shared/qrc", ""); // fix a path to qrcdeployment.pri
335 336 337
    return proFileContent;
}

338
QByteArray QtQuickApp::generateFileExtended(int fileType,
339
    bool *versionAndCheckSum, QString *comment, QString *errorMessage) const
340 341
{
    QByteArray data;
342
    switch (fileType) {
343
        case QtQuickAppGeneratedFileInfo::MainQmlFile:
344
            data = readBlob(path(MainQmlOrigin), errorMessage);
345
            break;
346 347 348
        case QtQuickAppGeneratedFileInfo::MainQrcFile:
            data = readBlob(path(MainQrcOrigin), errorMessage);
            break;
349 350 351
        case QtQuickAppGeneratedFileInfo::QrcDeploymentFile:
            data = readBlob(path(QrcDeploymentOrigin), errorMessage);
            break;
352
        case QtQuickAppGeneratedFileInfo::AppViewerPriFile:
353
            data = readBlob(path(AppViewerPriOrigin), errorMessage);
354
            data.append(readBlob(path(DeploymentPriOrigin), errorMessage));
355 356
            *comment = ProFileComment;
            *versionAndCheckSum = true;
357
            break;
358
        case QtQuickAppGeneratedFileInfo::AppViewerCppFile:
359 360
            data = readBlob(path(AppViewerCppOrigin), errorMessage);
            *versionAndCheckSum = true;
361
            break;
362
        case QtQuickAppGeneratedFileInfo::AppViewerHFile:
363
        default:
364 365
            data = readBlob(path(AppViewerHOrigin), errorMessage);
            *versionAndCheckSum = true;
366 367
            break;
    }
368
    return data;
369 370
}

371
int QtQuickApp::stubVersionMinor() const
372
{
373
    return m_templateInfo.stubVersionMinor;
374 375
}

376
QList<AbstractGeneratedFileInfo> QtQuickApp::updateableFiles(const QString &mainProFile) const
377
{
378
    QList<AbstractGeneratedFileInfo> result;
379
    static const struct {
380
        int fileType;
381 382
        QString fileName;
    } files[] = {
383 384 385
        {QtQuickAppGeneratedFileInfo::AppViewerPriFile, fileName(AppViewerPri)},
        {QtQuickAppGeneratedFileInfo::AppViewerHFile, fileName(AppViewerH)},
        {QtQuickAppGeneratedFileInfo::AppViewerCppFile, fileName(AppViewerCpp)}
386 387
    };
    const QFileInfo mainProFileInfo(mainProFile);
388 389
    const int size = sizeof(files) / sizeof(files[0]);
    for (int i = 0; i < size; ++i) {
390
        const QString fileName = mainProFileInfo.dir().absolutePath()
391
                + QLatin1Char('/') + appViewerOriginSubDir() + files[i].fileName;
392 393
        if (!QFile::exists(fileName))
            continue;
394
        QtQuickAppGeneratedFileInfo file;
395
        file.fileType = files[i].fileType;
396
        file.fileInfo = QFileInfo(fileName);
397
        file.currentVersion = AbstractMobileApp::makeStubVersion(stubVersionMinor());
398 399
        result.append(file);
    }
400 401
    if (result.count() != size)
        result.clear(); // All files must be found. No wrong/partial updates, please.
402 403 404
    return result;
}

405 406 407
QList<DeploymentFolder> QtQuickApp::deploymentFolders() const
{
    QList<DeploymentFolder> result;
408
    result.append(DeploymentFolder(path(QmlDirProFileRelative), QLatin1String("")));
409 410 411
    return result;
}

412
} // namespace Internal
413
} // namespace QmakeProjectManager