maemotemplatesmanager.cpp 23.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/

#include "maemotemplatesmanager.h"

32
33
34
35
#include "maemodeployablelistmodel.h"
#include "maemodeployables.h"
#include "maemodeploystep.h"
#include "maemoglobal.h"
36
37
38
39
40
41
42
#include "maemopackagecreationstep.h"
#include "maemotoolchain.h"

#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
43
44
#include <qt4projectmanager/qt4nodes.h>
#include <qt4projectmanager/qt4project.h>
45
46
47
#include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <qt4projectmanager/qt4target.h>

48
#include <QtCore/QBuffer>
49
#include <QtCore/QDir>
50
#include <QtCore/QFileSystemWatcher>
51
#include <QtCore/QList>
52
#include <QtGui/QPixmap>
53
54
55
#include <QtCore/QProcess>
#include <QtGui/QMessageBox>

56
57
#include <cctype>

58
59
60
61
62
using namespace ProjectExplorer;

namespace Qt4ProjectManager {
namespace Internal {

63
64
65
66
namespace {
const QByteArray IconFieldName("XB-Maemo-Icon-26:");
} // anonymous namespace

67
68
69
70
71
72
73
74
75
76
77

MaemoTemplatesManager *MaemoTemplatesManager::m_instance = 0;

MaemoTemplatesManager *MaemoTemplatesManager::instance(QObject *parent)
{
    Q_ASSERT(!m_instance != !parent);
    if (!m_instance)
        m_instance = new MaemoTemplatesManager(parent);
    return m_instance;
}

78
MaemoTemplatesManager::MaemoTemplatesManager(QObject *parent) : QObject(parent)
79
80
81
82
83
{
    SessionManager * const session
        = ProjectExplorerPlugin::instance()->session();
    connect(session, SIGNAL(startupProjectChanged(ProjectExplorer::Project*)),
        this, SLOT(handleActiveProjectChanged(ProjectExplorer::Project*)));
84
85
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
        this, SLOT(handleProjectToBeRemoved(ProjectExplorer::Project*)));
86
    handleActiveProjectChanged(session->startupProject());    
87
88
89
90
}

void MaemoTemplatesManager::handleActiveProjectChanged(ProjectExplorer::Project *project)
{
91
92
93
94
95
96
97
98
99
100
    if (!project || m_maemoProjects.contains(project))
        return;

    connect(project, SIGNAL(addedTarget(ProjectExplorer::Target*)),
        this, SLOT(handleTarget(ProjectExplorer::Target*)));
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
        this, SLOT(handleTarget(ProjectExplorer::Target*)));
    const QList<Target *> &targets = project->targets();
    foreach (Target * const target, targets)
        handleTarget(target);
101
102
}

103
bool MaemoTemplatesManager::handleTarget(ProjectExplorer::Target *target)
104
105
106
{
    if (!target
        || target->id() != QLatin1String(Constants::MAEMO_DEVICE_TARGET_ID))
107
108
109
        return false;
    if (!createDebianTemplatesIfNecessary(target))
        return false;
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

    const Qt4Target * const qt4Target = qobject_cast<Qt4Target *>(target);
    const MaemoDeployStep * const deployStep
        = MaemoGlobal::buildStep<MaemoDeployStep>(qt4Target->activeDeployConfiguration());
    connect(deployStep->deployables(), SIGNAL(modelsCreated()), this,
        SLOT(handleProFileUpdated()), Qt::QueuedConnection);

    Project * const project = target->project();
    if (m_maemoProjects.contains(project))
        return true;

    QFileSystemWatcher * const fsWatcher = new QFileSystemWatcher(this);
    fsWatcher->addPath(debianDirPath(project));
    fsWatcher->addPath(changeLogFilePath(project));
    fsWatcher->addPath(controlFilePath(project));
    connect(fsWatcher, SIGNAL(directoryChanged(QString)), this,
        SLOT(handleDebianDirContentsChanged()));
    connect(fsWatcher, SIGNAL(fileChanged(QString)), this,
        SLOT(handleDebianFileChanged(QString)));
    handleDebianDirContentsChanged();
    handleDebianFileChanged(changeLogFilePath(project));
    handleDebianFileChanged(controlFilePath(project));
    m_maemoProjects.insert(project, fsWatcher);

134
    return true;
135
136
}

