genericproject.cpp 16.5 KB
Newer Older
Roberto Raggi's avatar
Roberto Raggi committed
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
Roberto Raggi's avatar
Roberto Raggi committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
**
** 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
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
Roberto Raggi's avatar
Roberto Raggi committed
27 28 29 30
**
**************************************************************************/

#include "genericproject.h"
Tobias Hunger's avatar
Tobias Hunger committed
31

dt's avatar
dt committed
32
#include "genericbuildconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
33 34
#include "genericprojectconstants.h"
#include "generictarget.h"
Roberto Raggi's avatar
Roberto Raggi committed
35

36
#include <projectexplorer/toolchain.h>
Roberto Raggi's avatar
Roberto Raggi committed
37 38 39
#include <projectexplorer/projectexplorerconstants.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <extensionsystem/pluginmanager.h>
40
#include <utils/pathchooser.h>
Roberto Raggi's avatar
Roberto Raggi committed
41 42
#include <coreplugin/icore.h>

43
#include <QtCore/QDir>
44
#include <QtCore/QProcessEnvironment>
Roberto Raggi's avatar
Roberto Raggi committed
45

46 47 48
#include <QtGui/QFormLayout>
#include <QtGui/QMainWindow>
#include <QtGui/QComboBox>
Roberto Raggi's avatar
Roberto Raggi committed
49 50 51

using namespace GenericProjectManager;
using namespace GenericProjectManager::Internal;
52
using namespace ProjectExplorer;
Roberto Raggi's avatar
Roberto Raggi committed
53

54
namespace {
55
const char * const TOOLCHAIN_KEY("GenericProjectManager.GenericProject.Toolchain");
56 57
} // end of anonymous namespace

58 59 60 61
////////////////////////////////////////////////////////////////////////////////////
// GenericProject
////////////////////////////////////////////////////////////////////////////////////

Roberto Raggi's avatar
Roberto Raggi committed
62
GenericProject::GenericProject(Manager *manager, const QString &fileName)
63 64
    : m_manager(manager),
      m_fileName(fileName),
Tobias Hunger's avatar
Tobias Hunger committed
65
      m_targetFactory(new GenericTargetFactory(this)),
66
      m_toolChain(0)
Roberto Raggi's avatar
Roberto Raggi committed
67
{
68
    QFileInfo fileInfo(m_fileName);
69 70
    QDir dir = fileInfo.dir();

71
    m_projectName      = fileInfo.completeBaseName();
72 73 74
    m_filesFileName    = QFileInfo(dir, m_projectName + QLatin1String(".files")).absoluteFilePath();
    m_includesFileName = QFileInfo(dir, m_projectName + QLatin1String(".includes")).absoluteFilePath();
    m_configFileName   = QFileInfo(dir, m_projectName + QLatin1String(".config")).absoluteFilePath();
75

76 77
    m_file = new GenericProjectFile(this, fileName);
    m_rootNode = new GenericProjectNode(this, m_file);
78

79
    m_manager->registerProject(this);
Roberto Raggi's avatar
Roberto Raggi committed
80 81 82 83
}

GenericProject::~GenericProject()
{
84
    m_manager->unregisterProject(this);
85

86 87
    delete m_rootNode;
    delete m_toolChain;
Roberto Raggi's avatar
Roberto Raggi committed
88 89
}

Tobias Hunger's avatar
Tobias Hunger committed
90 91 92 93 94 95
GenericTargetFactory *GenericProject::targetFactory() const
{
    return m_targetFactory;
}

GenericTarget *GenericProject::activeTarget() const
96
{
Tobias Hunger's avatar
Tobias Hunger committed
97
    return static_cast<GenericTarget *>(Project::activeTarget());
98 99
}

100
QString GenericProject::filesFileName() const
101
{ return m_filesFileName; }
102 103

QString GenericProject::includesFileName() const
104
{ return m_includesFileName; }
105 106

QString GenericProject::configFileName() const
107
{ return m_configFileName; }
108

