File indexing completed on 2024-05-05 04:39:50

0001 /*
0002     SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "templateselectionpage.h"
0008 
0009 #include "templateclassassistant.h"
0010 #include "templatepreview.h"
0011 
0012 #include <language/codegen/templatesmodel.h>
0013 #include <language/codegen/sourcefiletemplate.h>
0014 #include <language/codegen/documentchangeset.h>
0015 #include <language/codegen/templaterenderer.h>
0016 #include <language/interfaces/ilanguagesupport.h>
0017 #include <interfaces/icore.h>
0018 #include <interfaces/iproject.h>
0019 #include <interfaces/iprojectcontroller.h>
0020 #include <interfaces/isession.h>
0021 #include <util/scopeddialog.h>
0022 
0023 #include "ui_templateselection.h"
0024 
0025 #include <QFileDialog>
0026 #include <QPushButton>
0027 #include <QTemporaryDir>
0028 
0029 #include <KConfigGroup>
0030 #include <KLocalizedString>
0031 #include <KNSWidgets/Button>
0032 #include <KTextEditor/Document>
0033 
0034 using namespace KDevelop;
0035 
0036 static const char LastUsedTemplateEntry[] = "LastUsedTemplate";
0037 static const char FileTemplatesGroup[] = "SourceFileTemplates";
0038 
0039 class KDevelop::TemplateSelectionPagePrivate
0040 {
0041 public:
0042     explicit TemplateSelectionPagePrivate(TemplateSelectionPage* page_)
0043     : page(page_)
0044     {}
0045 
0046     TemplateSelectionPage* page;
0047     Ui::TemplateSelection* ui;
0048     QString selectedTemplate;
0049     TemplateClassAssistant* assistant;
0050     TemplatesModel* model;
0051 
0052     void currentTemplateChanged(const QModelIndex& index);
0053     void handleNewStuffDialogFinished();
0054     void loadFileClicked();
0055     void previewTemplate(const QString& templateFile);
0056 };
0057 
0058 void TemplateSelectionPagePrivate::currentTemplateChanged(const QModelIndex& index)
0059 {
0060     // delete preview tabs
0061     if (!index.isValid() || index.model()->hasChildren(index))
0062     {
0063         // invalid or has child
0064         assistant->setValid(assistant->currentPage(), false);
0065         ui->previewLabel->setVisible(false);
0066         ui->tabWidget->setVisible(false);
0067     } else {
0068         selectedTemplate = model->data(index, TemplatesModel::DescriptionFileRole).toString();
0069         assistant->setValid(assistant->currentPage(), true);
0070         previewTemplate(selectedTemplate);
0071         ui->previewLabel->setVisible(true);
0072         ui->tabWidget->setVisible(true);
0073         ui->previewLabel->setText(i18nc("%1: template comment", "<b>Preview:</b> %1",
0074                                         index.data(TemplatesModel::CommentRole).toString()));
0075     }
0076 }
0077 
0078 void TemplateSelectionPagePrivate::previewTemplate(const QString& file)
0079 {
0080     SourceFileTemplate fileTemplate(file);
0081     if (!fileTemplate.isValid() || fileTemplate.outputFiles().isEmpty()) {
0082         return;
0083     }
0084 
0085     TemplatePreviewRenderer renderer;
0086     // set default option values
0087     if (fileTemplate.hasCustomOptions()) {
0088         QVariantHash extraVars;
0089         const auto& optionGroups = fileTemplate.customOptions(&renderer);
0090         for (const auto& optionGroup : optionGroups) {
0091             for (const auto& entry : optionGroup.options) {
0092                 extraVars[entry.name] = entry.value;
0093             }
0094         }
0095         renderer.addVariables(extraVars);
0096     }
0097     renderer.setEmptyLinesPolicy(TemplateRenderer::TrimEmptyLines);
0098 
0099     QTemporaryDir dir;
0100     QUrl base = QUrl::fromLocalFile(dir.path() + QLatin1Char('/'));
0101     QHash<QString, QUrl> fileUrls;
0102     const auto outputFiles = fileTemplate.outputFiles();
0103     for (const SourceFileTemplate::OutputFile& out : outputFiles) {
0104         QUrl url = base.resolved(QUrl(renderer.render(out.outputName)));
0105         fileUrls.insert(out.identifier, url);
0106     }
0107     DocumentChangeSet changes = renderer.renderFileTemplate(fileTemplate, base, fileUrls);
0108     changes.setActivationPolicy(DocumentChangeSet::DoNotActivate);
0109     changes.setUpdateHandling(DocumentChangeSet::NoUpdate);
0110     DocumentChangeSet::ChangeResult result = changes.applyAllChanges();
0111     if (!result) {
0112         return;
0113     }
0114 
0115     int idx = 0;
0116     for (const SourceFileTemplate::OutputFile& out : outputFiles) {
0117         TemplatePreview* preview = nullptr;
0118         if (ui->tabWidget->count() > idx) {
0119             // reuse existing tab
0120             preview = qobject_cast<TemplatePreview*>(ui->tabWidget->widget(idx));
0121             ui->tabWidget->setTabText(idx, out.label);
0122             Q_ASSERT(preview);
0123         } else {
0124             // create new tabs on demand
0125             preview = new TemplatePreview(page);
0126             ui->tabWidget->addTab(preview, out.label);
0127         }
0128         preview->document()->openUrl(fileUrls.value(out.identifier));
0129         ++idx;
0130     }
0131     // remove superfluous tabs from last time
0132     while (ui->tabWidget->count() > fileUrls.size()) {
0133         delete ui->tabWidget->widget(fileUrls.size());
0134     }
0135     return;
0136 }
0137 
0138 void TemplateSelectionPagePrivate::handleNewStuffDialogFinished()
0139 {
0140     model->refresh();
0141 }
0142 
0143 void TemplateSelectionPagePrivate::loadFileClicked()
0144 {
0145     const QStringList filters{
0146         QStringLiteral("application/x-desktop"),
0147         QStringLiteral("application/x-bzip-compressed-tar"),
0148         QStringLiteral("application/zip")
0149     };
0150     ScopedDialog<QFileDialog> dlg(page);
0151     dlg->setMimeTypeFilters(filters);
0152     dlg->setFileMode(QFileDialog::ExistingFiles);
0153 
0154     if (!dlg->exec())
0155     {
0156         return;
0157     }
0158 
0159     const auto selectedFiles = dlg->selectedFiles();
0160     for (const QString& fileName : selectedFiles) {
0161         QString destination = model->loadTemplateFile(fileName);
0162         QModelIndexList indexes = model->templateIndexes(destination);
0163         int n = indexes.size();
0164         if (n > 1)
0165         {
0166             ui->view->setCurrentIndex(indexes[1]);
0167         }
0168     }
0169 }
0170 
0171 void TemplateSelectionPage::saveConfig()
0172 {
0173     KSharedConfigPtr config;
0174     if (IProject* project = ICore::self()->projectController()->findProjectForUrl(d->assistant->baseUrl()))
0175     {
0176         config = project->projectConfiguration();
0177     }
0178     else
0179     {
0180         config = ICore::self()->activeSession()->config();
0181     }
0182 
0183     KConfigGroup group(config, FileTemplatesGroup);
0184     group.writeEntry(LastUsedTemplateEntry, d->selectedTemplate);
0185     group.sync();
0186 }
0187 
0188 TemplateSelectionPage::TemplateSelectionPage(TemplateClassAssistant* parent)
0189 : QWidget(parent)
0190 , d(new TemplateSelectionPagePrivate(this))
0191 {
0192     d->assistant = parent;
0193 
0194     d->ui = new Ui::TemplateSelection;
0195     d->ui->setupUi(this);
0196 
0197     d->model = new TemplatesModel(QStringLiteral("kdevfiletemplates"), this);
0198     d->model->refresh();
0199 
0200     d->ui->view->setLevels(3);
0201     d->ui->view->setHeaderLabels(QStringList{
0202         i18nc("@title:column", "Language"),
0203         i18nc("@title:column", "Framework"),
0204         i18nc("@title:column", "Template")
0205     });
0206     d->ui->view->setModel(d->model);
0207 
0208     connect(d->ui->view, &MultiLevelListView::currentIndexChanged,
0209             this, [&] (const QModelIndex& index) { d->currentTemplateChanged(index); });
0210 
0211     QModelIndex templateIndex;
0212     while (d->model->hasIndex(0, 0, templateIndex))
0213     {
0214         templateIndex = d->model->index(0, 0, templateIndex);
0215     }
0216 
0217     KSharedConfigPtr config;
0218     if (IProject* project = ICore::self()->projectController()->findProjectForUrl(d->assistant->baseUrl()))
0219     {
0220         config = project->projectConfiguration();
0221     }
0222     else
0223     {
0224         config = ICore::self()->activeSession()->config();
0225     }
0226 
0227     KConfigGroup group(config, FileTemplatesGroup);
0228     QString lastTemplate = group.readEntry(LastUsedTemplateEntry);
0229 
0230     QModelIndexList indexes = d->model->match(d->model->index(0, 0), TemplatesModel::DescriptionFileRole, lastTemplate, 1, Qt::MatchRecursive);
0231 
0232     if (!indexes.isEmpty())
0233     {
0234         templateIndex = indexes.first();
0235     }
0236 
0237     d->ui->view->setCurrentIndex(templateIndex);
0238 
0239     auto* getMoreButton = new KNSWidgets::Button(i18nc("@action:button", "Get More Templates..."),
0240                                                  QStringLiteral("kdevfiletemplates.knsrc"), d->ui->view);
0241     connect(getMoreButton, &KNSWidgets::Button::dialogFinished, this, [&]() {
0242         d->handleNewStuffDialogFinished();
0243     });
0244     d->ui->view->addWidget(0, getMoreButton);
0245 
0246     auto* loadButton = new QPushButton(QIcon::fromTheme(QStringLiteral("application-x-archive")), i18nc("@action:button", "Load Template from File"), d->ui->view);
0247     connect (loadButton, &QPushButton::clicked, this, [&] { d->loadFileClicked(); });
0248     d->ui->view->addWidget(0, loadButton);
0249 
0250     d->ui->view->setContentsMargins(0, 0, 0, 0);
0251 }
0252 
0253 TemplateSelectionPage::~TemplateSelectionPage()
0254 {
0255     delete d->ui;
0256     delete d;
0257 }
0258 
0259 QSize TemplateSelectionPage::minimumSizeHint() const
0260 {
0261     return QSize(400, 600);
0262 }
0263 
0264 QString TemplateSelectionPage::selectedTemplate() const
0265 {
0266     return d->selectedTemplate;
0267 }
0268 
0269 #include "moc_templateselectionpage.cpp"