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"