109
static QStringList readLines(const QString &absoluteFileName)
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
    QStringList lines;

    QFile file(absoluteFileName);
    if (file.open(QFile::ReadOnly)) {
        QTextStream stream(&file);

        forever {
            QString line = stream.readLine();
            if (line.isNull())
                break;

            lines.append(line);
        }
    }

    return lines;
}

129
bool GenericProject::saveRawFileList(const QStringList &rawFileList)
130
{
131
    // Make sure we can open the file for writing
132
    QFile file(filesFileName());
133 134 135 136
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return false;

    QTextStream stream(&file);
137 138
    foreach (const QString &filePath, rawFileList)
        stream << filePath << QLatin1Char('\n');
139 140 141 142

    file.close();
    refresh(GenericProject::Files);
    return true;
143
}
144

145 146
bool GenericProject::addFiles(const QStringList &filePaths)
{
147 148 149 150 151
    QStringList newList = m_rawFileList;

    QDir baseDir(QFileInfo(m_fileName).dir());
    foreach (const QString &filePath, filePaths)
        newList.append(baseDir.relativeFilePath(filePath));
152

153
    return saveRawFileList(newList);
154 155 156 157
}

bool GenericProject::removeFiles(const QStringList &filePaths)
{
158
    QStringList newList = m_rawFileList;
159

160 161 162 163
    foreach (const QString &filePath, filePaths) {
        QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
        if (i != m_rawListEntries.end())
            newList.removeOne(i.value());
164
    }
165

166
    return saveRawFileList(newList);
167 168
}

169
void GenericProject::parseProject(RefreshOptions options)
Roberto Raggi's avatar
Roberto Raggi committed
170
{
171 172 173
    if (options & Files) {
        m_rawListEntries.clear();
        m_rawFileList = readLines(filesFileName());
174
        m_files = processEntries(m_rawFileList, &m_rawListEntries);
175
    }
176

177
    if (options & Configuration) {
178
        m_projectIncludePaths = processEntries(readLines(includesFileName()));
179

180 181
        // TODO: Possibly load some configuration from the project file
        //QSettings projectInfo(m_fileName, QSettings::IniFormat);
Roberto Raggi's avatar
Roberto Raggi committed
182

183
        m_defines.clear();
Roberto Raggi's avatar
Roberto Raggi committed
184

185 186 187 188
        QFile configFile(configFileName());
        if (configFile.open(QFile::ReadOnly))
            m_defines = configFile.readAll();
    }
189

190 191
    if (options & Files)
        emit fileListChanged();
Roberto Raggi's avatar
Roberto Raggi committed
192 193
}

