diff --git a/language/CMakeLists.txt b/language/CMakeLists.txt --- a/language/CMakeLists.txt +++ b/language/CMakeLists.txt @@ -158,6 +158,7 @@ set(grantlee_LIB_SRCS codegen/templatesmodel.cpp + codegen/templatepreviewicon.cpp codegen/templateclassgenerator.cpp codegen/sourcefiletemplate.cpp codegen/templaterenderer.cpp @@ -352,6 +353,7 @@ codegen/coderepresentation.h codegen/utilities.h codegen/templatesmodel.h + codegen/templatepreviewicon.h codegen/templaterenderer.h codegen/templateengine.h codegen/sourcefiletemplate.h diff --git a/language/codegen/templatepreviewicon.h b/language/codegen/templatepreviewicon.h new file mode 100644 --- /dev/null +++ b/language/codegen/templatepreviewicon.h @@ -0,0 +1,84 @@ +/* This file is part of KDevelop + Copyright 2017 Friedrich W. H. Kossebau + + 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. +*/ + +#ifndef KDEVPLATFORM_TEMPLATEPREVIEWICON_H +#define KDEVPLATFORM_TEMPLATEPREVIEWICON_H + +#include +#include + +#include + +class QPixmap; + +namespace KDevelop +{ + +class TemplatePreviewIconData; + +/** + * @brief A class for loading preview images/icons as specified in a template description on demand + * + * It stores the icon name as extracted from the template description together with + * the path to the archive. Only on demand will the icon file tried to be loaded and + * returned as pixmap object. + * To support also legacy template installations with the preview image installed as separate file, + * the local directory prefix in the QStandardPaths::GenericDataLocation is stored as well + * to search also those paths as fallback. + * + * If the icon name is empty, no matching icon file could be found or no image successfully loaded, + * the returned pixmap will default to a themed "kdevelop" icon image (128x128). + **/ +class KDEVPLATFORMLANGUAGE_EXPORT TemplatePreviewIcon +{ +public: + /** + * Creates a new template preview icon object + * + * @param iconName the raw icon name as specified in the template description + * @param archivePath absolute path to the template archive the icon is used by + * @param dataDir local directory prefix of the icon file if searching it through QStandardPaths::GenericDataLocation + **/ + TemplatePreviewIcon(const QString& iconName, const QString& archivePath, const QString& dataDir); + TemplatePreviewIcon(); + TemplatePreviewIcon(const TemplatePreviewIcon& other); + ~TemplatePreviewIcon(); + + TemplatePreviewIcon& operator=(const TemplatePreviewIcon& other); + +public: + /** + * Generates a pixmap to be used as preview for a template. + * Either from the preview image file as specified, or falling back + * to a themed "kdevelop" icon image (128x128). + * The pixmap is not cached, . + * + * @return pixmap to be used as preview for a template + **/ + QPixmap pixmap() const; + +private: + QSharedDataPointer d; +}; + +} + +Q_DECLARE_METATYPE(KDevelop::TemplatePreviewIcon) + +#endif // KDEVPLATFORM_TEMPLATEPREVIEWICON_H diff --git a/language/codegen/templatepreviewicon.cpp b/language/codegen/templatepreviewicon.cpp new file mode 100644 --- /dev/null +++ b/language/codegen/templatepreviewicon.cpp @@ -0,0 +1,115 @@ +/* This file is part of KDevelop + Copyright 2017 Friedrich W. H. Kossebau + + 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 "templatepreviewicon.h" + +#include "util/debug.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace KDevelop; + +class KDevelop::TemplatePreviewIconData : public QSharedData +{ +public: + QString iconName; + QString archivePath; + QString dataDir; +}; + + +TemplatePreviewIcon::TemplatePreviewIcon(const QString& iconName, const QString& archivePath, const QString& dataDir) + : d(new TemplatePreviewIconData) +{ + d->iconName = iconName; + d->archivePath = archivePath; + d->dataDir = dataDir; +} + +TemplatePreviewIcon::TemplatePreviewIcon() + : d(new TemplatePreviewIconData) +{ +} + +TemplatePreviewIcon::TemplatePreviewIcon(const TemplatePreviewIcon& other) + : d(other.d) +{ +} + +TemplatePreviewIcon::~TemplatePreviewIcon() = default; + +TemplatePreviewIcon& TemplatePreviewIcon::operator=(const TemplatePreviewIcon& other) +{ + if (this != &other) { + d = other.d; + } + + return *this; +} + +QPixmap TemplatePreviewIcon::pixmap() const +{ + if (!d->iconName.isEmpty()) { + // read icon from archive + QScopedPointer templateArchive; + if (QFileInfo(d->archivePath).completeSuffix() == QLatin1String("zip")) { + templateArchive.reset(new KZip(d->archivePath)); + } else { + templateArchive.reset(new KTar(d->archivePath)); + } + + if (templateArchive->open(QIODevice::ReadOnly)) { + const KArchiveFile* iconFile = templateArchive->directory()->file(d->iconName); + if (iconFile) { + const auto data = iconFile->data(); + QPixmap pixmap; + const bool loadSuccess = pixmap.loadFromData(iconFile->data()); + if (loadSuccess) { + return pixmap; + } + qCWarning(LANGUAGE) << "Could not load preview icon" << d->iconName << "from" << d->archivePath; + } + } + + // support legacy templates with image files installed separately in the filesystem + const QString iconFilePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->dataDir + d->iconName); + if (!iconFilePath.isEmpty()) { + QPixmap pixmap(iconFilePath); + if (!pixmap.isNull()) { + return pixmap; + } + qCWarning(LANGUAGE) << "Could not load preview icon" << iconFilePath << "as wanted for" << d->archivePath; + } + } + + // try theme icon or default to a kdevelop icon + // QIcon::hasThemeIcon for empty string can yield true with some engines, not wanted here + const bool isThemeIcon = (!d->iconName.isEmpty() && QIcon::hasThemeIcon(d->iconName)); + const QString iconName = isThemeIcon ? d->iconName : QStringLiteral("kdevelop"); + + const QIcon icon = QIcon::fromTheme(iconName); + return icon.pixmap(128, 128); +} diff --git a/language/codegen/templatesmodel.h b/language/codegen/templatesmodel.h --- a/language/codegen/templatesmodel.h +++ b/language/codegen/templatesmodel.h @@ -40,7 +40,9 @@ * We use QStandardPaths with the GenericData type and create a filter string as such: * \li templates: typePrefix "/templates/" * \li descriptions: typePrefix "/template_descriptions/" - * \li previews: typePrefix "/template_previews/" + * + * Preview images are directly read from the archives on demand (or for legacy + * template installations from typePrefix "/template_previews/"). * * @sa ITemplateProvider::templatesModel() **/ @@ -56,7 +58,7 @@ enum TemplateRole { DescriptionFileRole = Qt::UserRole + 1, ///< Template description file name - IconNameRole = Qt::UserRole + 2, ///< Template icon name + IconRole = Qt::UserRole + 2, ///< Template preview icon, returns a TemplatePreviewIcon CommentRole = Qt::UserRole + 3, ///< Template comment ArchiveFileRole = Qt::UserRole + 4 ///< Template archive file name }; diff --git a/language/codegen/templatesmodel.cpp b/language/codegen/templatesmodel.cpp --- a/language/codegen/templatesmodel.cpp +++ b/language/codegen/templatesmodel.cpp @@ -19,6 +19,8 @@ */ #include "templatesmodel.h" + +#include "templatepreviewicon.h" #include "util/debug.h" #include @@ -146,20 +148,13 @@ QString name = general.readEntry("Name"); QString category = general.readEntry("Category"); QString comment = general.readEntry("Comment"); + TemplatePreviewIcon icon(general.readEntry("Icon"), templateArchive, d->resourceFilter(TemplatesModelPrivate::Preview)); 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 = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - d->resourceFilter(TemplatesModelPrivate::Preview, general.readEntry("Icon"))); - if (QFile::exists(icon)) { - templateItem->setData(icon, IconNameRole); - } - } + templateItem->setData(QVariant::fromValue(icon), IconRole); } } @@ -300,25 +295,6 @@ 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(iconEntry); - const QString saveDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ resourceFilter(Preview); - QDir dir(saveDir); - if (!dir.exists()) { - dir.mkpath(QStringLiteral(".")); - } - iconFile->copyTo(saveDir); - QFileInfo iconInfo(saveDir + templateEntry->name()); - QFile::rename(iconInfo.absoluteFilePath(), saveDir + templateInfo.baseName() + '.' + iconInfo.suffix()); - } - } } else { diff --git a/language/codegen/tests/test_templatesmodel.cpp b/language/codegen/tests/test_templatesmodel.cpp --- a/language/codegen/tests/test_templatesmodel.cpp +++ b/language/codegen/tests/test_templatesmodel.cpp @@ -68,7 +68,6 @@ QStandardItem* item = items.first(); QCOMPARE(item->data(TemplatesModel::CommentRole).toString(), QStringLiteral("Describes a class using YAML syntax")); - QVERIFY(item->data(TemplatesModel::IconNameRole).toString().isEmpty()); QString descriptionFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kdevcodegentest/template_descriptions/test_yaml.desktop"; diff --git a/plugins/appwizard/projectselectionpage.cpp b/plugins/appwizard/projectselectionpage.cpp --- a/plugins/appwizard/projectselectionpage.cpp +++ b/plugins/appwizard/projectselectionpage.cpp @@ -23,6 +23,7 @@ #include #include +#include "language/codegen/templatepreviewicon.h" #include @@ -122,16 +123,11 @@ void ProjectSelectionPage::itemChanged( const QModelIndex& current) { - QString picPath = current.data( KDevelop::TemplatesModel::IconNameRole ).toString(); - if( picPath.isEmpty() ) { - const QIcon icon = QIcon::fromTheme(QStringLiteral("kdevelop")); - ui->icon->setPixmap(icon.pixmap(128, 128)); - ui->icon->setFixedHeight(128); - } else { - QPixmap pixmap( picPath ); - ui->icon->setPixmap( pixmap ); - ui->icon->setFixedHeight( pixmap.height() ); - } + TemplatePreviewIcon icon = current.data(KDevelop::TemplatesModel::IconRole).value(); + + QPixmap pixmap = icon.pixmap(); + ui->icon->setPixmap(pixmap); + ui->icon->setFixedHeight(pixmap.height()); // header name is either from this index directly or the parents if we show the combo box const QVariant headerData = ui->templateType->isVisible() ? current.parent().data()