137
bool MaemoTemplatesManager::createDebianTemplatesIfNecessary(const ProjectExplorer::Target *target)
138
139
140
{
    Project * const project = target->project();
    QDir projectDir(project->projectDirectory());
141
    if (projectDir.exists(QLatin1String("debian")))
142
        return true;
143
144
145

    QProcess dh_makeProc;
    QString error;
146
    const Qt4Target * const qt4Target = qobject_cast<const Qt4Target *>(target);
147
148
149
    Q_ASSERT_X(qt4Target, Q_FUNC_INFO, "Target ID does not match actual type.");
    const MaemoToolChain * const tc
        = dynamic_cast<MaemoToolChain *>(qt4Target->activeBuildConfiguration()->toolChain());
150
151
152
153
    if (!tc) {
        qDebug("Maemo target has no Maemo toolchain.");
        return false;
    }
154
    if (!MaemoPackageCreationStep::preparePackagingProcess(&dh_makeProc, tc,
155
        projectDir.path(), &error)) {
156
        raiseError(error);
157
        return false;
158
159
160
    }

    const QString command = QLatin1String("dh_make -s -n -p ")
161
        + MaemoPackageCreationStep::packageName(project) + QLatin1Char('_')
162
163
164
        + MaemoPackageCreationStep::DefaultVersionNumber;
    dh_makeProc.start(MaemoPackageCreationStep::packagingCommand(tc, command));
    if (!dh_makeProc.waitForStarted()) {
165
        raiseError(tr("Unable to create Debian templates: dh_make failed (%1)")
166
            .arg(dh_makeProc.errorString()));
167
        return false;
168
169
170
171
172
173
174
    }
    dh_makeProc.write("\n"); // Needs user input.
    dh_makeProc.waitForFinished(-1);
    if (dh_makeProc.error() != QProcess::UnknownError
        || dh_makeProc.exitCode() != 0) {
        raiseError(tr("Unable to create debian templates: dh_make failed (%1)")
            .arg(dh_makeProc.errorString()));
175
        return false;
176
177
    }

178
    QDir debianDir(debianDirPath(project));
179
180
181
    const QStringList &files = debianDir.entryList(QDir::Files);
    QStringList filesToAddToProject;
    foreach (const QString &fileName, files) {
182
183
184
185
        if (fileName.endsWith(QLatin1String(".ex"), Qt::CaseInsensitive)
            || fileName.compare(QLatin1String("README.debian"), Qt::CaseInsensitive) == 0
            || fileName.compare(QLatin1String("dirs"), Qt::CaseInsensitive) == 0
            || fileName.compare(QLatin1String("docs"), Qt::CaseInsensitive) == 0) {
186
187
188
189
190
            debianDir.remove(fileName);
        } else
            filesToAddToProject << debianDir.absolutePath()
                + QLatin1Char('/') + fileName;
    }
191
    qobject_cast<Qt4Project *>(project)->rootProjectNode()
192
        ->addFiles(UnknownFileType, filesToAddToProject);
193

194
195
196
197
198
199
    return adaptRulesFile(project) && adaptControlFile(project);
}

bool MaemoTemplatesManager::adaptRulesFile(const Project *project)
{
    const QString rulesFilePath = debianDirPath(project) + "/rules";
200
201
202
203
    QFile rulesFile(rulesFilePath);
    if (!rulesFile.open(QIODevice::ReadWrite)) {
        raiseError(tr("Packaging Error: Cannot open file '%1'.")
                   .arg(QDir::toNativeSeparators(rulesFilePath)));
204
        return false;
205
206
207
208
    }
    QByteArray rulesContents = rulesFile.readAll();
    rulesContents.replace("DESTDIR", "INSTALL_ROOT");
    rulesContents.replace("dh_shlibdeps", "# dh_shlibdeps");
209
    rulesContents.replace("dh_strip", "# dh_strip");
210
211
212
213
214
215
216
//    rulesContents.replace("$(MAKE) clean", "# $(MAKE) clean");
//    const Qt4Project * const qt4Project
//        = static_cast<const Qt4Project *>(project);
//    const QString proFileName
//        = QFileInfo(qt4Project->rootProjectNode()->path()).fileName();
//    rulesContents.replace("# Add here commands to configure the package.",
//        "qmake " + proFileName.toLocal8Bit());
217
218
219
220
221
222
223

    // Would be the right solution, but does not work (on Windows),
    // because dpkg-genchanges doesn't know about it (and can't be told).
    // rulesContents.replace("dh_builddeb", "dh_builddeb --destdir=.");

    rulesFile.resize(0);
    rulesFile.write(rulesContents);
224
    rulesFile.close();
225
226
227
    if (rulesFile.error() != QFile::NoError) {
        raiseError(tr("Packaging Error: Cannot write file '%1'.")
                   .arg(QDir::toNativeSeparators(rulesFilePath)));
228
        return false;
229
    }
230
231
    return true;
}
232