194
void GenericProject::refresh(RefreshOptions options)
Roberto Raggi's avatar
Roberto Raggi committed
195
{
196 197 198
    QSet<QString> oldFileList;
    if (!(options & Configuration))
        oldFileList = m_files.toSet();
199

200 201 202 203
    parseProject(options);

    if (options & Files)
        m_rootNode->refresh();
Roberto Raggi's avatar
Roberto Raggi committed
204 205 206 207

    CppTools::CppModelManagerInterface *modelManager =
        ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();

208 209
    if (m_toolChain && modelManager) {
        const QByteArray predefinedMacros = m_toolChain->predefinedMacros();
Roberto Raggi's avatar
Roberto Raggi committed
210 211 212

        CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelManager->projectInfo(this);
        pinfo.defines = predefinedMacros;
Roberto Raggi's avatar
Roberto Raggi committed
213
        pinfo.defines += '\n';
214
        pinfo.defines += m_defines;
Roberto Raggi's avatar
Roberto Raggi committed
215

216 217
        QStringList allIncludePaths;
        QStringList allFrameworkPaths;
Roberto Raggi's avatar
Roberto Raggi committed
218

219
        foreach (const ProjectExplorer::HeaderPath &headerPath, m_toolChain->systemHeaderPaths()) {
Roberto Raggi's avatar
Roberto Raggi committed
220 221 222 223 224 225
            if (headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath)
                allFrameworkPaths.append(headerPath.path());
            else
                allIncludePaths.append(headerPath.path());
        }

Roberto Raggi's avatar
Roberto Raggi committed
226
        allIncludePaths += this->allIncludePaths();
Roberto Raggi's avatar
Roberto Raggi committed
227 228 229 230 231

        pinfo.frameworkPaths = allFrameworkPaths;
        pinfo.includePaths = allIncludePaths;

        // ### add _defines.
Roberto Raggi's avatar
Roberto Raggi committed
232 233
        pinfo.sourceFiles = files();
        pinfo.sourceFiles += generated();
Roberto Raggi's avatar
Roberto Raggi committed
234

235 236 237 238 239 240 241 242 243 244 245
        QStringList filesToUpdate;

        if (options & Configuration) {
            filesToUpdate = pinfo.sourceFiles;
            filesToUpdate.append(QLatin1String("<configuration>")); // XXX don't hardcode configuration file name
        } else if (options & Files) {
            // Only update files that got added to the list
            QSet<QString> newFileList = m_files.toSet();
            newFileList.subtract(oldFileList);
            filesToUpdate.append(newFileList.toList());
        }
246

Roberto Raggi's avatar
Roberto Raggi committed
247
        modelManager->updateProjectInfo(pinfo);
248
        modelManager->updateSourceFiles(filesToUpdate);
Roberto Raggi's avatar
Roberto Raggi committed
249 250 251
    }
}

252
/**
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
 * Expands environment variables in the given \a string when they are written
 * like $$(VARIABLE).
 */
static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
{
    const static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));

    int index = candidate.indexIn(string);
    while (index != -1) {
        const QString value = env.value(candidate.cap(1));

        string.replace(index, candidate.matchedLength(), value);
        index += value.length();

        index = candidate.indexIn(string, index);
    }
}

/**
 * Expands environment variables and converts the path from relative to the
 * project to an absolute path.
 *
275
 * The \a map variable is an optional argument that will map the returned
276
 * absolute paths back to their original \a entries.
277
 */
278 279
QStringList GenericProject::processEntries(const QStringList &paths,
                                           QHash<QString, QString> *map) const
Roberto Raggi's avatar
Roberto Raggi committed
280
{
281
    const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
282
    const QDir projectDir(QFileInfo(m_fileName).dir());
283

Roberto Raggi's avatar
Roberto Raggi committed
284
    QStringList absolutePaths;
285
    foreach (const QString &path, paths) {
286 287
        QString trimmedPath = path.trimmed();
        if (trimmedPath.isEmpty())
288 289
            continue;

290 291 292
        expandEnvironmentVariables(env, trimmedPath);

        const QString absPath = QFileInfo(projectDir, trimmedPath).absoluteFilePath();
293 294
        absolutePaths.append(absPath);
        if (map)
295
            map->insert(absPath, trimmedPath);
Roberto Raggi's avatar
Roberto Raggi committed
296 297 298 299 300
    }
    absolutePaths.removeDuplicates();
    return absolutePaths;
}

Roberto Raggi's avatar
Roberto Raggi committed
301 302 303
QStringList GenericProject::allIncludePaths() const
{
    QStringList paths;
304 305
    paths += m_includePaths;
    paths += m_projectIncludePaths;
Roberto Raggi's avatar
Roberto Raggi committed
306 307 308 309 310
    paths.removeDuplicates();
    return paths;
}

QStringList GenericProject::projectIncludePaths() const
311
{ return m_projectIncludePaths; }
Roberto Raggi's avatar
Roberto Raggi committed
312

Roberto Raggi's avatar
Roberto Raggi committed
313
QStringList GenericProject::files() const
314
{ return m_files; }
Roberto Raggi's avatar
Roberto Raggi committed
315 316

QStringList GenericProject::generated() const
317
{ return m_generated; }
Roberto Raggi's avatar
Roberto Raggi committed
318 319

