OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / wizards / qtquickapp.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "qtquickapp.h"
34
35 #include <QtCore/QDir>
36 #include <QtCore/QFile>
37 #include <QtCore/QRegExp>
38 #include <QtCore/QTextStream>
39 #include <QtCore/QCoreApplication>
40
41 #ifndef CREATORLESSTEST
42 #include <coreplugin/icore.h>
43 #endif // CREATORLESSTEST
44
45 namespace Qt4ProjectManager {
46 namespace Internal {
47
48 const QString qmldir(QLatin1String("qmldir"));
49 const QString qmldir_plugin(QLatin1String("plugin"));
50 const QString appViewerBaseName(QLatin1String("qmlapplicationviewer"));
51 const QString appViewerPriFileName(appViewerBaseName + QLatin1String(".pri"));
52 const QString appViewerCppFileName(appViewerBaseName + QLatin1String(".cpp"));
53 const QString appViewerHFileName(appViewerBaseName + QLatin1String(".h"));
54 const QString appViewerOriginsSubDir(appViewerBaseName + QLatin1Char('/'));
55
56 QmlModule::QmlModule(const QString &uri, const QFileInfo &rootDir, const QFileInfo &qmldir,
57                      bool isExternal, QtQuickApp *qtQuickApp)
58     : uri(uri)
59     , rootDir(rootDir)
60     , qmldir(qmldir)
61     , isExternal(isExternal)
62     , qtQuickApp(qtQuickApp)
63 {}
64
65 QString QmlModule::path(Path path) const
66 {
67     switch (path) {
68         case Root: {
69             return rootDir.canonicalFilePath();
70         }
71         case ContentDir: {
72             const QDir proFile(qtQuickApp->path(QtQuickApp::AppProPath));
73             return proFile.relativeFilePath(qmldir.canonicalPath());
74         }
75         case ContentBase: {
76             const QString localRoot = rootDir.canonicalFilePath() + QLatin1Char('/');
77             QDir contentDir = qmldir.dir();
78             contentDir.cdUp();
79             const QString localContentDir = contentDir.canonicalPath();
80             return localContentDir.right(localContentDir.length() - localRoot.length());
81         }
82         case DeployedContentBase: {
83             const QString modulesDir = qtQuickApp->path(QtQuickApp::ModulesDir);
84             return modulesDir + QLatin1Char('/') + this->path(ContentBase);
85         }
86         default: qFatal("QmlModule::path() needs more work");
87     }
88     return QString();
89 }
90
91 QmlCppPlugin::QmlCppPlugin(const QString &name, const QFileInfo &path,
92                            const QmlModule *module, const QFileInfo &proFile)
93     : name(name)
94     , path(path)
95     , module(module)
96     , proFile(proFile)
97 {
98 }
99
100 QtQuickApp::QtQuickApp()
101     : AbstractMobileApp()
102     , m_mainQmlMode(ModeGenerate)
103 {
104 }
105
106 QtQuickApp::~QtQuickApp()
107 {
108     clearModulesAndPlugins();
109 }
110
111 void QtQuickApp::setMainQml(Mode mode, const QString &file)
112 {
113     Q_ASSERT(mode != ModeGenerate || file.isEmpty());
114     m_mainQmlMode = mode;
115     m_mainQmlFile.setFile(file);
116 }
117
118 QtQuickApp::Mode QtQuickApp::mainQmlMode() const
119 {
120     return m_mainQmlMode;
121 }
122
123 bool QtQuickApp::setExternalModules(const QStringList &uris,
124                                     const QStringList &importPaths)
125 {
126     clearModulesAndPlugins();
127     m_importPaths.clear();
128     foreach (const QFileInfo &importPath, importPaths) {
129         if (!importPath.exists()) {
130             m_error = QCoreApplication::translate(
131                         "Qt4ProjectManager::Internal::QtQuickApp",
132                         "The QML import path '%1' cannot be found.")
133                       .arg(QDir::toNativeSeparators(importPath.filePath()));
134             return false;
135         } else {
136             m_importPaths.append(importPath.canonicalFilePath());
137         }
138     }
139     foreach (const QString &uri, uris) {
140         QString uriPath = uri;
141         uriPath.replace(QLatin1Char('.'), QLatin1Char('/'));
142         const int modulesCount = m_modules.count();
143         foreach (const QFileInfo &importPath, m_importPaths) {
144             const QFileInfo qmlDirFile(
145                     importPath.absoluteFilePath() + QLatin1Char('/')
146                     + uriPath + QLatin1Char('/') + qmldir);
147             if (qmlDirFile.exists()) {
148                 if (!addExternalModule(uri, importPath, qmlDirFile))
149                     return false;
150                 break;
151             }
152         }
153         if (modulesCount == m_modules.count()) { // no module was added
154             m_error = QCoreApplication::translate(
155                       "Qt4ProjectManager::Internal::QtQuickApp",
156                       "The QML module '%1' cannot be found.").arg(uri);
157             return false;
158         }
159     }
160     m_error.clear();
161     return true;
162 }
163
164 QString QtQuickApp::pathExtended(int fileType) const
165 {
166     QString cleanProjectName = projectName().replace(QLatin1Char('-'), QString());
167     const bool importQmlFile = m_mainQmlMode == ModeImport;
168     const QString qmlSubDir = QLatin1String("qml/")
169                               + (importQmlFile ? m_mainQmlFile.dir().dirName() : cleanProjectName)
170                               + QLatin1Char('/');
171     const QString appViewerTargetSubDir = appViewerOriginsSubDir;
172     const QString mainQml = QLatin1String("main.qml");
173     const QString pathBase = outputPathBase();
174     const QDir appProFilePath(pathBase);
175
176     switch (fileType) {
177         case MainQml:                       return importQmlFile ? m_mainQmlFile.canonicalFilePath()
178                                                                  : pathBase + qmlSubDir + mainQml;
179         case MainQmlDeployed:               return importQmlFile ? qmlSubDir + m_mainQmlFile.fileName()
180                                                                  : QString(qmlSubDir + mainQml);
181         case MainQmlOrigin:                 return originsRoot() + QLatin1String("qml/app/") + mainQml;
182         case AppViewerPri:                  return pathBase + appViewerTargetSubDir + appViewerPriFileName;
183         case AppViewerPriOrigin:            return originsRoot() + appViewerOriginsSubDir + appViewerPriFileName;
184         case AppViewerCpp:                  return pathBase + appViewerTargetSubDir + appViewerCppFileName;
185         case AppViewerCppOrigin:            return originsRoot() + appViewerOriginsSubDir + appViewerCppFileName;
186         case AppViewerH:                    return pathBase + appViewerTargetSubDir + appViewerHFileName;
187         case AppViewerHOrigin:              return originsRoot() + appViewerOriginsSubDir + appViewerHFileName;
188         case QmlDir:                        return pathBase + qmlSubDir;
189         case QmlDirProFileRelative:         return importQmlFile ? appProFilePath.relativeFilePath(m_mainQmlFile.canonicalPath())
190                                                                  : QString(qmlSubDir).remove(qmlSubDir.length() - 1, 1);
191         case ModulesDir:                    return QLatin1String("modules");
192         default:                            qFatal("QtQuickApp::pathExtended() needs more work");
193     }
194     return QString();
195 }
196
197 QString QtQuickApp::originsRoot() const
198 {
199     return templatesRoot() + QLatin1String("qtquickapp/");
200 }
201
202 QString QtQuickApp::mainWindowClassName() const
203 {
204     return QLatin1String("QmlApplicationViewer");
205 }
206
207 bool QtQuickApp::adaptCurrentMainCppTemplateLine(QString &line) const
208 {
209     const QLatin1Char quote('"');
210     bool adaptLine = true;
211     if (line.contains(QLatin1String("// MAINQML"))) {
212         insertParameter(line, quote + path(MainQmlDeployed) + quote);
213     } else if (line.contains(QLatin1String("// ADDIMPORTPATH"))) {
214         if (m_modules.isEmpty())
215             adaptLine = false;
216         else
217             insertParameter(line, quote + path(ModulesDir) + quote);
218     }
219     return adaptLine;
220 }
221
222 void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line,
223     QTextStream &proFileTemplate, QTextStream &proFile,
224     bool &commentOutNextLine) const
225 {
226     Q_UNUSED(commentOutNextLine)
227     if (line.contains(QLatin1String("# QML_IMPORT_PATH"))) {
228         QString nextLine = proFileTemplate.readLine(); // eats 'QML_IMPORT_PATH ='
229         if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH =")))
230             return;
231
232         proFile << nextLine;
233
234         const QLatin1String separator(" \\\n    ");
235         const QDir proPath(path(AppProPath));
236         foreach (const QString &importPath, m_importPaths) {
237             const QString relativePath = proPath.relativeFilePath(importPath);
238             proFile << separator << relativePath;
239         }
240
241         proFile << endl;
242     }
243 }
244
245 void QtQuickApp::clearModulesAndPlugins()
246 {
247     qDeleteAll(m_modules);
248     m_modules.clear();
249     qDeleteAll(m_cppPlugins);
250     m_cppPlugins.clear();
251 }
252
253 bool QtQuickApp::addCppPlugin(const QString &qmldirLine, QmlModule *module)
254 {
255     const QStringList qmldirLineElements =
256             qmldirLine.split(QLatin1Char(' '), QString::SkipEmptyParts);
257     if (qmldirLineElements.count() < 2) {
258         m_error = QCoreApplication::translate(
259                       "Qt4ProjectManager::Internal::QtQuickApp",
260                       "Invalid '%1' entry in '%2' of module '%3'.")
261                   .arg(qmldir_plugin).arg(qmldir).arg(module->uri);
262         return false;
263     }
264     const QString name = qmldirLineElements.at(1);
265     const QFileInfo path(module->qmldir.dir(), qmldirLineElements.value(2, QString()));
266
267     // TODO: Add more magic to find a good .pro file..
268     const QString proFileName = name + QLatin1String(".pro");
269     const QFileInfo proFile_guess1(module->qmldir.dir(), proFileName);
270     const QFileInfo proFile_guess2(QString(module->qmldir.dir().absolutePath() + QLatin1String("/../")),
271                                    proFileName);
272     const QFileInfo proFile_guess3(module->qmldir.dir(),
273                                    QFileInfo(module->qmldir.path()).fileName() + QLatin1String(".pro"));
274     const QFileInfo proFile_guess4(proFile_guess3.absolutePath() + QLatin1String("/../")
275                                    + proFile_guess3.fileName());
276
277     QFileInfo foundProFile;
278     if (proFile_guess1.exists()) {
279         foundProFile = proFile_guess1.canonicalFilePath();
280     } else if (proFile_guess2.exists()) {
281         foundProFile = proFile_guess2.canonicalFilePath();
282     } else if (proFile_guess3.exists()) {
283         foundProFile = proFile_guess3.canonicalFilePath();
284     } else if (proFile_guess4.exists()) {
285         foundProFile = proFile_guess4.canonicalFilePath();
286     } else {
287         m_error = QCoreApplication::translate(
288                     "Qt4ProjectManager::Internal::QtQuickApp",
289                     "No .pro file for plugin '%1' cannot be found.").arg(name);
290         return false;
291     }
292     QmlCppPlugin *plugin =
293             new QmlCppPlugin(name, path, module, foundProFile);
294     m_cppPlugins.append(plugin);
295     module->cppPlugins.insert(name, plugin);
296     return true;
297 }
298
299 bool QtQuickApp::addCppPlugins(QmlModule *module)
300 {
301     QFile qmlDirFile(module->qmldir.absoluteFilePath());
302     if (qmlDirFile.open(QIODevice::ReadOnly)) {
303         QTextStream in(&qmlDirFile);
304         QString line;
305         while (!(line = in.readLine()).isNull()) {
306             line = line.trimmed();
307             if (line.startsWith(qmldir_plugin) && !addCppPlugin(line, module))
308                 return false;
309         };
310     }
311     return true;
312 }
313
314 bool QtQuickApp::addExternalModule(const QString &name, const QFileInfo &dir,
315                                          const QFileInfo &contentDir)
316 {
317     QmlModule *module = new QmlModule(name, dir, contentDir, true, this);
318     m_modules.append(module);
319     return addCppPlugins(module);
320 }
321
322 #ifndef CREATORLESSTEST
323 Core::GeneratedFiles QtQuickApp::generateFiles(QString *errorMessage) const
324 {
325     Core::GeneratedFiles files = AbstractMobileApp::generateFiles(errorMessage);
326     if (!useExistingMainQml()) {
327         files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQmlFile, errorMessage), path(MainQml)));
328         files.last().setAttributes(Core::GeneratedFile::OpenEditorAttribute);
329     }
330
331     files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerPriFile, errorMessage), path(AppViewerPri)));
332     files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerCppFile, errorMessage), path(AppViewerCpp)));
333     files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerHFile, errorMessage), path(AppViewerH)));
334
335     return files;
336 }
337 #endif // CREATORLESSTEST
338
339 bool QtQuickApp::useExistingMainQml() const
340 {
341     return !m_mainQmlFile.filePath().isEmpty();
342 }
343
344 const QList<QmlModule*> QtQuickApp::modules() const
345 {
346     return m_modules;
347 }
348
349 QByteArray QtQuickApp::generateFileExtended(int fileType,
350     bool *versionAndCheckSum, QString *comment, QString *errorMessage) const
351 {
352     QByteArray data;
353     switch (fileType) {
354         case QtQuickAppGeneratedFileInfo::MainQmlFile:
355             data = readBlob(path(MainQmlOrigin), errorMessage);
356             break;
357         case QtQuickAppGeneratedFileInfo::AppViewerPriFile:
358             data = readBlob(path(AppViewerPriOrigin), errorMessage);
359             data.append(readBlob(path(DeploymentPriOrigin), errorMessage));
360             *comment = ProFileComment;
361             *versionAndCheckSum = true;
362             break;
363         case QtQuickAppGeneratedFileInfo::AppViewerCppFile:
364             data = readBlob(path(AppViewerCppOrigin), errorMessage);
365             *versionAndCheckSum = true;
366             break;
367         case QtQuickAppGeneratedFileInfo::AppViewerHFile:
368         default:
369             data = readBlob(path(AppViewerHOrigin), errorMessage);
370             *versionAndCheckSum = true;
371             break;
372     }
373     return data;
374 }
375
376 int QtQuickApp::stubVersionMinor() const
377 {
378     return StubVersion;
379 }
380
381 QList<AbstractGeneratedFileInfo> QtQuickApp::updateableFiles(const QString &mainProFile) const
382 {
383     QList<AbstractGeneratedFileInfo> result;
384     static const struct {
385         int fileType;
386         QString fileName;
387     } files[] = {
388         {QtQuickAppGeneratedFileInfo::AppViewerPriFile, appViewerPriFileName},
389         {QtQuickAppGeneratedFileInfo::AppViewerHFile, appViewerHFileName},
390         {QtQuickAppGeneratedFileInfo::AppViewerCppFile, appViewerCppFileName}
391     };
392     const QFileInfo mainProFileInfo(mainProFile);
393     const int size = sizeof(files) / sizeof(files[0]);
394     for (int i = 0; i < size; ++i) {
395         const QString fileName = mainProFileInfo.dir().absolutePath()
396                 + QLatin1Char('/') + appViewerOriginsSubDir + files[i].fileName;
397         if (!QFile::exists(fileName))
398             continue;
399         QtQuickAppGeneratedFileInfo file;
400         file.fileType = files[i].fileType;
401         file.fileInfo = QFileInfo(fileName);
402         file.currentVersion = AbstractMobileApp::makeStubVersion(QtQuickApp::StubVersion);
403         result.append(file);
404     }
405     if (result.count() != size)
406         result.clear(); // All files must be found. No wrong/partial updates, please.
407     return result;
408 }
409
410 QList<DeploymentFolder> QtQuickApp::deploymentFolders() const
411 {
412     QList<DeploymentFolder> result;
413     result.append(DeploymentFolder(path(QmlDirProFileRelative), QLatin1String("qml")));
414     foreach (const QmlModule *module, m_modules)
415         if (module->isExternal)
416             result.append(DeploymentFolder(module->path(QmlModule::ContentDir), module->path(QmlModule::DeployedContentBase)));
417     return result;
418 }
419
420 const int QtQuickApp::StubVersion = 11;
421
422 } // namespace Internal
423 } // namespace Qt4ProjectManager