233
234
235
236
237
238
239
240
bool MaemoTemplatesManager::adaptControlFile(const Project *project)
{
    QFile controlFile(controlFilePath(project));
    if (!controlFile.open(QIODevice::ReadWrite)) {
        raiseError(tr("Packaging Error: Cannot open file '%1'.")
                   .arg(QDir::toNativeSeparators(controlFilePath(project))));
        return false;
    }
241

242
    QByteArray controlContents = controlFile.readAll();
243
244
245

    adaptControlFileField(controlContents, "Section", "user/hidden");
    adaptControlFileField(controlContents, "Priority", "optional");
246
247
248
249
250
251
252
253
254
255
256
    const int buildDependsOffset = controlContents.indexOf("Build-Depends:");
    if (buildDependsOffset == -1) {
        qWarning("Weird: no Build-Depends field in debian/control file.");
    } else {
        int buildDependsNewlineOffset
            = controlContents.indexOf('\n', buildDependsOffset);
        if (buildDependsNewlineOffset == -1) {
            controlContents += '\n';
            buildDependsNewlineOffset = controlContents.length() - 1;
        }
        controlContents.insert(buildDependsNewlineOffset,
257
            ", libqt4-dev");
258
    }
259

260
261
262
263
264
265
266
267
268
269
270
    controlFile.resize(0);
    controlFile.write(controlContents);
    controlFile.close();
    if (controlFile.error() != QFile::NoError) {
        raiseError(tr("Packaging Error: Cannot write file '%1'.")
                   .arg(QDir::toNativeSeparators(controlFilePath(project))));
        return false;
    }
    return true;
}

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
void MaemoTemplatesManager::adaptControlFileField(QByteArray &document,
    const QByteArray &fieldName, const QByteArray &newFieldValue)
{
    QByteArray adaptedLine = fieldName + ": " + newFieldValue;
    const int lineOffset = document.indexOf(fieldName + ":");
    if (lineOffset == -1) {
        document.append(adaptedLine).append('\n');
    } else {
        int newlineOffset = document.indexOf('\n', lineOffset);
        if (newlineOffset == -1) {
            newlineOffset = document.length();
            adaptedLine += '\n';
        }
        document.replace(lineOffset, newlineOffset - lineOffset, adaptedLine);
    }
}

288
bool MaemoTemplatesManager::updateDesktopFiles(const Qt4Target *target)
289
290
291
292
293
294
{
    const Qt4Target * const qt4Target = qobject_cast<const Qt4Target *>(target);
    Q_ASSERT_X(qt4Target, Q_FUNC_INFO,
        "Impossible: Target has Maemo id, but could not be cast to Qt4Target.");
    const QList<Qt4ProFileNode *> &applicationProjects
        = qt4Target->qt4Project()->applicationProFiles();
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
    bool success = true;
    foreach (Qt4ProFileNode *proFileNode, applicationProjects)
        success &= updateDesktopFile(qt4Target, proFileNode);
    return success;
}