QStringList GenericProject::includePaths() const
320
{ return m_includePaths; }
Roberto Raggi's avatar
Roberto Raggi committed
321 322

void GenericProject::setIncludePaths(const QStringList &includePaths)
323
{ m_includePaths = includePaths; }
Roberto Raggi's avatar
Roberto Raggi committed
324

Roberto Raggi's avatar
Roberto Raggi committed
325
QByteArray GenericProject::defines() const
326
{ return m_defines; }
Roberto Raggi's avatar
Roberto Raggi committed
327

328
void GenericProject::setToolChainType(ProjectExplorer::ToolChain::ToolChainType type)
Roberto Raggi's avatar
Roberto Raggi committed
329 330 331
{
    using namespace ProjectExplorer;

332
    m_toolChainType = type;
333

334 335
    delete m_toolChain;
    m_toolChain = 0;
Roberto Raggi's avatar
Roberto Raggi committed
336

337
    if (type == ToolChain::MinGW) {
Roberto Raggi's avatar
Roberto Raggi committed
338 339 340
        const QLatin1String qmake_cxx("g++"); // ### FIXME
        const QString mingwDirectory; // ### FIXME

341
        m_toolChain = ToolChain::createMinGWToolChain(qmake_cxx, mingwDirectory);
Roberto Raggi's avatar
Roberto Raggi committed
342

343
    } else if (type == ToolChain::MSVC) {
Roberto Raggi's avatar
Roberto Raggi committed
344
        const QString msvcVersion; // ### FIXME
345
        m_toolChain = ToolChain::createMSVCToolChain(msvcVersion, false);
Roberto Raggi's avatar
Roberto Raggi committed
346

347
    } else if (type == ToolChain::WINCE) {
Roberto Raggi's avatar
Roberto Raggi committed
348
        const QString msvcVersion, wincePlatform; // ### FIXME
349
        m_toolChain = ToolChain::createWinCEToolChain(msvcVersion, wincePlatform);
Roberto Raggi's avatar
Roberto Raggi committed
350

351
    } else if (type == ToolChain::GCC || type == ToolChain::GCC) {
Roberto Raggi's avatar
Roberto Raggi committed
352
        const QLatin1String qmake_cxx("g++"); // ### FIXME
353
        m_toolChain = ToolChain::createGccToolChain(qmake_cxx);
Roberto Raggi's avatar
Roberto Raggi committed
354 355 356
    }
}

357 358 359 360 361
ProjectExplorer::ToolChain *GenericProject::toolChain() const
{
    return m_toolChain;
}

362 363
ProjectExplorer::ToolChain::ToolChainType GenericProject::toolChainType() const
{ return m_toolChainType; }
364

365
QString GenericProject::displayName() const
Roberto Raggi's avatar
Roberto Raggi committed
366
{
367
    return m_projectName;
Roberto Raggi's avatar
Roberto Raggi committed
368 369
}

Tobias Hunger's avatar
Tobias Hunger committed
370 371 372 373 374
QString GenericProject::id() const
{
    return QLatin1String("GenericProjectManager.GenericProject");
}

Roberto Raggi's avatar
Roberto Raggi committed
375 376
Core::IFile *GenericProject::file() const
{
377
    return m_file;
Roberto Raggi's avatar
Roberto Raggi committed
378 379 380 381
}

ProjectExplorer::IProjectManager *GenericProject::projectManager() const
{
382
    return m_manager;
Roberto Raggi's avatar
Roberto Raggi committed
383 384 385 386 387 388 389 390 391 392 393 394
}

QList<ProjectExplorer::Project *> GenericProject::dependsOn()
{
    return QList<Project *>();
}

bool GenericProject::isApplication() const
{
    return true;
}

dt's avatar
dt committed
395
ProjectExplorer::BuildConfigWidget *GenericProject::createConfigWidget()
Roberto Raggi's avatar
Roberto Raggi committed
396 397 398 399
{
    return new GenericBuildSettingsWidget(this);
}

