File indexing completed on 2024-05-05 16:45:36
0001 /* 0002 SPDX-FileCopyrightText: 2008 Hamish Rodda <rodda@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "outputpage.h" 0008 #include "ui_outputlocation.h" 0009 #include "debug.h" 0010 0011 #include <language/codegen/sourcefiletemplate.h> 0012 #include <language/codegen/templaterenderer.h> 0013 #include <KConfigGroup> 0014 #include <KSharedConfig> 0015 #include <KLocalizedString> 0016 #include <KUrlRequester> 0017 0018 #include <QSpinBox> 0019 #include <QLabel> 0020 0021 namespace KDevelop { 0022 0023 struct OutputPagePrivate 0024 { 0025 explicit OutputPagePrivate(OutputPage* page_) 0026 : page(page_) 0027 , output(nullptr) 0028 { } 0029 OutputPage* page; 0030 Ui::OutputLocationDialog* output; 0031 0032 QHash<QString, KUrlRequester*> outputFiles; 0033 QHash<QString, QSpinBox*> outputLines; 0034 QHash<QString, QSpinBox*> outputColumns; 0035 QList<QLabel*> labels; 0036 0037 QHash<QString, QUrl> defaultUrls; 0038 QHash<QString, QUrl> lowerCaseUrls; 0039 QStringList fileIdentifiers; 0040 0041 void updateRanges(QSpinBox* line, QSpinBox* column, bool enable); 0042 /** 0043 * This implementation simply enables the position widgets on a file that exists. 0044 * Derived classes should overload to set the ranges where class generation should be allowed 0045 * 0046 * @param field the name of the file to be generated (Header, Implementation, etc) 0047 */ 0048 void updateFileRange(const QString& field); 0049 void updateFileNames(); 0050 void validate(); 0051 }; 0052 0053 void OutputPagePrivate::updateRanges(QSpinBox* line, QSpinBox* column, bool enable) 0054 { 0055 qCDebug(PLUGIN_FILETEMPLATES) << "Updating Ranges, file exists: " << enable; 0056 line->setEnabled(enable); 0057 column->setEnabled(enable); 0058 } 0059 0060 void OutputPagePrivate::updateFileRange(const QString& field) 0061 { 0062 const auto outputFileIt = outputFiles.constFind(field); 0063 if (outputFileIt == outputFiles.constEnd()) { 0064 return; 0065 } 0066 0067 const QString url = (*outputFileIt)->url().toLocalFile(); 0068 QFileInfo info(url); 0069 0070 updateRanges(outputLines[field], outputColumns[field], info.exists() && !info.isDir()); 0071 0072 validate(); 0073 } 0074 0075 void OutputPagePrivate::updateFileNames() 0076 { 0077 bool lower = output->lowerFilenameCheckBox->isChecked(); 0078 0079 const QHash<QString, QUrl> urls = lower ? lowerCaseUrls : defaultUrls; 0080 for (QHash<QString, KUrlRequester*>::const_iterator it = outputFiles.constBegin(); 0081 it != outputFiles.constEnd(); ++it) 0082 { 0083 const QUrl url = urls.value(it.key()); 0084 if (!url.isEmpty()) 0085 { 0086 it.value()->setUrl(url); 0087 } 0088 } 0089 0090 //Save the setting for next time 0091 KConfigGroup codegenGroup( KSharedConfig::openConfig(), "CodeGeneration" ); 0092 codegenGroup.writeEntry( "LowerCaseFilenames", output->lowerFilenameCheckBox->isChecked() ); 0093 0094 validate(); 0095 } 0096 0097 void OutputPagePrivate::validate() 0098 { 0099 QStringList invalidFiles; 0100 for(QHash< QString, KUrlRequester* >::const_iterator it = outputFiles.constBegin(); 0101 it != outputFiles.constEnd(); ++it) 0102 { 0103 if (!it.value()->url().isValid()) { 0104 invalidFiles << it.key(); 0105 } else if (it.value()->url().isLocalFile() && !QFileInfo(it.value()->url().adjusted(QUrl::RemoveFilename).toLocalFile()).isWritable()) { 0106 invalidFiles << it.key(); 0107 } 0108 } 0109 0110 bool valid = invalidFiles.isEmpty(); 0111 if (valid) { 0112 output->messageWidget->animatedHide(); 0113 } else { 0114 std::sort(invalidFiles.begin(), invalidFiles.end()); 0115 output->messageWidget->setMessageType(KMessageWidget::Error); 0116 output->messageWidget->setCloseButtonVisible(false); 0117 output->messageWidget->setText(i18np("Invalid output file: %2", "Invalid output files: %2", invalidFiles.count(), invalidFiles.join(QLatin1String(", ")))); 0118 output->messageWidget->animatedShow(); 0119 } 0120 emit page->isValid(valid); 0121 } 0122 0123 OutputPage::OutputPage(QWidget* parent) 0124 : QWidget(parent) 0125 , d(new OutputPagePrivate(this)) 0126 { 0127 d->output = new Ui::OutputLocationDialog; 0128 d->output->setupUi(this); 0129 d->output->messageWidget->setVisible(false); 0130 0131 connect(d->output->lowerFilenameCheckBox, &QCheckBox::stateChanged, 0132 this, [&] { d->updateFileNames(); }); 0133 } 0134 0135 OutputPage::~OutputPage() 0136 { 0137 delete d->output; 0138 delete d; 0139 } 0140 0141 void OutputPage::prepareForm(const SourceFileTemplate& fileTemplate) 0142 { 0143 // First clear any existing file configurations 0144 // This can happen when going back and forth between assistant pages 0145 d->fileIdentifiers.clear(); 0146 d->defaultUrls.clear(); 0147 d->lowerCaseUrls.clear(); 0148 0149 while (d->output->urlFormLayout->count() > 0) 0150 { 0151 d->output->urlFormLayout->takeAt(0); 0152 } 0153 while (d->output->positionFormLayout->count() > 0) 0154 { 0155 d->output->positionFormLayout->takeAt(0); 0156 } 0157 0158 qDeleteAll(d->outputFiles); 0159 qDeleteAll(d->outputLines); 0160 qDeleteAll(d->outputColumns); 0161 qDeleteAll(d->labels); 0162 0163 d->outputFiles.clear(); 0164 d->outputLines.clear(); 0165 d->outputColumns.clear(); 0166 d->labels.clear(); 0167 0168 const auto outputFiles = fileTemplate.outputFiles(); 0169 0170 const int outputFilesCount = outputFiles.count(); 0171 d->output->urlGroupBox->setTitle(i18ncp("@title:group", "Output File", "Output Files", outputFilesCount)); 0172 d->output->positionGroupBox->setTitle(i18ncp("@title:group", "Location within Existing File", "Location within Existing Files", outputFilesCount)); 0173 0174 for (const SourceFileTemplate::OutputFile& file : outputFiles) { 0175 const QString id = file.identifier; 0176 d->fileIdentifiers << id; 0177 0178 const QString fileLabelText = i18nc("@label:chooser file name arg", "%1:", file.label); 0179 auto* label = new QLabel(fileLabelText, this); 0180 d->labels << label; 0181 auto* requester = new KUrlRequester(this); 0182 requester->setMode( KFile::File | KFile::LocalOnly ); 0183 0184 connect(requester, &KUrlRequester::textChanged, this, [this, id] () { d->updateFileRange(id); }); 0185 0186 d->output->urlFormLayout->addRow(label, requester); 0187 d->outputFiles.insert(file.identifier, requester); 0188 0189 label = new QLabel(fileLabelText, this); 0190 d->labels << label; 0191 auto* layout = new QHBoxLayout; 0192 0193 auto line = new QSpinBox(this); 0194 line->setPrefix(i18n("Line: ")); 0195 line->setValue(0); 0196 line->setMinimum(0); 0197 layout->addWidget(line); 0198 0199 auto column = new QSpinBox(this); 0200 column->setPrefix(i18n("Column: ")); 0201 column->setValue(0); 0202 column->setMinimum(0); 0203 layout->addWidget(column); 0204 0205 d->output->positionFormLayout->addRow(label, layout); 0206 d->outputLines.insert(file.identifier, line); 0207 d->outputColumns.insert(file.identifier, column); 0208 } 0209 } 0210 0211 void OutputPage::loadFileTemplate(const SourceFileTemplate& fileTemplate, 0212 const QUrl& _baseUrl, 0213 TemplateRenderer* renderer) 0214 { 0215 QUrl baseUrl = _baseUrl; 0216 if (!baseUrl.path().endsWith(QLatin1Char('/'))) { 0217 baseUrl.setPath(baseUrl.path() + QLatin1Char('/')); 0218 } 0219 0220 KConfigGroup codegenGroup( KSharedConfig::openConfig(), "CodeGeneration" ); 0221 bool lower = codegenGroup.readEntry( "LowerCaseFilenames", true ); 0222 d->output->lowerFilenameCheckBox->setChecked(lower); 0223 0224 const auto outputFiles = fileTemplate.outputFiles(); 0225 for (const SourceFileTemplate::OutputFile& file : outputFiles) { 0226 d->fileIdentifiers << file.identifier; 0227 0228 QUrl url = baseUrl.resolved(QUrl(renderer->render(file.outputName))); 0229 d->defaultUrls.insert(file.identifier, url); 0230 0231 url = baseUrl.resolved(QUrl(renderer->render(file.outputName).toLower())); 0232 d->lowerCaseUrls.insert(file.identifier, url); 0233 } 0234 0235 d->updateFileNames(); 0236 } 0237 0238 QHash< QString, QUrl > OutputPage::fileUrls() const 0239 { 0240 QHash<QString, QUrl> urls; 0241 for (QHash<QString, KUrlRequester*>::const_iterator it = d->outputFiles.constBegin(); it != d->outputFiles.constEnd(); ++it) 0242 { 0243 urls.insert(it.key(), it.value()->url()); 0244 } 0245 return urls; 0246 } 0247 0248 QHash< QString, KTextEditor::Cursor > OutputPage::filePositions() const 0249 { 0250 QHash<QString, KTextEditor::Cursor> positions; 0251 for (const QString& identifier : qAsConst(d->fileIdentifiers)) { 0252 positions.insert(identifier, KTextEditor::Cursor(d->outputLines[identifier]->value(), d->outputColumns[identifier]->value())); 0253 } 0254 return positions; 0255 } 0256 0257 void OutputPage::setFocusToFirstEditWidget() 0258 { 0259 d->output->lowerFilenameCheckBox->setFocus(); 0260 } 0261 0262 } 0263 0264 #include "moc_outputpage.cpp"