kde-extraapps/kdevplatform/language/codegen/templatesmodel.cpp

393 lines
13 KiB
C++
Raw Normal View History

/* This file is part of KDevelop
Copyright 2007 Alexander Dymo <adymo@kdevelop.org>
Copyright 2012 Miha Čančula <miha@noughmad.eu>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "templatesmodel.h"
#include <interfaces/icore.h>
#include <KTar>
#include <KZip>
#include <KStandardDirs>
#include <KComponentData>
#include <KDebug>
#include <KConfigGroup>
#include <KLocale>
#include <KMimeType>
#include <QFileInfo>
#include <QDir>
using namespace KDevelop;
class KDevelop::TemplatesModelPrivate
{
public:
TemplatesModelPrivate(const QString& typePrefix);
QString typePrefix;
QMap<QString, QStandardItem*> templateItems;
/**
* Extracts description files from all available template archives and saves them to a location
* determined by descriptionResourceSuffix().
**/
void extractTemplateDescriptions();
/**
* Creates a model item for the template @p name in category @p category
*
* @param name the name of the new template
* @param category the category of the new template
* @param parent the parent item
* @return the created item
**/
QStandardItem *createItem(const QString& name, const QString& category, QStandardItem* parent);
enum ResourceType
{
Description,
Template,
Preview
};
QString resourceFilter(ResourceType type, const QString &suffix = QString())
{
QString filter = typePrefix;
switch(type) {
case Description:
filter += QLatin1String("template_descriptions/");
break;
case Template:
filter += QLatin1String("templates/");
break;
case Preview:
filter += QLatin1String("template_previews/");
break;
}
return filter + suffix;
}
};
TemplatesModelPrivate::TemplatesModelPrivate(const QString& _typePrefix)
: typePrefix(_typePrefix)
{
if (!typePrefix.endsWith('/')) {
typePrefix.append('/');
}
}
TemplatesModel::TemplatesModel(const QString& typePrefix, QObject* parent)
: QStandardItemModel(parent)
, d(new TemplatesModelPrivate(typePrefix))
{
}
TemplatesModel::~TemplatesModel()
{
delete d;
}
void TemplatesModel::refresh()
{
clear();
d->templateItems.clear();
d->templateItems[""] = invisibleRootItem();
d->extractTemplateDescriptions();
KStandardDirs *dirs = ICore::self()->componentData().dirs();
const QStringList templateDescriptions = dirs->findAllResources("data", d->resourceFilter(TemplatesModelPrivate::Description));
const QStringList templateArchives = dirs->findAllResources("data", d->resourceFilter(TemplatesModelPrivate::Template));
foreach (const QString &templateDescription, templateDescriptions)
{
QFileInfo fi(templateDescription);
bool archiveFound = false;
foreach( const QString& templateArchive, templateArchives )
{
if( QFileInfo(templateArchive).baseName() == fi.baseName() )
{
archiveFound = true;
KConfig templateConfig(templateDescription);
KConfigGroup general(&templateConfig, "General");
QString name = general.readEntry("Name");
QString category = general.readEntry("Category");
QString comment = general.readEntry("Comment");
QStandardItem *templateItem = d->createItem(name, category, invisibleRootItem());
templateItem->setData(templateDescription, DescriptionFileRole);
templateItem->setData(templateArchive, ArchiveFileRole);
templateItem->setData(comment, CommentRole);
if (general.hasKey("Icon"))
{
QString icon = dirs->findResource("data", d->resourceFilter(TemplatesModelPrivate::Preview, general.readEntry("Icon")));
if (QFile::exists(icon)) {
templateItem->setData(icon, IconNameRole);
}
}
}
}
if (!archiveFound)
{
// Template file doesn't exist anymore, so remove the description
// saves us the extra lookups for templateExists on the next run
QFile(templateDescription).remove();
}
}
}
QStandardItem *TemplatesModelPrivate::createItem(const QString& name, const QString& category, QStandardItem* parent)
{
QStringList path = category.split('/');
QStringList currentPath;
foreach (const QString& entry, path)
{
currentPath << entry;
if (!templateItems.contains(currentPath.join("/"))) {
QStandardItem *item = new QStandardItem(entry);
item->setEditable(false);
parent->appendRow(item);
templateItems[currentPath.join("/")] = item;
parent = item;
} else {
parent = templateItems[currentPath.join("/")];
}
}
QStandardItem *templateItem = new QStandardItem(name);
templateItem->setEditable(false);
parent->appendRow(templateItem);
return templateItem;
}
void TemplatesModelPrivate::extractTemplateDescriptions()
{
KStandardDirs *dirs = ICore::self()->componentData().dirs();
QStringList templateArchives = dirs->findAllResources("data", resourceFilter(Template));
QString localDescriptionsDir = dirs->saveLocation("data", resourceFilter(Description));
foreach (const QString &archName, templateArchives)
{
kDebug() << "processing template" << archName;
QScopedPointer<KArchive> templateArchive;
if (QFileInfo(archName).completeSuffix() == "zip")
{
templateArchive.reset(new KZip(archName));
}
else
{
templateArchive.reset(new KTar(archName));
}
if (templateArchive->open(QIODevice::ReadOnly))
{
/*
* This class looks for template description files in the following order
*
* - "basename.kdevtemplate"
* - "*.kdevtemplate"
* - "basename.desktop"
* - "*.desktop"
*
* This is done because application templates can contain .desktop files used by the application
* so the kdevtemplate suffix must have priority.
*/
QFileInfo templateInfo(archName);
const KArchiveEntry *templateEntry =
templateArchive->directory()->entry(templateInfo.baseName() + ".kdevtemplate");
if (!templateEntry || !templateEntry->isFile())
{
/*
* First, if the .kdevtemplate file is not found by name,
* we check all the files in the archive for any .kdevtemplate file
*
* This is needed because kde-files.org renames downloaded files
*/
foreach (const QString& entryName, templateArchive->directory()->entries())
{
if (entryName.endsWith(".kdevtemplate"))
{
templateEntry = templateArchive->directory()->entry(entryName);
break;
}
}
}
if (!templateEntry || !templateEntry->isFile())
{
templateEntry = templateArchive->directory()->entry(templateInfo.baseName() + ".desktop");
}
if (!templateEntry || !templateEntry->isFile())
{
foreach (const QString& entryName, templateArchive->directory()->entries())
{
if (entryName.endsWith(".desktop"))
{
templateEntry = templateArchive->directory()->entry(entryName);
break;
}
}
}
if (!templateEntry || !templateEntry->isFile())
{
kDebug() << "template" << archName << "does not contain .kdevtemplate or .desktop file";
continue;
}
const KArchiveFile *templateFile = static_cast<const KArchiveFile*>(templateEntry);
kDebug() << "copy template description to" << localDescriptionsDir;
templateFile->copyTo(localDescriptionsDir);
/*
* Rename the extracted description
* so that its basename matches the basename of the template archive
*/
QFileInfo descriptionInfo(localDescriptionsDir + templateEntry->name());
QString destinationName = localDescriptionsDir + templateInfo.baseName() + '.' + descriptionInfo.suffix();
QFile::rename(descriptionInfo.absoluteFilePath(), destinationName);
KConfig config(destinationName);
KConfigGroup group(&config, "General");
if (group.hasKey("Icon"))
{
const KArchiveEntry* iconEntry = templateArchive->directory()->entry(group.readEntry("Icon"));
if (iconEntry && iconEntry->isFile())
{
const KArchiveFile* iconFile = static_cast<const KArchiveFile*>(iconEntry);
QString saveDir = dirs->saveLocation("data", resourceFilter(Preview));
iconFile->copyTo(saveDir);
QFileInfo iconInfo(saveDir + templateEntry->name());
QFile::rename(iconInfo.absoluteFilePath(), saveDir + templateInfo.baseName() + '.' + iconInfo.suffix());
}
}
}
else
{
kDebug() << "could not open template" << archName;
}
}
}
QModelIndexList TemplatesModel::templateIndexes(const QString& fileName) const
{
QFileInfo info(fileName);
KStandardDirs* dirs = ICore::self()->componentData().dirs();
QString description = dirs->findResource("data", d->resourceFilter(TemplatesModelPrivate::Description, info.baseName() + ".kdevtemplate"));
if (description.isEmpty())
{
description = dirs->findResource("data", d->resourceFilter(TemplatesModelPrivate::Description, info.baseName() + ".desktop"));
}
QModelIndexList indexes;
if (!description.isEmpty())
{
KConfig templateConfig(description);
KConfigGroup general(&templateConfig, "General");
QStringList categories = general.readEntry("Category").split('/');
QStringList levels;
foreach (const QString& category, categories)
{
levels << category;
indexes << d->templateItems[levels.join(QString('/'))]->index();
}
if (!indexes.isEmpty())
{
QString name = general.readEntry("Name");
QStandardItem* categoryItem = d->templateItems[levels.join(QString('/'))];
for (int i = 0; i < categoryItem->rowCount(); ++i)
{
QStandardItem* templateItem = categoryItem->child(i);
if (templateItem->text() == name)
{
indexes << templateItem->index();
break;
}
}
}
}
return indexes;
}
QString TemplatesModel::typePrefix() const
{
return d->typePrefix;
}
QString TemplatesModel::loadTemplateFile(const QString& fileName)
{
KStandardDirs* dirs = ICore::self()->componentData().dirs();
QString saveLocation = dirs->saveLocation("data", d->resourceFilter(TemplatesModelPrivate::Template));
QFileInfo info(fileName);
QString destination = saveLocation + info.baseName();
KMimeType::Ptr mimeType = KMimeType::findByUrl(KUrl(fileName));
kDebug() << "Loaded file" << fileName << "with type" << mimeType->name();
if (mimeType->name() == "application/x-desktop")
{
kDebug() << "Loaded desktop file" << info.absoluteFilePath() << ", compressing";
#ifdef Q_WS_WIN
destination += ".zip";
KZip archive(destination);
#else
destination += ".tar.bz2";
KTar archive(destination, "application/x-bzip");
#endif //Q_WS_WIN
archive.open(QIODevice::WriteOnly);
QDir dir(info.absoluteDir());
QDir::Filters filter = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;
foreach (const QFileInfo& entry, dir.entryInfoList(filter))
{
if (entry.isFile())
{
archive.addLocalFile(entry.absoluteFilePath(), entry.fileName());
}
else if (entry.isDir())
{
archive.addLocalDirectory(entry.absoluteFilePath(), entry.fileName());
}
}
archive.close();
}
else
{
kDebug() << "Copying" << fileName << "to" << saveLocation;
QFile::copy(fileName, destination);
}
refresh();
return destination;
}