bool MaemoTemplatesManager::updateDesktopFile(const Qt4Target *target,
    Qt4ProFileNode *proFileNode)
{
    const QString appName = proFileNode->targetInformation().target;
    const QString desktopFilePath = QFileInfo(proFileNode->path()).path()
        + QLatin1Char('/') + appName + QLatin1String(".desktop");
    QFile desktopFile(desktopFilePath);
    const bool existsAlready = desktopFile.exists();
    if (!desktopFile.open(QIODevice::ReadWrite)) {
        qWarning("Failed to open '%s': %s", qPrintable(desktopFilePath),
            qPrintable(desktopFile.errorString()));
        return false;
313
314
    }

315
316
317
318
    const QByteArray desktopTemplate("[Desktop Entry]\nEncoding=UTF-8\n"
        "Version=1.0\nType=Application\nTerminal=false\nName=\nExec=\n"
        "Icon=\nX-Window-Icon=\nX-HildonDesk-ShowInToolbar=true\n"
        "X-Osso-Type=application/x-executable\n");
319
    QByteArray desktopFileContents
320
321
322
323
324
325
326
327
328
329
330
        = existsAlready ? desktopFile.readAll() : desktopTemplate;

    QString executable;
    const MaemoDeployables * const deployables
        = MaemoGlobal::buildStep<MaemoDeployStep>(target->activeDeployConfiguration())
            ->deployables();
    for (int i = 0; i < deployables->modelCount(); ++i) {
        const MaemoDeployableListModel * const model = deployables->modelAt(i);
        if (model->proFileNode() == proFileNode) {
            executable = model->remoteExecutableFilePath();
            break;
331
332
        }
    }
333
334
335
336
    if (executable.isEmpty()) {
        qWarning("Strange: Project file node not managed by MaemoDeployables.");
    } else {
        int execNewLinePos, execValuePos;
337
338
        findLine("Exec=", desktopFileContents, execNewLinePos, execValuePos);
        desktopFileContents.replace(execValuePos, execNewLinePos - execValuePos,
339
            executable.toUtf8());
340
    }
341
342

    int nameNewLinePos, nameValuePos;
343
    findLine("Name=", desktopFileContents, nameNewLinePos, nameValuePos);
344
    if (nameNewLinePos == nameValuePos)
345
        desktopFileContents.insert(nameValuePos, appName.toUtf8());
346
347
348
349
    int iconNewLinePos, iconValuePos;
    findLine("Icon=", desktopFileContents, iconNewLinePos, iconValuePos);
    if (iconNewLinePos == iconValuePos)
        desktopFileContents.insert(iconValuePos, appName.toUtf8());
350
351

    desktopFile.resize(0);
352
    desktopFile.write(desktopFileContents);
353
354
355
356
    desktopFile.close();
    if (desktopFile.error() != QFile::NoError) {
        qWarning("Could not write '%s': %s", qPrintable(desktopFilePath),
            qPrintable(desktopFile.errorString()));
357
358
    }

359
360
361
    if (!existsAlready) {
        proFileNode->addFiles(UnknownFileType,
            QStringList() << desktopFilePath);
362
363
364
365
366
367
368
        QFile proFile(proFileNode->path());
        if (!proFile.open(QIODevice::ReadWrite)) {
            qWarning("Failed to open '%s': %s", qPrintable(proFileNode->path()),
                qPrintable(proFile.errorString()));
            return false;
        }
        QByteArray proFileContents = proFile.readAll();
369
        proFileContents += "\nunix:!symbian {\n"
370
            "    desktopfile.files = $${TARGET}.desktop\n"
371
372
373
374
            "    maemo5 {\n"
            "        desktopfile.path = /usr/share/applications/hildon\n"
            "    } else {\n"
            "        desktopfile.path = /usr/share/applications\n    }\n"
375
376
377
378
379
380
381
382
383
            "    INSTALLS += desktopfile\n}\n";
        proFile.resize(0);
        proFile.write(proFileContents);
        proFile.close();
        if (proFile.error() != QFile::NoError) {
            qWarning("Could not write '%s': %s", qPrintable(proFileNode->path()),
                qPrintable(proFile.errorString()));
            return false;
        }
384
    }
385
386
387
    return true;
}

388
389
390
391
392
393
394
void MaemoTemplatesManager::handleProjectToBeRemoved(ProjectExplorer::Project *project)
{
    MaemoProjectMap::Iterator it = m_maemoProjects.find(project);
    if (it != m_maemoProjects.end()) {
        delete it.value();
        m_maemoProjects.erase(it);
    }
395
396
}

397
void MaemoTemplatesManager::handleProFileUpdated()
398
{
399
400
401
402
403
404
405
    const MaemoDeployables * const deployables
        = qobject_cast<MaemoDeployables *>(sender());
    if (!deployables)
        return;
    const Target * const target = deployables->buildStep()->target();
    if (m_maemoProjects.contains(target->project()))
        updateDesktopFiles(qobject_cast<const Qt4Target *>(target));
406
407
}

