File indexing completed on 2024-04-28 04:37:48

0001 /*
0002     SPDX-FileCopyrightText: 2001 Bernd Gehrmann <bernd@kdevelop.org>
0003     SPDX-FileCopyrightText: 2004-2005 Sascha Cunz <sascha@kdevelop.org>
0004     SPDX-FileCopyrightText: 2005 Ian Reinhart Geiser <ian@geiseri.com>
0005     SPDX-FileCopyrightText: 2007 Alexander Dymo <adymo@kdevelop.org>
0006     SPDX-FileCopyrightText: 2008 Evgeniy Ivanov <powerfox@kde.ru>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "appwizardplugin.h"
0012 
0013 #include <QAction>
0014 #include <QDir>
0015 #include <QDirIterator>
0016 #include <QFile>
0017 #include <QFileInfo>
0018 #include <QMimeType>
0019 #include <QMimeDatabase>
0020 #include <QStandardPaths>
0021 #include <QTemporaryDir>
0022 #include <QTextCodec>
0023 #include <QTextStream>
0024 #include <qplatformdefs.h>
0025 
0026 #include <KActionCollection>
0027 #include <KConfigGroup>
0028 #include <KIO/CopyJob>
0029 #include <KIO/DeleteJob>
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 #include <KParts/MainWindow>
0033 #include <KPluginFactory>
0034 #include <KSharedConfig>
0035 #include <KTar>
0036 #include <KZip>
0037 #include <KMacroExpander>
0038 
0039 #include <interfaces/icore.h>
0040 #include <interfaces/iprojectcontroller.h>
0041 #include <interfaces/iplugincontroller.h>
0042 #include <interfaces/iuicontroller.h>
0043 #include <interfaces/idocumentcontroller.h>
0044 #include <interfaces/context.h>
0045 #include <interfaces/contextmenuextension.h>
0046 #include <util/scopeddialog.h>
0047 #include <sublime/message.h>
0048 #include <vcs/vcsjob.h>
0049 #include <vcs/interfaces/icentralizedversioncontrol.h>
0050 #include <vcs/interfaces/idistributedversioncontrol.h>
0051 
0052 #include "appwizarddialog.h"
0053 #include "projectselectionpage.h"
0054 #include "projectvcspage.h"
0055 #include "projecttemplatesmodel.h"
0056 #include "debug.h"
0057 
0058 using namespace KDevelop;
0059 
0060 K_PLUGIN_FACTORY_WITH_JSON(AppWizardFactory, "kdevappwizard.json", registerPlugin<AppWizardPlugin>();)
0061 
0062 AppWizardPlugin::AppWizardPlugin(QObject *parent, const QVariantList &)
0063     : KDevelop::IPlugin(QStringLiteral("kdevappwizard"), parent)
0064 {
0065     setXMLFile(QStringLiteral("kdevappwizard.rc"));
0066 
0067     m_newFromTemplate = actionCollection()->addAction(QStringLiteral("project_new"));
0068     m_newFromTemplate->setIcon(QIcon::fromTheme(QStringLiteral("project-development-new-template")));
0069     m_newFromTemplate->setText(i18nc("@action", "New from Template..."));
0070     connect(m_newFromTemplate, &QAction::triggered, this, &AppWizardPlugin::slotNewProject);
0071     m_newFromTemplate->setToolTip( i18nc("@info:tooltip", "Generate a new project from a template") );
0072     m_newFromTemplate->setWhatsThis( i18nc("@info:whatsthis", "This starts KDevelop's application wizard. "
0073                                           "It helps you to generate a skeleton for your "
0074                                           "application from a set of templates.") );
0075 }
0076 
0077 AppWizardPlugin::~AppWizardPlugin()
0078 {
0079 }
0080 
0081 void AppWizardPlugin::slotNewProject()
0082 {
0083     model()->refresh();
0084 
0085     ScopedDialog<AppWizardDialog> dlg(core()->pluginController(), m_templatesModel);
0086 
0087     if (dlg->exec() == QDialog::Accepted)
0088     {
0089         QString project = createProject( dlg->appInfo() );
0090         if (!project.isEmpty())
0091         {
0092             core()->projectController()->openProject(QUrl::fromLocalFile(project));
0093 
0094             KConfig templateConfig(dlg->appInfo().appTemplate);
0095             KConfigGroup general(&templateConfig, "General");
0096             const QStringList fileArgs =
0097                 general.readEntry("ShowFilesAfterGeneration").split(QLatin1Char(','), Qt::SkipEmptyParts);
0098             for (const auto& fileArg : fileArgs) {
0099                 QString file = KMacroExpander::expandMacros(fileArg.trimmed(), m_variables);
0100                 if (QDir::isRelativePath(file)) {
0101                     file = m_variables[QStringLiteral("PROJECTDIR")] + QLatin1Char('/') + file;
0102                 }
0103                 core()->documentController()->openDocument(QUrl::fromUserInput(file));
0104             }
0105         } else {
0106             const QString messageText = i18n("Could not create project from template.");
0107             auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
0108             ICore::self()->uiController()->postMessage(message);
0109        }
0110     }
0111 }
0112 
0113 namespace
0114 {
0115 
0116 IDistributedVersionControl* toDVCS(IPlugin* plugin)
0117 {
0118     Q_ASSERT(plugin);
0119     return plugin->extension<IDistributedVersionControl>();
0120 }
0121 
0122 ICentralizedVersionControl* toCVCS(IPlugin* plugin)
0123 {
0124     Q_ASSERT(plugin);
0125     return plugin->extension<ICentralizedVersionControl>();
0126 }
0127 
0128 /*! Trouble while initializing version control. Show failure message to user. */
0129 void vcsError(const QString &errorMsg, QTemporaryDir &tmpdir, const QUrl &dest, const QString &details = QString())
0130 {
0131     QString displayDetails = details;
0132     if (displayDetails.isEmpty())
0133     {
0134         displayDetails = i18n("Please see the Version Control tool view.");
0135     }
0136     KMessageBox::detailedError(nullptr, errorMsg, displayDetails, i18nc("@title:window", "Version Control System Error"));
0137     KIO::del(dest, KIO::HideProgressInfo)->exec();
0138     tmpdir.remove();
0139 }
0140 
0141 /*! Setup distributed version control for a new project defined by @p info. Use @p scratchArea for temporary files  */
0142 bool initializeDVCS(IDistributedVersionControl* dvcs, const ApplicationInfo& info, QTemporaryDir& scratchArea)
0143 {
0144     Q_ASSERT(dvcs);
0145     qCDebug(PLUGIN_APPWIZARD) << "DVCS system is used, just initializing DVCS";
0146 
0147     const QUrl& dest = info.location;
0148     //TODO: check if we want to handle KDevelop project files (like now) or only SRC dir
0149     VcsJob* job = dvcs->init(dest);
0150     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded)
0151     {
0152         vcsError(i18n("Could not initialize DVCS repository"), scratchArea, dest);
0153         return false;
0154     }
0155     qCDebug(PLUGIN_APPWIZARD) << "Initializing DVCS repository:" << dest;
0156 
0157     qCDebug(PLUGIN_APPWIZARD) << "Checking for valid files in the DVCS repository:" << dest;
0158     job = dvcs->status({dest}, KDevelop::IBasicVersionControl::Recursive);
0159     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded)
0160     {
0161         vcsError(i18n("Could not check status of the DVCS repository"), scratchArea, dest);
0162         return false;
0163     }
0164 
0165     if (job->fetchResults().toList().isEmpty())
0166     {
0167         qCDebug(PLUGIN_APPWIZARD) << "No files to add, skipping commit in the DVCS repository:" << dest;
0168         return true;
0169     }
0170 
0171     job = dvcs->add({dest}, KDevelop::IBasicVersionControl::Recursive);
0172     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded)
0173     {
0174         vcsError(i18n("Could not add files to the DVCS repository"), scratchArea, dest);
0175         return false;
0176     }
0177 
0178     job = dvcs->commit(info.importCommitMessage, {dest},
0179                             KDevelop::IBasicVersionControl::Recursive);
0180     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded)
0181     {
0182         vcsError(i18n("Could not import project into %1.", dvcs->name()), scratchArea, dest, job ? job->errorString() : QString());
0183         return false;
0184     }
0185 
0186     return true; // We're good
0187 }
0188 
0189 /*! Setup version control for a new project defined by @p info. Use @p scratchArea for temporary files  */
0190 bool initializeCVCS(ICentralizedVersionControl* cvcs, const ApplicationInfo& info, QTemporaryDir& scratchArea)
0191 {
0192     Q_ASSERT(cvcs);
0193 
0194     qCDebug(PLUGIN_APPWIZARD) << "Importing" << info.sourceLocation << "to"
0195              << info.repository.repositoryServer();
0196     VcsJob* job = cvcs->import( info.importCommitMessage, QUrl::fromLocalFile(scratchArea.path()), info.repository);
0197     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded )
0198     {
0199         vcsError(i18n("Could not import project"), scratchArea, QUrl::fromUserInput(info.repository.repositoryServer()));
0200         return false;
0201     }
0202 
0203     qCDebug(PLUGIN_APPWIZARD) << "Checking out";
0204     job = cvcs->createWorkingCopy( info.repository, info.location, IBasicVersionControl::Recursive);
0205     if (!job || !job->exec() || job->status() != VcsJob::JobSucceeded )
0206     {
0207         vcsError(i18n("Could not checkout imported project"), scratchArea, QUrl::fromUserInput(info.repository.repositoryServer()));
0208         return false;
0209     }
0210 
0211     return true; // initialization phase complete
0212 }
0213 
0214 QString generateIdentifier( const QString& appname )
0215 {
0216     QString tmp = appname;
0217     QRegExp re(QStringLiteral("[^a-zA-Z0-9_]"));
0218     return tmp.replace(re, QStringLiteral("_"));
0219 }
0220 
0221 } // end anonymous namespace
0222 
0223 QString AppWizardPlugin::createProject(const ApplicationInfo& info)
0224 {
0225     QFileInfo templateInfo(info.appTemplate);
0226     if (!templateInfo.exists()) {
0227         qCWarning(PLUGIN_APPWIZARD) << "Project app template does not exist:" << info.appTemplate;
0228         return QString();
0229     }
0230 
0231     QString templateName = templateInfo.baseName();
0232     qCDebug(PLUGIN_APPWIZARD) << "Searching archive for template name:" << templateName;
0233 
0234     QString templateArchive;
0235     const QStringList filters = {templateName + QStringLiteral(".*")};
0236     const QStringList matchesPaths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kdevappwizard/templates/"), QStandardPaths::LocateDirectory);
0237     for (const QString& matchesPath : matchesPaths) {
0238         const QStringList files = QDir(matchesPath).entryList(filters);
0239         if(!files.isEmpty()) {
0240             templateArchive = matchesPath + files.first();
0241         }
0242     }
0243 
0244     if(templateArchive.isEmpty()) {
0245         qCWarning(PLUGIN_APPWIZARD) << "Template name does not exist in the template list";
0246         return QString();
0247     }
0248 
0249     qCDebug(PLUGIN_APPWIZARD) << "Using template archive:" << templateArchive;
0250 
0251     QUrl dest = info.location;
0252 
0253     //prepare variable substitution hash
0254     m_variables.clear();
0255     m_variables[QStringLiteral("APPNAME")] = info.name;
0256     m_variables[QStringLiteral("APPNAMEUC")] = generateIdentifier(info.name.toUpper());
0257     m_variables[QStringLiteral("APPNAMELC")] = info.name.toLower();
0258     m_variables[QStringLiteral("APPNAMEID")] = generateIdentifier(info.name);
0259     m_variables[QStringLiteral("PROJECTDIR")] = dest.toLocalFile();
0260     // backwards compatibility
0261     m_variables[QStringLiteral("dest")] = m_variables[QStringLiteral("PROJECTDIR")];
0262     m_variables[QStringLiteral("PROJECTDIRNAME")] = dest.fileName();
0263     m_variables[QStringLiteral("VERSIONCONTROLPLUGIN")] = info.vcsPluginName;
0264 
0265     KArchive* arch = nullptr;
0266     if( templateArchive.endsWith(QLatin1String(".zip")) )
0267     {
0268         arch = new KZip(templateArchive);
0269     }
0270     else
0271     {
0272         arch = new KTar(templateArchive, QStringLiteral("application/x-bzip"));
0273     }
0274     if (arch->open(QIODevice::ReadOnly))
0275     {
0276         QTemporaryDir tmpdir;
0277         QString unpackDir = tmpdir.path(); //the default value for all Centralized VCS
0278         IPlugin* plugin = core()->pluginController()->loadPlugin( info.vcsPluginName );
0279         if( info.vcsPluginName.isEmpty() || ( plugin && plugin->extension<KDevelop::IDistributedVersionControl>() ) )
0280         {
0281             if( !QFileInfo::exists( dest.toLocalFile() ) )
0282             {
0283                 QDir::root().mkpath( dest.toLocalFile() );
0284             }
0285             unpackDir = dest.toLocalFile(); //in DVCS we unpack template directly to the project's directory
0286         }
0287         else
0288         {
0289             QUrl url = KIO::upUrl(dest);
0290             if(!QFileInfo::exists(url.toLocalFile())) {
0291                 QDir::root().mkpath(url.toLocalFile());
0292             }
0293         }
0294 
0295         // estimate metadata files which should not be copied
0296         QStringList metaDataFileNames;
0297 
0298         // try by same name
0299         const KArchiveEntry *templateEntry =
0300             arch->directory()->entry(templateName + QLatin1String(".kdevtemplate"));
0301 
0302         // but could be different name, if e.g. downloaded, so make a guess
0303         if (!templateEntry || !templateEntry->isFile()) {
0304             const auto& entries = arch->directory()->entries();
0305             for (const auto& entryName : entries) {
0306                 if (entryName.endsWith(QLatin1String(".kdevtemplate"))) {
0307                     templateEntry = arch->directory()->entry(entryName);
0308                     break;
0309                 }
0310             }
0311         }
0312 
0313         if (templateEntry && templateEntry->isFile()) {
0314             metaDataFileNames << templateEntry->name();
0315 
0316             // check if a preview file is to be ignored
0317             const auto *templateFile = static_cast<const KArchiveFile*>(templateEntry);
0318             QTemporaryDir temporaryDir;
0319             templateFile->copyTo(temporaryDir.path());
0320 
0321             KConfig config(temporaryDir.path() + QLatin1Char('/') + templateEntry->name());
0322             KConfigGroup group(&config, "General");
0323             if (group.hasKey("Icon")) {
0324                 const KArchiveEntry* iconEntry = arch->directory()->entry(group.readEntry("Icon"));
0325                 if (iconEntry && iconEntry->isFile()) {
0326                     metaDataFileNames << iconEntry->name();
0327                 }
0328             }
0329         }
0330 
0331         if (!unpackArchive(arch->directory(), unpackDir, metaDataFileNames)) {
0332             QString errorMsg = i18n("Could not create new project");
0333             vcsError(errorMsg, tmpdir, QUrl::fromLocalFile(unpackDir));
0334             return QString();
0335         }
0336 
0337         if( !info.vcsPluginName.isEmpty() )
0338         {
0339             if (!plugin)
0340             {
0341                 // Red Alert, serious program corruption.
0342                 // This should never happen, the vcs dialog presented a list of vcs
0343                 // systems and now the chosen system doesn't exist anymore??
0344                 tmpdir.remove();
0345                 return QString();
0346             }
0347 
0348             IDistributedVersionControl* dvcs = toDVCS(plugin);
0349             ICentralizedVersionControl* cvcs = toCVCS(plugin);
0350             bool success = false;
0351             if (dvcs)
0352             {
0353                 success = initializeDVCS(dvcs, info, tmpdir);
0354             }
0355             else if (cvcs)
0356             {
0357                 success = initializeCVCS(cvcs, info, tmpdir);
0358             }
0359             else
0360             {
0361                 if (KMessageBox::Continue ==
0362                     KMessageBox::warningContinueCancel(nullptr,
0363                     QStringLiteral("Failed to initialize version control system, "
0364                     "plugin is neither VCS nor DVCS.")))
0365                     success = true;
0366             }
0367             if (!success) return QString();
0368         }
0369         tmpdir.remove();
0370     }else
0371     {
0372         qCDebug(PLUGIN_APPWIZARD) << "failed to open template archive";
0373         return QString();
0374     }
0375 
0376     QString projectFileName = QDir::cleanPath(dest.toLocalFile() + QLatin1Char('/') + info.name + QLatin1String(".kdev4"));
0377 
0378     // Loop through the new project directory and try to detect the first .kdev4 file.
0379     // If one is found this file will be used. So .kdev4 file can be stored in any subdirectory and the
0380     // project templates can be more complex.
0381     QDirIterator it(QDir::cleanPath( dest.toLocalFile()), QStringList() << QStringLiteral("*.kdev4"), QDir::NoFilter, QDirIterator::Subdirectories);
0382     if(it.hasNext() == true)
0383     {
0384         projectFileName = it.next();
0385     }
0386 
0387     qCDebug(PLUGIN_APPWIZARD) << "Returning" << projectFileName << QFileInfo::exists( projectFileName ) ;
0388 
0389     const QFileInfo projectFileInfo(projectFileName);
0390     if (!projectFileInfo.exists()) {
0391         qCDebug(PLUGIN_APPWIZARD) << "creating .kdev4 file";
0392         KSharedConfigPtr cfg = KSharedConfig::openConfig( projectFileName, KConfig::SimpleConfig );
0393         KConfigGroup project = cfg->group( "Project" );
0394         project.writeEntry( "Name", info.name );
0395         QString manager = QStringLiteral("KDevGenericManager");
0396 
0397         QDir d( dest.toLocalFile() );
0398         const auto data = ICore::self()->pluginController()->queryExtensionPlugins(QStringLiteral("org.kdevelop.IProjectFileManager"));
0399         for (const KPluginMetaData& info : data) {
0400             const auto filter = info.value(QStringLiteral("X-KDevelop-ProjectFilesFilter"), QStringList());
0401             if (!filter.isEmpty()) {
0402                 if (!d.entryList(filter).isEmpty()) {
0403                     manager = info.pluginId();
0404                     break;
0405                 }
0406             }
0407         }
0408         project.writeEntry( "Manager", manager );
0409         project.sync();
0410         cfg->sync();
0411         KConfigGroup project2 = cfg->group( "Project" );
0412         qCDebug(PLUGIN_APPWIZARD) << "kdev4 file contents:" << project2.readEntry("Name", "") << project2.readEntry("Manager", "" );
0413     }
0414 
0415     // create developer .kde4 file
0416     const QString developerProjectFileName = projectFileInfo.canonicalPath() + QLatin1String("/.kdev4/") + projectFileInfo.fileName();
0417 
0418     qCDebug(PLUGIN_APPWIZARD) << "creating developer .kdev4 file:" << developerProjectFileName;
0419     KSharedConfigPtr developerCfg = KSharedConfig::openConfig(developerProjectFileName, KConfig::SimpleConfig);
0420     KConfigGroup developerProjectGroup = developerCfg->group("Project");
0421     developerProjectGroup.writeEntry("VersionControlSupport", info.vcsPluginName);
0422     developerProjectGroup.sync();
0423 
0424     developerCfg->sync();
0425 
0426     return projectFileName;
0427 }
0428 
0429 bool AppWizardPlugin::unpackArchive(const KArchiveDirectory* dir, const QString& dest, const QStringList& skipList)
0430 {
0431     qCDebug(PLUGIN_APPWIZARD) << "unpacking dir:" << dir->name() << "to" << dest;
0432     const QStringList entries = dir->entries();
0433     qCDebug(PLUGIN_APPWIZARD) << "entries:" << entries.join(QLatin1Char(','));
0434 
0435     //This extra tempdir is needed just for the files that have special names,
0436     //which may contain macros also files contain content with macros. So the
0437     //easiest way to extract the files from the archive and then rename them
0438     //and replace the macros is to use a tempdir and copy the file (and
0439     //replacing while copying). This also allows one to easily remove all files,
0440     //by just unlinking the tempdir
0441     QTemporaryDir tdir;
0442 
0443     bool ret = true;
0444 
0445     for (const auto& entryName : entries) {
0446         if (skipList.contains(entryName)) {
0447             continue;
0448         }
0449 
0450         const auto entry = dir->entry(entryName);
0451         if (entry->isDirectory()) {
0452             const auto* subdir = static_cast<const KArchiveDirectory*>(entry);
0453             const QString newdest = dest + QLatin1Char('/') + KMacroExpander::expandMacros(subdir->name(), m_variables);
0454             if( !QFileInfo::exists( newdest ) )
0455             {
0456                 QDir::root().mkdir( newdest  );
0457             }
0458             ret |= unpackArchive(subdir, newdest);
0459         }
0460         else if (entry->isFile()) {
0461             const auto* file = static_cast<const KArchiveFile*>(entry);
0462             file->copyTo(tdir.path());
0463             const QString destName = dest + QLatin1Char('/') + file->name();
0464             if (!copyFileAndExpandMacros(QDir::cleanPath(tdir.path() + QLatin1Char('/') + file->name()),
0465                     KMacroExpander::expandMacros(destName, m_variables)))
0466             {
0467                 KMessageBox::error(nullptr, i18n("The file %1 cannot be created.", dest));
0468                 return false;
0469             }
0470         }
0471     }
0472     tdir.remove();
0473     return ret;
0474 }
0475 
0476 bool AppWizardPlugin::copyFileAndExpandMacros(const QString &source, const QString &dest)
0477 {
0478     qCDebug(PLUGIN_APPWIZARD) << "copy:" << source << "to" << dest;
0479     QMimeDatabase db;
0480     QMimeType mime = db.mimeTypeForFile(source);
0481     if( !mime.inherits(QStringLiteral("text/plain")) )
0482     {
0483         KIO::CopyJob* job = KIO::copy( QUrl::fromUserInput(source), QUrl::fromUserInput(dest), KIO::HideProgressInfo );
0484         if( !job->exec() )
0485         {
0486             return false;
0487         }
0488         return true;
0489     } else
0490     {
0491         QFile inputFile(source);
0492         QFile outputFile(dest);
0493 
0494 
0495         if (inputFile.open(QFile::ReadOnly) && outputFile.open(QFile::WriteOnly))
0496         {
0497             QTextStream input(&inputFile);
0498             input.setCodec(QTextCodec::codecForName("UTF-8"));
0499             QTextStream output(&outputFile);
0500             output.setCodec(QTextCodec::codecForName("UTF-8"));
0501             while(!input.atEnd())
0502             {
0503                 QString line = input.readLine();
0504                 output << KMacroExpander::expandMacros(line, m_variables) << "\n";
0505             }
0506 #ifndef Q_OS_WIN
0507             // Preserve file mode...
0508             QT_STATBUF statBuf;
0509             QT_FSTAT(inputFile.handle(), &statBuf);
0510             // Unix only, won't work in Windows, maybe KIO::chmod could be used
0511             ::fchmod(outputFile.handle(), statBuf.st_mode);
0512 #endif
0513             return true;
0514         }
0515         else
0516         {
0517             inputFile.close();
0518             outputFile.close();
0519             return false;
0520         }
0521     }
0522 }
0523 KDevelop::ContextMenuExtension AppWizardPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent)
0524 {
0525     Q_UNUSED(parent);
0526     KDevelop::ContextMenuExtension ext;
0527     if ( context->type() != KDevelop::Context::ProjectItemContext || !static_cast<KDevelop::ProjectItemContext*>(context)->items().isEmpty() ) {
0528         return ext;
0529     }
0530     ext.addAction(KDevelop::ContextMenuExtension::ProjectGroup, m_newFromTemplate);
0531     return ext;
0532 }
0533 
0534 ProjectTemplatesModel* AppWizardPlugin::model() const
0535 {
0536     if(!m_templatesModel) {
0537         auto* self = const_cast<AppWizardPlugin*>(this);
0538         m_templatesModel = new ProjectTemplatesModel(self);
0539     }
0540     return m_templatesModel;
0541 }
0542 
0543 QAbstractItemModel* AppWizardPlugin::templatesModel() const
0544 {
0545     return model();
0546 }
0547 
0548 QString AppWizardPlugin::knsConfigurationFile() const
0549 {
0550     return QStringLiteral("kdevappwizard.knsrc");
0551 }
0552 
0553 QStringList AppWizardPlugin::supportedMimeTypes() const
0554 {
0555     const QStringList types{
0556         QStringLiteral("application/x-desktop"),
0557         QStringLiteral("application/x-bzip-compressed-tar"),
0558         QStringLiteral("application/zip"),
0559     };
0560     return types;
0561 }
0562 
0563 QIcon AppWizardPlugin::icon() const
0564 {
0565     return QIcon::fromTheme(QStringLiteral("project-development-new-template"));
0566 }
0567 
0568 QString AppWizardPlugin::name() const
0569 {
0570     return i18n("Project Templates");
0571 }
0572 
0573 void AppWizardPlugin::loadTemplate(const QString& fileName)
0574 {
0575     model()->loadTemplateFile(fileName);
0576 }
0577 
0578 void AppWizardPlugin::reload()
0579 {
0580     model()->refresh();
0581 }
0582 
0583 
0584 #include "appwizardplugin.moc"
0585 #include "moc_appwizardplugin.cpp"