OSDN Git Service

244bb36eed0a7a82019b59828d6937715f0e5b70
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / wizards / abstractmobileapp.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 (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "abstractmobileapp.h"
35
36 #include <QtCore/QCoreApplication>
37 #include <QtCore/QDir>
38 #include <QtCore/QFile>
39 #include <QtCore/QTextStream>
40
41 #ifndef CREATORLESSTEST
42 #include <coreplugin/icore.h>
43 #endif // CREATORLESSTEST
44
45 namespace Qt4ProjectManager {
46
47 AbstractGeneratedFileInfo::AbstractGeneratedFileInfo()
48     : fileType(ExtendedFile)
49     , currentVersion(-1)
50     , version(-1)
51     , dataChecksum(0)
52     , statedChecksum(0)
53 {
54 }
55
56 const QString AbstractMobileApp::CFileComment(QLatin1String("//"));
57 const QString AbstractMobileApp::ProFileComment(QLatin1String("#"));
58 const QString AbstractMobileApp::DeploymentPriFileName(QLatin1String("deployment.pri"));
59 const QString AbstractMobileApp::FileChecksum(QLatin1String("checksum"));
60 const QString AbstractMobileApp::FileStubVersion(QLatin1String("version"));
61 const int AbstractMobileApp::StubVersion = 4;
62
63 AbstractMobileApp::AbstractMobileApp()
64     : m_orientation(ScreenOrientationAuto)
65     , m_networkEnabled(true)
66 {
67 }
68
69 AbstractMobileApp::~AbstractMobileApp() { }
70
71 QString AbstractMobileApp::symbianUidForPath(const QString &path)
72 {
73     quint32 hash = 5381;
74     for (int i = 0; i < path.size(); ++i) {
75         const char c = path.at(i).toAscii();
76         hash ^= c + ((c - i) << i % 20) + ((c + i) << (i + 5) % 20) + ((c - 2 * i) << (i + 10) % 20) + ((c + 2 * i) << (i + 15) % 20);
77     }
78     return QString::fromLatin1("0xE")
79             + QString::fromLatin1("%1").arg(hash, 7, 16, QLatin1Char('0')).right(7).toUpper();
80 }
81
82 void AbstractMobileApp::setOrientation(ScreenOrientation orientation)
83 {
84     m_orientation = orientation;
85 }
86
87 AbstractMobileApp::ScreenOrientation AbstractMobileApp::orientation() const
88 {
89     return m_orientation;
90 }
91
92 void AbstractMobileApp::setProjectName(const QString &name)
93 {
94     m_projectName = name;
95 }
96
97 QString AbstractMobileApp::projectName() const
98 {
99     return m_projectName;
100 }
101
102 void AbstractMobileApp::setProjectPath(const QString &path)
103 {
104     m_projectPath.setFile(path);
105 }
106
107 void AbstractMobileApp::setSymbianSvgIcon(const QString &icon)
108 {
109     m_symbianSvgIcon = icon;
110 }
111
112 QString AbstractMobileApp::symbianSvgIcon() const
113 {
114     return path(SymbianSvgIconOrigin);
115 }
116
117 void AbstractMobileApp::setMaemoPngIcon(const QString &icon)
118 {
119     m_maemoPngIcon = icon;
120 }
121
122 QString AbstractMobileApp::maemoPngIcon() const
123 {
124     return path(MaemoPngIconOrigin);
125 }
126
127 void AbstractMobileApp::setSymbianTargetUid(const QString &uid)
128 {
129     m_symbianTargetUid = uid;
130 }
131
132 QString AbstractMobileApp::symbianTargetUid() const
133 {
134     return !m_symbianTargetUid.isEmpty() ? m_symbianTargetUid
135         : symbianUidForPath(path(AppPro));
136 }
137
138 void AbstractMobileApp::setNetworkEnabled(bool enabled)
139 {
140     m_networkEnabled = enabled;
141 }
142
143 bool AbstractMobileApp::networkEnabled() const
144 {
145     return m_networkEnabled;
146 }
147
148 QString AbstractMobileApp::path(int fileType) const
149 {
150     const QString originsRootApp = originsRoot();
151     const QString originsRootShared = templatesRoot() + QLatin1String("shared/");
152     const QString mainCppFileName = QLatin1String("main.cpp");
153     const QString symbianIconFileName = QLatin1String("symbianicon.svg");
154     QString cleanProjectName = m_projectName;
155     cleanProjectName.replace(QLatin1Char('-'), QString());
156     switch (fileType) {
157         case MainCpp:               return outputPathBase() + mainCppFileName;
158         case MainCppOrigin:         return originsRootApp + mainCppFileName;
159         case AppPro:                return outputPathBase() + cleanProjectName + QLatin1String(".pro");
160         case AppProOrigin:          return originsRootApp + QLatin1String("app.pro");
161         case AppProPath:            return outputPathBase();
162         case Desktop:               return outputPathBase() + cleanProjectName + QLatin1String(".desktop");
163         case DesktopOrigin:         return originsRootShared + QLatin1String("app.desktop");
164         case DeploymentPri:         return outputPathBase() + DeploymentPriFileName;
165         case DeploymentPriOrigin:   return originsRootShared + DeploymentPriFileName;
166         case SymbianSvgIcon:        return outputPathBase() + cleanProjectName + QLatin1String(".svg");
167         case SymbianSvgIconOrigin:  return !m_symbianSvgIcon.isEmpty() ? m_symbianSvgIcon
168                                         : originsRootShared + symbianIconFileName;
169         case MaemoPngIcon:          return outputPathBase() + cleanProjectName +  QLatin1String(".png");
170         case MaemoPngIconOrigin:    return !m_maemoPngIcon.isEmpty() ? m_maemoPngIcon
171                                         : originsRootShared + QLatin1String("maemoicon.png");
172         default:                    return pathExtended(fileType);
173     }
174     return QString();
175 }
176
177 QByteArray AbstractMobileApp::generateDesktopFile(QString *errorMessage) const
178 {
179     QFile desktopTemplate(path(DesktopOrigin));
180     if (!desktopTemplate.open(QIODevice::ReadOnly)) {
181         *errorMessage = QCoreApplication::translate("Qt4ProjectManager::AbstractMobileApp",
182             "Could not open desktop file template");
183         return QByteArray();
184     }
185     QByteArray desktopFileContent = desktopTemplate.readAll();
186     return desktopFileContent.replace("thisApp", projectName().toUtf8());
187 }
188
189 QByteArray AbstractMobileApp::generateMainCpp(QString *errorMessage) const
190 {
191     QFile sourceFile(path(MainCppOrigin));
192     if (!sourceFile.open(QIODevice::ReadOnly)) {
193         *errorMessage = QCoreApplication::translate("Qt4ProjectManager::AbstractMobileApp",
194                                                     "Could not open main.cpp template '%1'.")
195             .arg(sourceFile.fileName());
196         return QByteArray();
197     }
198     QTextStream in(&sourceFile);
199
200     QByteArray mainCppContent;
201     QTextStream out(&mainCppContent, QIODevice::WriteOnly);
202
203     QString line;
204     while (!(line = in.readLine()).isNull()) {
205         bool adaptLine = true;
206         if (line.contains(QLatin1String("// ORIENTATION"))) {
207             const char *orientationString;
208             switch (orientation()) {
209             case ScreenOrientationLockLandscape:
210                 orientationString = "ScreenOrientationLockLandscape";
211                 break;
212             case ScreenOrientationLockPortrait:
213                 orientationString = "ScreenOrientationLockPortrait";
214                 break;
215             case ScreenOrientationAuto:
216                 orientationString = "ScreenOrientationAuto";
217                 break;
218             }
219             insertParameter(line, mainWindowClassName() + QLatin1String("::")
220                 + QLatin1String(orientationString));
221         } else if (line.contains(QLatin1String("// DELETE_LINE"))) {
222             continue; // omit this line in the output
223         } else {
224             adaptLine = adaptCurrentMainCppTemplateLine(line);
225         }
226         if (adaptLine) {
227             const int commentIndex = line.indexOf(QLatin1String(" //"));
228             if (commentIndex != -1)
229                 line.truncate(commentIndex);
230             out << line << endl;
231         }
232     }
233
234     return mainCppContent;
235 }
236
237 QByteArray AbstractMobileApp::generateProFile(QString *errorMessage) const
238 {
239     const QChar comment = QLatin1Char('#');
240     QFile proFile(path(AppProOrigin));
241     if (!proFile.open(QIODevice::ReadOnly)) {
242         *errorMessage = QCoreApplication::translate("Qt4ProjectManager::AbstractMobileApp",
243                                                     "Could not open project file template '%1'.")
244             .arg(proFile.fileName());
245         return QByteArray();
246     }
247     QTextStream in(&proFile);
248
249     QByteArray proFileContent;
250     QTextStream out(&proFileContent, QIODevice::WriteOnly);
251
252     QString valueOnNextLine;
253     bool commentOutNextLine = false;
254     QString line;
255     while (!(line = in.readLine()).isNull()) {
256         if (line.contains(QLatin1String("# TARGETUID3"))) {
257             valueOnNextLine = symbianTargetUid();
258         } else if (line.contains(QLatin1String("# NETWORKACCESS"))
259             && !networkEnabled()) {
260             commentOutNextLine = true;
261         } else if (line.contains(QLatin1String("# DEPLOYMENTFOLDERS"))) {
262             // Eat lines
263             QString nextLine;
264             while (!(nextLine = in.readLine()).isNull()
265                 && !nextLine.contains(QLatin1String("# DEPLOYMENTFOLDERS_END")))
266             { }
267             if (nextLine.isNull())
268                 continue;
269
270             int foldersCount = 0;
271             QStringList folders;
272             foreach (const DeploymentFolder &folder, deploymentFolders()) {
273                 foldersCount++;
274                 const QString folderName =
275                     QString::fromLatin1("folder_%1").arg(foldersCount, 2, 10, QLatin1Char('0'));
276                 out << folderName << ".source = " << folder.first << endl;
277                 if (!folder.second.isEmpty())
278                     out << folderName << ".target = " << folder.second << endl;
279                 folders.append(folderName);
280             }
281             if (foldersCount > 0)
282                 out << "DEPLOYMENTFOLDERS = " << folders.join(QLatin1String(" ")) << endl;
283         } else if (line.contains(QLatin1String("# INCLUDE_DEPLOYMENT_PRI"))) {
284             in.readLine(); // eats 'include(deployment.pri)'
285         } else {
286             handleCurrentProFileTemplateLine(line, in, out, commentOutNextLine);
287         }
288
289         // Remove all marker comments
290         if (line.trimmed().startsWith(comment)
291             && line.trimmed().endsWith(comment))
292             continue;
293
294         if (!valueOnNextLine.isEmpty()) {
295             out << line.left(line.indexOf(QLatin1Char('=')) + 2)
296                 << QDir::fromNativeSeparators(valueOnNextLine) << endl;
297             valueOnNextLine.clear();
298             continue;
299         }
300
301         if (commentOutNextLine) {
302             out << comment << line << endl;
303             commentOutNextLine = false;
304             continue;
305         }
306         out << line << endl;
307     };
308
309     proFileContent.replace("../shared/" + DeploymentPriFileName.toAscii(),
310         DeploymentPriFileName.toAscii());
311     return proFileContent;
312 }
313
314 QList<AbstractGeneratedFileInfo> AbstractMobileApp::fileUpdates(const QString &mainProFile) const
315 {
316     QList<AbstractGeneratedFileInfo> result;
317     foreach (const AbstractGeneratedFileInfo &file, updateableFiles(mainProFile)) {
318         AbstractGeneratedFileInfo newFile = file;
319         QFile readFile(newFile.fileInfo.absoluteFilePath());
320         if (!readFile.open(QIODevice::ReadOnly))
321            continue;
322         const QString firstLine = readFile.readLine();
323         const QStringList elements = firstLine.split(QLatin1Char(' '));
324         if (elements.count() != 5 || elements.at(1) != FileChecksum
325                 || elements.at(3) != FileStubVersion)
326             continue;
327         const QString versionString = elements.at(4);
328         newFile.version = versionString.startsWith(QLatin1String("0x"))
329             ? versionString.toInt(0, 16) : 0;
330         newFile.statedChecksum = elements.at(2).toUShort(0, 16);
331         QByteArray data = readFile.readAll();
332         data.replace('\x0D', "");
333         data.replace('\x0A', "");
334         newFile.dataChecksum = qChecksum(data.constData(), data.length());
335         if (newFile.dataChecksum != newFile.statedChecksum
336                 || newFile.version < newFile.currentVersion)
337             result.append(newFile);
338     }
339     return result;
340 }
341
342
343 bool AbstractMobileApp::updateFiles(const QList<AbstractGeneratedFileInfo> &list, QString &error) const
344 {
345     error.clear();
346     foreach (const AbstractGeneratedFileInfo &info, list) {
347         const QByteArray data = generateFile(info.fileType, &error);
348         if (!error.isEmpty())
349             return false;
350         QFile file(info.fileInfo.absoluteFilePath());
351         if (!file.open(QIODevice::WriteOnly) || file.write(data) == -1) {
352             error = QCoreApplication::translate(
353                         "Qt4ProjectManager::Internal::QtQuickApp",
354                         "Could not write file '%1'.").
355                     arg(QDir::toNativeSeparators(info.fileInfo.canonicalFilePath()));
356             return false;
357         }
358     }
359     return true;
360 }
361
362 #ifndef CREATORLESSTEST
363 // The definition of QtQuickApp::templatesRoot() for
364 // CREATORLESSTEST is in tests/manual/appwizards/helpers.cpp
365 QString AbstractMobileApp::templatesRoot()
366 {
367     return Core::ICore::instance()->resourcePath()
368         + QLatin1String("/templates/");
369 }
370
371 Core::GeneratedFile AbstractMobileApp::file(const QByteArray &data,
372     const QString &targetFile)
373 {
374     Core::GeneratedFile generatedFile(targetFile);
375     generatedFile.setBinary(true);
376     generatedFile.setBinaryContents(data);
377     return generatedFile;
378 }
379
380 Core::GeneratedFiles AbstractMobileApp::generateFiles(QString *errorMessage) const
381 {
382     Core::GeneratedFiles files;
383     files << file(generateFile(AbstractGeneratedFileInfo::AppProFile, errorMessage), path(AppPro));
384     files.last().setAttributes(Core::GeneratedFile::OpenProjectAttribute);
385     files << file(generateFile(AbstractGeneratedFileInfo::MainCppFile, errorMessage), path(MainCpp));
386     files << file(generateFile(AbstractGeneratedFileInfo::SymbianSvgIconFile, errorMessage), path(SymbianSvgIcon));
387     files << file(generateFile(AbstractGeneratedFileInfo::MaemoPngIconFile, errorMessage), path(MaemoPngIcon));
388     files << file(generateFile(AbstractGeneratedFileInfo::DesktopFile, errorMessage), path(Desktop));
389     return files;
390 }
391 #endif // CREATORLESSTEST
392
393 QString AbstractMobileApp::error() const
394 {
395     return m_error;
396 }
397
398 QByteArray AbstractMobileApp::readBlob(const QString &filePath,
399     QString *errorMsg) const
400 {
401     QFile sourceFile(filePath);
402     if (!sourceFile.open(QIODevice::ReadOnly)) {
403         *errorMsg = QCoreApplication::translate("Qt4ProjectManager::AbstractMobileApp",
404             "Could not open template file '%1'.").arg(filePath);
405         return QByteArray();
406     }
407     return sourceFile.readAll();
408 }
409
410 QByteArray AbstractMobileApp::generateFile(int fileType,
411     QString *errorMessage) const
412 {
413     QByteArray data;
414     QString comment = CFileComment;
415     bool versionAndChecksum = false;
416     switch (fileType) {
417         case AbstractGeneratedFileInfo::MainCppFile:
418             data = generateMainCpp(errorMessage);
419             break;
420         case AbstractGeneratedFileInfo::AppProFile:
421             data = generateProFile(errorMessage);
422             comment = ProFileComment;
423             break;
424         case AbstractGeneratedFileInfo::SymbianSvgIconFile:
425             data = readBlob(path(SymbianSvgIconOrigin), errorMessage);
426             break;
427         case AbstractGeneratedFileInfo::MaemoPngIconFile:
428             data = readBlob(path(MaemoPngIconOrigin), errorMessage);
429             break;
430         case AbstractGeneratedFileInfo::DesktopFile:
431             data = generateDesktopFile(errorMessage);
432             break;
433         case AbstractGeneratedFileInfo::DeploymentPriFile:
434             data = readBlob(path(DeploymentPriOrigin), errorMessage);
435             comment = ProFileComment;
436             versionAndChecksum = true;
437             break;
438         default:
439             data = generateFileExtended(fileType, &versionAndChecksum,
440                 &comment, errorMessage);
441     }
442     if (!versionAndChecksum)
443         return data;
444     QByteArray versioned = data;
445     versioned.replace('\x0D', "");
446     versioned.replace('\x0A', "");
447     const QLatin1String hexPrefix("0x");
448     const quint16 checkSum = qChecksum(versioned.constData(), versioned.length());
449     const QString checkSumString = hexPrefix + QString::number(checkSum, 16);
450     const QString versionString
451         = hexPrefix + QString::number(makeStubVersion(stubVersionMinor()), 16);
452     const QChar sep = QLatin1Char(' ');
453     const QString versionLine =
454             comment + sep + FileChecksum + sep + checkSumString
455             + sep + FileStubVersion + sep + versionString + QLatin1Char('\x0A');
456     return versionLine.toAscii() + data;
457 }
458
459 int AbstractMobileApp::makeStubVersion(int minor)
460 {
461     return StubVersion << 16 | minor;
462 }
463
464 QString AbstractMobileApp::outputPathBase() const
465 {
466     QString path = m_projectPath.absoluteFilePath();
467     if (!path.endsWith(QLatin1Char('/')))
468             path.append(QLatin1Char('/'));
469     return path + projectName() + QLatin1Char('/');
470 }
471
472 void AbstractMobileApp::insertParameter(QString &line, const QString &parameter)
473 {
474     line.replace(QRegExp(QLatin1String("\\([^()]+\\)")),
475         QLatin1Char('(') + parameter + QLatin1Char(')'));
476 }
477
478 } // namespace Qt4ProjectManager