408
409
410
QString MaemoTemplatesManager::version(const Project *project,
    QString *error) const
{
411
412
413
    QSharedPointer<QFile> changeLog
        = openFile(changeLogFilePath(project), QIODevice::ReadOnly, error);
    if (!changeLog)
414
        return QString();
415
    const QByteArray &firstLine = changeLog->readLine();
416
417
418
    const int openParenPos = firstLine.indexOf('(');
    if (openParenPos == -1) {
        *error = tr("Debian changelog file '%1' has unexpected format.")
419
                .arg(QDir::toNativeSeparators(changeLog->fileName()));
420
421
422
423
424
        return QString();
    }
    const int closeParenPos = firstLine.indexOf(')', openParenPos);
    if (closeParenPos == -1) {
        *error = tr("Debian changelog file '%1' has unexpected format.")
425
                .arg(QDir::toNativeSeparators(changeLog->fileName()));
426
427
428
429
430
431
432
433
434
        return QString();
    }
    return QString::fromUtf8(firstLine.mid(openParenPos + 1,
        closeParenPos - openParenPos - 1).data());
}

bool MaemoTemplatesManager::setVersion(const Project *project,
    const QString &version, QString *error) const
{
435
436
437
    QSharedPointer<QFile> changeLog
        = openFile(changeLogFilePath(project), QIODevice::ReadWrite, error);
    if (!changeLog)
438
439
        return false;

440
    QString content = QString::fromUtf8(changeLog->readAll());
441
    content.replace(QRegExp(QLatin1String("\\([a-zA-Z0-9_\\.]+\\)")),
Roberto Raggi's avatar
Roberto Raggi committed
442
        QLatin1Char('(') + version + QLatin1Char(')'));
443
444
445
446
    changeLog->resize(0);
    changeLog->write(content.toUtf8());
    changeLog->close();
    if (changeLog->error() != QFile::NoError) {
447
        *error = tr("Error writing Debian changelog file '%1': %2")
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
            .arg(QDir::toNativeSeparators(changeLog->fileName()),
                 changeLog->errorString());
        return false;
    }
    return true;
}

QIcon MaemoTemplatesManager::packageManagerIcon(const Project *project,
    QString *error) const
{
    QSharedPointer<QFile> controlFile
        = openFile(controlFilePath(project), QIODevice::ReadOnly, error);
    if (!controlFile)
        return QIcon();

    bool iconFieldFound = false;
    QByteArray currentLine;
    while (!iconFieldFound && !controlFile->atEnd()) {
        currentLine = controlFile->readLine();
        iconFieldFound = currentLine.startsWith(IconFieldName);
    }
    if (!iconFieldFound)
        return QIcon();

    int pos = IconFieldName.length();
    currentLine = currentLine.trimmed();
    QByteArray base64Icon;
    do {
        while (pos < currentLine.length())
            base64Icon += currentLine.at(pos++);
        do
            currentLine = controlFile->readLine();
        while (currentLine.startsWith('#'));
        if (currentLine.isEmpty() || !isspace(currentLine.at(0)))
            break;
        currentLine = currentLine.trimmed();
        if (currentLine.isEmpty())
            break;
        pos = 0;
    } while (true);
    QPixmap pixmap;
    if (!pixmap.loadFromData(QByteArray::fromBase64(base64Icon))) {
        *error = tr("Invalid icon data in Debian control file.");
        return QIcon();
    }
    return QIcon(pixmap);
}

bool MaemoTemplatesManager::setPackageManagerIcon(const Project *project,
    const QString &iconFilePath, QString *error) const
{
    const QSharedPointer<QFile> controlFile
        = openFile(controlFilePath(project), QIODevice::ReadWrite, error);
    if (!controlFile)
        return false;
    const QPixmap pixmap(iconFilePath);
    if (pixmap.isNull()) {
        *error = tr("Could not read image file '%1'.").arg(iconFilePath);
        return false;
    }

    QByteArray iconAsBase64;
    QBuffer buffer(&iconAsBase64);
    buffer.open(QIODevice::WriteOnly);
    if (!pixmap.scaled(48, 48).save(&buffer,
        QFileInfo(iconFilePath).suffix().toAscii())) {
        *error = tr("Could not export image file '%1'.").arg(iconFilePath);
        return false;
    }
    buffer.close();
    iconAsBase64 = iconAsBase64.toBase64();
    QByteArray contents = controlFile->readAll();
    const int iconFieldPos = contents.startsWith(IconFieldName)
        ? 0 : contents.indexOf('\n' + IconFieldName);
    if (iconFieldPos == -1) {
        if (!contents.endsWith('\n'))
            contents += '\n';
        contents.append(IconFieldName).append(' ').append(iconAsBase64)
            .append('\n');
    } else {
        const int oldIconStartPos
            = (iconFieldPos != 0) + iconFieldPos + IconFieldName.length();
        int nextEolPos = contents.indexOf('\n', oldIconStartPos);
        while (nextEolPos != -1 && nextEolPos != contents.length() - 1
            && contents.at(nextEolPos + 1) != '\n'
            && (contents.at(nextEolPos + 1) == '#'
                || std::isspace(contents.at(nextEolPos + 1))))
            nextEolPos = contents.indexOf('\n', nextEolPos + 1);
        if (nextEolPos == -1)
            nextEolPos = contents.length();
        contents.replace(oldIconStartPos, nextEolPos - oldIconStartPos,
            ' ' + iconAsBase64);
    }
    controlFile->resize(0);
    controlFile->write(contents);
    if (controlFile->error() != QFile::NoError) {
        *error = tr("Error writing file '%1': %2")
            .arg(QDir::toNativeSeparators(controlFile->fileName()),
                controlFile->errorString());
547
548
549
550
551
        return false;
    }
    return true;
}

