1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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
16 ** GNU Lesser General Public License Usage
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.
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.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "abstractmobileapp.h"
36 #include <QtCore/QCoreApplication>
37 #include <QtCore/QDir>
38 #include <QtCore/QFile>
39 #include <QtCore/QTextStream>
41 #ifndef CREATORLESSTEST
42 #include <coreplugin/icore.h>
43 #endif // CREATORLESSTEST
45 namespace Qt4ProjectManager {
47 AbstractGeneratedFileInfo::AbstractGeneratedFileInfo()
48 : fileType(ExtendedFile)
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;
63 AbstractMobileApp::AbstractMobileApp()
64 : m_orientation(ScreenOrientationAuto)
65 , m_networkEnabled(true)
69 AbstractMobileApp::~AbstractMobileApp() { }
71 QString AbstractMobileApp::symbianUidForPath(const QString &path)
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);
78 return QString::fromLatin1("0xE")
79 + QString::fromLatin1("%1").arg(hash, 7, 16, QLatin1Char('0')).right(7).toUpper();
82 void AbstractMobileApp::setOrientation(ScreenOrientation orientation)
84 m_orientation = orientation;
87 AbstractMobileApp::ScreenOrientation AbstractMobileApp::orientation() const
92 void AbstractMobileApp::setProjectName(const QString &name)
97 QString AbstractMobileApp::projectName() const
102 void AbstractMobileApp::setProjectPath(const QString &path)
104 m_projectPath.setFile(path);
107 void AbstractMobileApp::setSymbianSvgIcon(const QString &icon)
109 m_symbianSvgIcon = icon;
112 QString AbstractMobileApp::symbianSvgIcon() const
114 return path(SymbianSvgIconOrigin);
117 void AbstractMobileApp::setMaemoPngIcon(const QString &icon)
119 m_maemoPngIcon = icon;
122 QString AbstractMobileApp::maemoPngIcon() const
124 return path(MaemoPngIconOrigin);
127 void AbstractMobileApp::setSymbianTargetUid(const QString &uid)
129 m_symbianTargetUid = uid;
132 QString AbstractMobileApp::symbianTargetUid() const
134 return !m_symbianTargetUid.isEmpty() ? m_symbianTargetUid
135 : symbianUidForPath(path(AppPro));
138 void AbstractMobileApp::setNetworkEnabled(bool enabled)
140 m_networkEnabled = enabled;
143 bool AbstractMobileApp::networkEnabled() const
145 return m_networkEnabled;
148 QString AbstractMobileApp::path(int fileType) const
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());
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);
177 QByteArray AbstractMobileApp::generateDesktopFile(QString *errorMessage) const
179 QFile desktopTemplate(path(DesktopOrigin));
180 if (!desktopTemplate.open(QIODevice::ReadOnly)) {
181 *errorMessage = QCoreApplication::translate("Qt4ProjectManager::AbstractMobileApp",
182 "Could not open desktop file template");
185 QByteArray desktopFileContent = desktopTemplate.readAll();
186 return desktopFileContent.replace("thisApp", projectName().toUtf8());
189 QByteArray AbstractMobileApp::generateMainCpp(QString *errorMessage) const
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());
198 QTextStream in(&sourceFile);
200 QByteArray mainCppContent;
201 QTextStream out(&mainCppContent, QIODevice::WriteOnly);
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";
212 case ScreenOrientationLockPortrait:
213 orientationString = "ScreenOrientationLockPortrait";
215 case ScreenOrientationAuto:
216 orientationString = "ScreenOrientationAuto";
219 insertParameter(line, mainWindowClassName() + QLatin1String("::")
220 + QLatin1String(orientationString));
221 } else if (line.contains(QLatin1String("// DELETE_LINE"))) {
222 continue; // omit this line in the output
224 adaptLine = adaptCurrentMainCppTemplateLine(line);
227 const int commentIndex = line.indexOf(QLatin1String(" //"));
228 if (commentIndex != -1)
229 line.truncate(commentIndex);
234 return mainCppContent;
237 QByteArray AbstractMobileApp::generateProFile(QString *errorMessage) const
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());
247 QTextStream in(&proFile);
249 QByteArray proFileContent;
250 QTextStream out(&proFileContent, QIODevice::WriteOnly);
252 QString valueOnNextLine;
253 bool commentOutNextLine = false;
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"))) {
264 while (!(nextLine = in.readLine()).isNull()
265 && !nextLine.contains(QLatin1String("# DEPLOYMENTFOLDERS_END")))
267 if (nextLine.isNull())
270 int foldersCount = 0;
272 foreach (const DeploymentFolder &folder, deploymentFolders()) {
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);
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)'
286 handleCurrentProFileTemplateLine(line, in, out, commentOutNextLine);
289 // Remove all marker comments
290 if (line.trimmed().startsWith(comment)
291 && line.trimmed().endsWith(comment))
294 if (!valueOnNextLine.isEmpty()) {
295 out << line.left(line.indexOf(QLatin1Char('=')) + 2)
296 << QDir::fromNativeSeparators(valueOnNextLine) << endl;
297 valueOnNextLine.clear();
301 if (commentOutNextLine) {
302 out << comment << line << endl;
303 commentOutNextLine = false;
309 proFileContent.replace("../shared/" + DeploymentPriFileName.toAscii(),
310 DeploymentPriFileName.toAscii());
311 return proFileContent;
314 QList<AbstractGeneratedFileInfo> AbstractMobileApp::fileUpdates(const QString &mainProFile) const
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))
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)
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);
343 bool AbstractMobileApp::updateFiles(const QList<AbstractGeneratedFileInfo> &list, QString &error) const
346 foreach (const AbstractGeneratedFileInfo &info, list) {
347 const QByteArray data = generateFile(info.fileType, &error);
348 if (!error.isEmpty())
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()));
362 #ifndef CREATORLESSTEST
363 // The definition of QtQuickApp::templatesRoot() for
364 // CREATORLESSTEST is in tests/manual/appwizards/helpers.cpp
365 QString AbstractMobileApp::templatesRoot()
367 return Core::ICore::instance()->resourcePath()
368 + QLatin1String("/templates/");
371 Core::GeneratedFile AbstractMobileApp::file(const QByteArray &data,
372 const QString &targetFile)
374 Core::GeneratedFile generatedFile(targetFile);
375 generatedFile.setBinary(true);
376 generatedFile.setBinaryContents(data);
377 return generatedFile;
380 Core::GeneratedFiles AbstractMobileApp::generateFiles(QString *errorMessage) const
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));
391 #endif // CREATORLESSTEST
393 QString AbstractMobileApp::error() const
398 QByteArray AbstractMobileApp::readBlob(const QString &filePath,
399 QString *errorMsg) const
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);
407 return sourceFile.readAll();
410 QByteArray AbstractMobileApp::generateFile(int fileType,
411 QString *errorMessage) const
414 QString comment = CFileComment;
415 bool versionAndChecksum = false;
417 case AbstractGeneratedFileInfo::MainCppFile:
418 data = generateMainCpp(errorMessage);
420 case AbstractGeneratedFileInfo::AppProFile:
421 data = generateProFile(errorMessage);
422 comment = ProFileComment;
424 case AbstractGeneratedFileInfo::SymbianSvgIconFile:
425 data = readBlob(path(SymbianSvgIconOrigin), errorMessage);
427 case AbstractGeneratedFileInfo::MaemoPngIconFile:
428 data = readBlob(path(MaemoPngIconOrigin), errorMessage);
430 case AbstractGeneratedFileInfo::DesktopFile:
431 data = generateDesktopFile(errorMessage);
433 case AbstractGeneratedFileInfo::DeploymentPriFile:
434 data = readBlob(path(DeploymentPriOrigin), errorMessage);
435 comment = ProFileComment;
436 versionAndChecksum = true;
439 data = generateFileExtended(fileType, &versionAndChecksum,
440 &comment, errorMessage);
442 if (!versionAndChecksum)
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;
459 int AbstractMobileApp::makeStubVersion(int minor)
461 return StubVersion << 16 | minor;
464 QString AbstractMobileApp::outputPathBase() const
466 QString path = m_projectPath.absoluteFilePath();
467 if (!path.endsWith(QLatin1Char('/')))
468 path.append(QLatin1Char('/'));
469 return path + projectName() + QLatin1Char('/');
472 void AbstractMobileApp::insertParameter(QString &line, const QString ¶meter)
474 line.replace(QRegExp(QLatin1String("\\([^()]+\\)")),
475 QLatin1Char('(') + parameter + QLatin1Char(')'));
478 } // namespace Qt4ProjectManager