dt's avatar
dt committed
400
QList<ProjectExplorer::BuildConfigWidget*> GenericProject::subConfigWidgets()
Roberto Raggi's avatar
Roberto Raggi committed
401
{
dt's avatar
dt committed
402
    return QList<ProjectExplorer::BuildConfigWidget*>();
Roberto Raggi's avatar
Roberto Raggi committed
403 404
}

405
GenericProjectNode *GenericProject::rootProjectNode() const
Roberto Raggi's avatar
Roberto Raggi committed
406
{
407
    return m_rootNode;
Roberto Raggi's avatar
Roberto Raggi committed
408 409 410 411
}

QStringList GenericProject::files(FilesMode fileMode) const
{
412
    Q_UNUSED(fileMode)
413
    return m_files; // ### TODO: handle generated files here.
Roberto Raggi's avatar
Roberto Raggi committed
414 415
}

Tobias Hunger's avatar
Tobias Hunger committed
416
QStringList GenericProject::buildTargets() const
Roberto Raggi's avatar
Roberto Raggi committed
417 418 419 420 421 422 423
{
    QStringList targets;
    targets.append(QLatin1String("all"));
    targets.append(QLatin1String("clean"));
    return targets;
}

424
QVariantMap GenericProject::toMap() const
Roberto Raggi's avatar
Roberto Raggi committed
425
{
426 427 428 429 430 431 432 433 434
    QVariantMap map(Project::toMap());
    map.insert(QLatin1String(TOOLCHAIN_KEY), static_cast<int>(m_toolChainType));
    return map;
}

bool GenericProject::fromMap(const QVariantMap &map)
{
    if (!Project::fromMap(map))
        return false;
Roberto Raggi's avatar
Roberto Raggi committed
435

436
    // Add default BC:
Tobias Hunger's avatar
Tobias Hunger committed
437 438
    if (targets().isEmpty())
        addTarget(targetFactory()->create(this, QLatin1String(GENERIC_DESKTOP_TARGET_ID)));
439

440 441 442
    ToolChain::ToolChainType type =
            static_cast<ProjectExplorer::ToolChain::ToolChainType>
            (map.value(QLatin1String(TOOLCHAIN_KEY), 0).toInt());
Roberto Raggi's avatar
Roberto Raggi committed
443

444
    setToolChainType(type);
Roberto Raggi's avatar
Roberto Raggi committed
445 446

    setIncludePaths(allIncludePaths());
447

448
    refresh(Everything);
dt's avatar
dt committed
449
    return true;
450 451
}

Roberto Raggi's avatar
Roberto Raggi committed
452 453 454
////////////////////////////////////////////////////////////////////////////////////
// GenericBuildSettingsWidget
////////////////////////////////////////////////////////////////////////////////////
455

Roberto Raggi's avatar
Roberto Raggi committed
456
GenericBuildSettingsWidget::GenericBuildSettingsWidget(GenericProject *project)
dt's avatar
dt committed
457
    : m_project(project), m_buildConfiguration(0)
