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

0001 /*
0002     SPDX-FileCopyrightText: 2008 Hamish Rodda <rodda@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "licensepage.h"
0008 
0009 #include "ui_licensechooser.h"
0010 #include "debug.h"
0011 
0012 #include <KSharedConfig>
0013 #include <KLocalizedString>
0014 #include <KEMailSettings>
0015 #include <KMessageBox>
0016 #include <KConfigGroup>
0017 
0018 #include <QDirIterator>
0019 #include <QStandardPaths>
0020 
0021 namespace KDevelop {
0022 
0023 struct LicensePagePrivate
0024 {
0025     struct LicenseInfo
0026     {
0027         QString name;
0028         QString path;
0029         QString contents;
0030         bool operator< (const LicenseInfo& o) const
0031         {
0032             return name.localeAwareCompare(o.name) < 0;
0033         }
0034     };
0035     using LicenseList = QVector<LicenseInfo>;
0036 
0037 
0038     explicit LicensePagePrivate(LicensePage* page_)
0039     : license(nullptr)
0040     , page(page_)
0041     {
0042     }
0043 
0044     // methods
0045     void initializeLicenses();
0046     QString readLicense(int licenseIndex);
0047     bool saveLicense();
0048     // slots
0049     void licenseComboChanged(int license);
0050 
0051     Ui::LicenseChooserDialog* license;
0052     LicenseList availableLicenses;
0053     LicensePage* page;
0054 };
0055 
0056 //! Read all the license files in the global and local config dirs
0057 void LicensePagePrivate::initializeLicenses()
0058 {
0059     qCDebug(PLUGIN_FILETEMPLATES) << "Searching for available licenses";
0060     const QStringList licenseDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kdevcodegen/licenses"), QStandardPaths::LocateDirectory);
0061 
0062     //Iterate through the possible directories that contain licenses, and load their names
0063     for (const QString& currentDir : licenseDirs) {
0064         QDirIterator it(currentDir, QDir::Files | QDir::Readable);
0065         while(it.hasNext())
0066         {
0067             LicenseInfo newLicense;
0068             newLicense.path = it.next();
0069             newLicense.name = it.fileName();
0070 
0071             qCDebug(PLUGIN_FILETEMPLATES) << "Found License: " << newLicense.name;
0072 
0073             availableLicenses.push_back(newLicense);
0074         }
0075     }
0076 
0077     std::sort(availableLicenses.begin(), availableLicenses.end());
0078 
0079     for (const LicenseInfo& info : qAsConst(availableLicenses)) {
0080         license->licenseComboBox->addItem(info.name);
0081     }
0082     //Finally add the option other for user specified licenses
0083     LicenseInfo otherLicense;
0084     availableLicenses.push_back(otherLicense);
0085     license->licenseComboBox->addItem(i18nc("@item:inlistbox other license", "Other"));
0086 }
0087 
0088 // Read a license index, if it is not loaded, open it from the file
0089 QString LicensePagePrivate::readLicense(int licenseIndex)
0090 {
0091     //If the license is not loaded into memory, read it in
0092     if(availableLicenses[licenseIndex].contents.isEmpty())
0093     {
0094         QString licenseText;
0095         //If we are dealing with the last option "other" just return a new empty string
0096         if(licenseIndex != (availableLicenses.size() - 1))
0097         {
0098             qCDebug(PLUGIN_FILETEMPLATES) << "Reading license: " << availableLicenses[licenseIndex].name ;
0099             QFile newLicense(availableLicenses[licenseIndex].path);
0100 
0101             if(newLicense.open(QIODevice::ReadOnly | QIODevice::Text))
0102             {
0103                 QTextStream newLicenseText(&newLicense);
0104                 newLicenseText.setAutoDetectUnicode(true);
0105                 licenseText = newLicenseText.readAll();
0106                 newLicense.close();
0107 
0108                 // license text files are stored with a trailing linebreak,
0109                 // as otherwise some tools complain about it
0110                 // so deal with that and remove any potential trailing linebreak
0111                 // which otherwise would result in a trailing empty line
0112                 QRegularExpression anyLinebreakAtEnd(QStringLiteral("(\n|\r\n)$"));
0113                 licenseText.remove(anyLinebreakAtEnd);
0114             }
0115             else
0116                 licenseText = QStringLiteral("Error, could not open license file.\n Was it deleted?");
0117         }
0118 
0119         availableLicenses[licenseIndex].contents = licenseText;
0120     }
0121 
0122     return availableLicenses[licenseIndex].contents;
0123 }
0124 
0125 // ---Slots---
0126 
0127 void LicensePagePrivate::licenseComboChanged(int selectedLicense)
0128 {
0129     //If the last slot is selected enable the save license combobox
0130     if(selectedLicense == (availableLicenses.size() - 1))
0131     {
0132         license->licenseTextEdit->clear();
0133         license->licenseTextEdit->setReadOnly(false);
0134         license->saveLicense->setEnabled(true);
0135     }
0136     else
0137     {
0138         license->saveLicense->setEnabled(false);
0139         license->licenseTextEdit->setReadOnly(true);
0140     }
0141 
0142     if(selectedLicense < 0 || selectedLicense >= availableLicenses.size())
0143         license->licenseTextEdit->setText(i18n("Could not load previous license"));
0144     else
0145         license->licenseTextEdit->setText(readLicense(selectedLicense));
0146 }
0147 
0148 bool LicensePagePrivate::saveLicense()
0149 {
0150     qCDebug(PLUGIN_FILETEMPLATES) << "Attempting to save custom license: " << license->licenseName->text();
0151 
0152     QString localDataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+QLatin1String("/kdevcodegen/licenses/");
0153     QString fullPath = localDataDir + license->licenseName->text();
0154     QFile newFile(fullPath);
0155 
0156     if(newFile.exists())
0157     {
0158         KMessageBox::error(page, i18n("The specified license already exists. "
0159                                       "Please provide a different name."));
0160         return false;
0161     }
0162 
0163     QDir().mkpath(localDataDir);
0164     newFile.open(QIODevice::WriteOnly);
0165     qint64 result = newFile.write(license->licenseTextEdit->toPlainText().toUtf8());
0166     newFile.close();
0167 
0168     if(result == -1)
0169     {
0170         KMessageBox::error(page, i18n("Failed to write custom license template to file %1.", fullPath));
0171         return false;
0172     }
0173 
0174     // also add to our data structures, this esp. needed for proper saving
0175     // of the license index so it can be restored the next time we show up
0176     LicenseInfo info;
0177     info.name = license->licenseName->text();
0178     info.path = localDataDir;
0179     availableLicenses << info;
0180     // find index of the new the license, omitting the very last item ('Other')
0181     int idx = availableLicenses.count() - 1;
0182     for(int i = 0; i < availableLicenses.size() - 1; ++i) {
0183         if (info < availableLicenses.at(i)) {
0184             idx = i;
0185             break;
0186         }
0187     }
0188     availableLicenses.insert(idx, info);
0189     license->licenseComboBox->insertItem(idx, info.name);
0190     license->licenseComboBox->setCurrentIndex(idx);
0191 
0192 
0193     return true;
0194 }
0195 
0196 LicensePage::LicensePage(QWidget* parent)
0197 : QWidget(parent)
0198 , d(new LicensePagePrivate(this))
0199 {
0200     d->license = new Ui::LicenseChooserDialog;
0201     d->license->setupUi(this);
0202 
0203     connect(d->license->licenseComboBox, QOverload<int>::of(&KComboBox::currentIndexChanged),
0204             this, [&] (int selectedLicense) { d->licenseComboChanged(selectedLicense); });
0205     connect(d->license->saveLicense, &QCheckBox::clicked,
0206             d->license->licenseName, &QLineEdit::setEnabled);
0207 
0208     // Read all the available licenses from the standard dirs
0209     d->initializeLicenses();
0210 
0211     //Set the license selection to the previous one
0212     KConfigGroup config(KSharedConfig::openConfig()->group("CodeGeneration"));
0213     d->license->licenseComboBox->setCurrentIndex(config.readEntry( "LastSelectedLicense", 0 ));
0214     // Needed to avoid a bug where licenseComboChanged doesn't get
0215     // called by QComboBox if the past selection was 0
0216     d->licenseComboChanged(d->license->licenseComboBox->currentIndex());
0217 }
0218 
0219 LicensePage::~LicensePage()
0220 {
0221     if (d->license->saveLicense->isChecked() && !d->license->licenseName->text().isEmpty()) {
0222         d->saveLicense();
0223     }
0224     KConfigGroup config(KSharedConfig::openConfig()->group("CodeGeneration"));
0225     //Do not save invalid license numbers'
0226     int index = d->license->licenseComboBox->currentIndex();
0227     if( index >= 0 || index < d->availableLicenses.size() )
0228     {
0229         config.writeEntry("LastSelectedLicense", index);
0230         config.config()->sync();
0231     }
0232     else
0233     {
0234         qCWarning(PLUGIN_FILETEMPLATES) << "Attempted to save an invalid license number: " << index
0235                                         << ". Number of licenses:" << d->availableLicenses.size();
0236     }
0237 
0238     delete d->license;
0239     delete d;
0240 }
0241 
0242 QString LicensePage::license() const
0243 {
0244     QString licenseText = d->license->licenseTextEdit->document()->toPlainText();
0245     /* Add date, name and email to license text */
0246     licenseText.replace(QLatin1String("<year>"), QDate::currentDate().toString(QStringLiteral("yyyy")));
0247     licenseText.replace(QLatin1String("<month>"), QDate::currentDate().toString(QStringLiteral("MM")));
0248     licenseText.replace(QLatin1String("<day>"), QDate::currentDate().toString(QStringLiteral("dd")));
0249     QString developer(QStringLiteral("%1 <%2>"));
0250     KEMailSettings emailSettings;
0251     QString name = emailSettings.getSetting(KEMailSettings::RealName);
0252     if (name.isEmpty())
0253     {
0254         name = QStringLiteral("<copyright holder>");
0255     }
0256     developer = developer.arg(name);
0257     QString email = emailSettings.getSetting(KEMailSettings::EmailAddress);
0258     if (email.isEmpty())
0259     {
0260         email = QStringLiteral("email"); //no < > as they are already through the email field
0261     }
0262     developer = developer.arg(email);
0263     licenseText.replace(QLatin1String("<copyright holder>"), developer);
0264 
0265     return licenseText;
0266 }
0267 
0268 void LicensePage::setFocusToFirstEditWidget()
0269 {
0270     d->license->licenseComboBox->setFocus();
0271 }
0272 
0273 }
0274 
0275 Q_DECLARE_TYPEINFO(KDevelop::LicensePagePrivate::LicenseInfo, Q_MOVABLE_TYPE);
0276 
0277 #include "moc_licensepage.cpp"