552
553
554
555
556
557
558
559
560
QStringList MaemoTemplatesManager::debianFiles(const Project *project) const
{
    return QDir(debianDirPath(project))
        .entryList(QDir::Files, QDir::Name | QDir::IgnoreCase);
}

QString MaemoTemplatesManager::debianDirPath(const Project *project) const
{
    return project->projectDirectory() + QLatin1Char('/')
561
        + QLatin1String("/debian");
562
563
}

564
565
566
567
568
569
570
571
572
573
QString MaemoTemplatesManager::changeLogFilePath(const Project *project) const
{
    return debianDirPath(project) + QLatin1String("/changelog");
}

QString MaemoTemplatesManager::controlFilePath(const Project *project) const
{
    return debianDirPath(project) + QLatin1String("/control");
}

574
575
576
577
578
void MaemoTemplatesManager::raiseError(const QString &reason)
{
    QMessageBox::critical(0, tr("Error creating Maemo templates"), reason);
}

579
void MaemoTemplatesManager::handleDebianFileChanged(const QString &filePath)
580
{
581
582
583
584
585
586
587
588
    const Project * const project
        = findProject(qobject_cast<QFileSystemWatcher *>(sender()));
    if (project) {
        if (filePath == changeLogFilePath(project))
            emit changeLogChanged(project);
        else if (filePath == controlFilePath(project))
            emit controlChanged(project);
    }
589
590
591
592
}

void MaemoTemplatesManager::handleDebianDirContentsChanged()
{
593
594
595
596
    const Project * const project
        = findProject(qobject_cast<QFileSystemWatcher *>(sender()));
    if (project)
        emit debianDirContentsChanged(project);
597
598
}

599
600
601
602
603
604
605
606
607
608
609
610
611
612
QSharedPointer<QFile> MaemoTemplatesManager::openFile(const QString &filePath,
    QIODevice::OpenMode mode, QString *error) const
{
    const QString nativePath = QDir::toNativeSeparators(filePath);
    QSharedPointer<QFile> file(new QFile(filePath));
    if (!file->exists()) {
        *error = tr("File '%1' does not exist").arg(nativePath);
    } else if (!file->open(mode)) {
        *error = tr("Cannot open file '%1': %2")
            .arg(nativePath, file->errorString());
    }
    return file;
}

613
614
615
616
617
618
619
620
621
622
Project *MaemoTemplatesManager::findProject(const QFileSystemWatcher *fsWatcher) const
{
    for (MaemoProjectMap::ConstIterator it = m_maemoProjects.constBegin();
        it != m_maemoProjects.constEnd(); ++it) {
        if (it.value() == fsWatcher)
            return it.key();
    }
    return 0;
}

623
624
void MaemoTemplatesManager::findLine(const QByteArray &string,
    QByteArray &document, int &lineEndPos, int &valuePos)
625
{
626
627
628
629
630
631
632
633
634
635
    int lineStartPos = document.indexOf(string);
    if (lineStartPos == -1) {
        lineStartPos = document.length();
        document += string + '\n';
    }
    valuePos = lineStartPos + string.length();
    lineEndPos = document.indexOf('\n', lineStartPos);
    if (lineEndPos == -1) {
        lineEndPos = document.length();
        document += '\n';
636
637
638
    }
}

639
640
} // namespace Internal
} // namespace Qt4ProjectManager