Roberto Raggi's avatar
Roberto Raggi committed
458 459
{
    QFormLayout *fl = new QFormLayout(this);
460 461
    fl->setContentsMargins(0, -1, 0, -1);
    fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
462

463 464 465 466 467 468 469
    // Configuration name
    m_nameLineEdit = new QLineEdit();
    fl->addRow(tr("Configuration Name:"), m_nameLineEdit);

    connect(m_nameLineEdit, SIGNAL(textEdited(QString)),
            this, SLOT(configNameEdited(QString)));

470
    // build directory
471
    m_pathChooser = new Utils::PathChooser(this);
472 473
    m_pathChooser->setEnabled(true);
    fl->addRow(tr("Build directory:"), m_pathChooser);
con's avatar
con committed
474
    connect(m_pathChooser, SIGNAL(changed(QString)), this, SLOT(buildDirectoryChanged()));
475

476
    // tool chain
477
    QComboBox *toolChainChooser = new QComboBox;
478
    toolChainChooser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
479
    using namespace ProjectExplorer;
480 481
    int index = 0;
    int selectedIndex = -1;
482
    foreach (ToolChain::ToolChainType tc, ToolChain::supportedToolChains()) {
483 484 485 486
        toolChainChooser->addItem(ToolChain::toolChainName(tc), QVariant::fromValue<ToolChain::ToolChainType>(tc));
        if (m_project->toolChainType() == tc)
            selectedIndex = index;
        ++index;
487 488
    }

489
    toolChainChooser->setCurrentIndex(selectedIndex);
490
    fl->addRow(tr("Tool Chain:"), toolChainChooser);
491
    connect(toolChainChooser, SIGNAL(activated(int)), this, SLOT(toolChainSelected(int)));
Roberto Raggi's avatar
Roberto Raggi committed
492 493 494 495 496 497 498 499
}

GenericBuildSettingsWidget::~GenericBuildSettingsWidget()
{ }

QString GenericBuildSettingsWidget::displayName() const
{ return tr("Generic Manager"); }

dt's avatar
dt committed
500
void GenericBuildSettingsWidget::init(BuildConfiguration *bc)
Roberto Raggi's avatar
Roberto Raggi committed
501
{
502
    m_buildConfiguration = static_cast<GenericBuildConfiguration *>(bc);
503
    m_nameLineEdit->setText(m_buildConfiguration->displayName());
504
    m_pathChooser->setPath(m_buildConfiguration->buildDirectory());
Roberto Raggi's avatar
Roberto Raggi committed
505 506
}

507 508 509 510 511
void GenericBuildSettingsWidget::configNameEdited(QString name)
{
    m_buildConfiguration->setDisplayName(name);
}

Roberto Raggi's avatar
Roberto Raggi committed
512 513
void GenericBuildSettingsWidget::buildDirectoryChanged()
{
514
    m_buildConfiguration->setBuildDirectory(m_pathChooser->path());
Roberto Raggi's avatar
Roberto Raggi committed
515 516
}

517 518 519 520 521 522 523 524 525
void GenericBuildSettingsWidget::toolChainSelected(int index)
{
    using namespace ProjectExplorer;

    QComboBox *toolChainChooser = qobject_cast<QComboBox*>(sender());
    ToolChain::ToolChainType type = toolChainChooser->itemData(index).value<ToolChain::ToolChainType>();
    m_project->setToolChainType(type);
}

Roberto Raggi's avatar
Roberto Raggi committed
526 527 528
////////////////////////////////////////////////////////////////////////////////////
// GenericProjectFile
////////////////////////////////////////////////////////////////////////////////////
529

Roberto Raggi's avatar
Roberto Raggi committed
530 531
GenericProjectFile::GenericProjectFile(GenericProject *parent, QString fileName)
    : Core::IFile(parent),
532 533
      m_project(parent),
      m_fileName(fileName)
Roberto Raggi's avatar
Roberto Raggi committed
534 535 536 537 538 539 540 541 542 543 544 545
{ }

GenericProjectFile::~GenericProjectFile()
{ }

bool GenericProjectFile::save(const QString &)
{
    return false;
}

QString GenericProjectFile::fileName() const
{
546
    return m_fileName;
Roberto Raggi's avatar
Roberto Raggi committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
}

QString GenericProjectFile::defaultPath() const
{
    return QString();
}

QString GenericProjectFile::suggestedFileName() const
{
    return QString();
}

QString GenericProjectFile::mimeType() const
{
    return Constants::GENERICMIMETYPE;
}

bool GenericProjectFile::isModified() const
{
    return false;
}

bool GenericProjectFile::isReadOnly() const
{
    return true;
}

bool GenericProjectFile::isSaveAsAllowed() const
{
    return false;
}

void GenericProjectFile::modified(ReloadBehavior *)
{
}