File indexing completed on 2024-04-21 16:32:36

0001 /***************************************************************************
0002                    scriptplugin.cpp  -  description
0003                              -------------------
0004     begin                : Fri Nov 9 2007
0005     copyright            : (C) 2007 by Dominik Seichter
0006     email                : domseichter@web.de
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "scriptplugin.h"
0019 
0020 #include <kconfiggroup.h>
0021 #include <kiconloader.h>
0022 #include <kmessagebox.h>
0023 #include <KIO/StoredTransferJob>
0024 #include <KIO/StatJob>
0025 #include <KJobWidgets>
0026 #include <QTemporaryFile>
0027 
0028 #include <QFile>
0029 #include <QMenu>
0030 #include <QPointer>
0031 #include <QTextStream>
0032 #include <QVariant>
0033 #include <QFileDialog>
0034 
0035 #include <kjs/kjsinterpreter.h>
0036 
0037 #include "ui_scriptplugindialog.h"
0038 #include "ui_scriptpluginwidget.h"
0039 #include "batchrenamer.h"
0040 #include "krenamefile.h"
0041 
0042 const char *ScriptPlugin::s_pszFileDialogLocation = "kfiledialog://krenamejscript";
0043 const char *ScriptPlugin::s_pszVarNameIndex       = "krename_index";
0044 const char *ScriptPlugin::s_pszVarNameUrl         = "krename_url";
0045 const char *ScriptPlugin::s_pszVarNameFilename    = "krename_filename";
0046 const char *ScriptPlugin::s_pszVarNameExtension   = "krename_extension";
0047 const char *ScriptPlugin::s_pszVarNameDirectory   = "krename_directory";
0048 
0049 enum EVarType {
0050     eVarType_String = 0,
0051     eVarType_Int,
0052     eVarType_Double,
0053     eVarType_Bool
0054 };
0055 
0056 ScriptPlugin::ScriptPlugin(PluginLoader *loader)
0057     : QObject(),
0058       Plugin(loader), m_parent(nullptr)
0059 {
0060     m_name = i18n("JavaScript Plugin");
0061     m_icon = "applications-development";
0062     m_interpreter = new KJSInterpreter();
0063     m_menu   = new QMenu();
0064     m_widget = new Ui::ScriptPluginWidget();
0065 
0066     this->addSupportedToken("js;.*");
0067 
0068     m_help.append("[js;4+5];;" + i18n("Insert a snippet of JavaScript code (4+5 in this case)"));
0069 
0070     m_menu->addAction(i18n("Index of the current file"),     this, SLOT(slotInsertIndex()));
0071     m_menu->addAction(i18n("URL of the current file"),       this, SLOT(slotInsertUrl()));
0072     m_menu->addAction(i18n("Filename of the current file"),  this, SLOT(slotInsertFilename()));
0073     m_menu->addAction(i18n("Extension of the current file"), this, SLOT(slotInsertExtension()));
0074     m_menu->addAction(i18n("Directory of the current file"), this, SLOT(slotInsertDirectory()));
0075 }
0076 
0077 ScriptPlugin::~ScriptPlugin()
0078 {
0079     delete m_widget;
0080     delete m_menu;
0081     delete m_interpreter;
0082 }
0083 
0084 QString ScriptPlugin::processFile(BatchRenamer *b, int index,
0085                                   const QString &filenameOrToken, EPluginType)
0086 {
0087     QString token(filenameOrToken);
0088     QString script;
0089     QString definitions = m_widget->textCode->toPlainText();
0090 
0091     if (token.contains(";")) {
0092         script = token.section(';', 1);   // all sections from 1 to the last
0093         token  = token.section(';', 0, 0).toLower();
0094     } else {
0095         token = token.toLower();
0096     }
0097 
0098     if (token == "js") {
0099         // Setup interpreter
0100         const KRenameFile &file = b->files()->at(index);
0101         initKRenameVars(file, index);
0102 
0103         // Make sure definitions are executed first
0104         script = definitions + '\n' + script;
0105 
0106         KJSResult result = m_interpreter->evaluate(script, nullptr);
0107         if (result.isException()) {
0108             qDebug("JavaScript Error: %s", result.errorMessage().toUtf8().data());
0109             return QString();
0110         }
0111 
0112         return result.value().toString(m_interpreter->globalContext());
0113     }
0114 
0115     return QString();
0116 }
0117 
0118 const QPixmap ScriptPlugin::icon() const
0119 {
0120     return KIconLoader::global()->loadIcon(m_icon, KIconLoader::NoGroup, KIconLoader::SizeSmall);
0121 }
0122 
0123 void ScriptPlugin::createUI(QWidget *parent) const
0124 {
0125     QStringList labels;
0126     labels << i18n("Variable Name");
0127     labels << i18n("Initial Value");
0128 
0129     const_cast<ScriptPlugin *>(this)->m_parent = parent;
0130     m_widget->setupUi(parent);
0131     m_widget->listVariables->setColumnCount(2);
0132     m_widget->listVariables->setHeaderLabels(labels);
0133 
0134     connect(m_widget->listVariables, &QTreeWidget::itemSelectionChanged,
0135             this, &ScriptPlugin::slotEnableControls);
0136     connect(m_widget->buttonAdd, &QPushButton::clicked,
0137             this, &ScriptPlugin::slotAdd);
0138     connect(m_widget->buttonRemove, &QPushButton::clicked,
0139             this, &ScriptPlugin::slotRemove);
0140     connect(m_widget->buttonLoad, &QPushButton::clicked,
0141             this, &ScriptPlugin::slotLoad);
0142     connect(m_widget->buttonSave, &QPushButton::clicked,
0143             this, &ScriptPlugin::slotSave);
0144     connect(m_widget->textCode, &QTextEdit::textChanged,
0145             this, &ScriptPlugin::slotEnableControls);
0146 
0147     const_cast<ScriptPlugin *>(this)->slotEnableControls();
0148 
0149     QPixmap openIcon   = KIconLoader::global()->loadIcon("document-open", KIconLoader::NoGroup, KIconLoader::SizeSmall);
0150     QPixmap saveIcon   = KIconLoader::global()->loadIcon("document-save-as", KIconLoader::NoGroup, KIconLoader::SizeSmall);
0151     QPixmap removeIcon = KIconLoader::global()->loadIcon("list-remove", KIconLoader::NoGroup, KIconLoader::SizeSmall);
0152     QPixmap addIcon    = KIconLoader::global()->loadIcon("list-add", KIconLoader::NoGroup, KIconLoader::SizeSmall);
0153 
0154     m_widget->buttonLoad->setIcon(openIcon);
0155     m_widget->buttonSave->setIcon(saveIcon);
0156     m_widget->buttonAdd->setIcon(addIcon);
0157     m_widget->buttonRemove->setIcon(removeIcon);
0158 
0159     m_widget->buttonInsert->setMenu(m_menu);
0160 }
0161 
0162 void ScriptPlugin::initKRenameVars(const KRenameFile &file, int index)
0163 {
0164     // KRename definitions
0165     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0166             ScriptPlugin::s_pszVarNameIndex,
0167             index);
0168     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0169             ScriptPlugin::s_pszVarNameUrl,
0170             file.srcUrl().url());
0171     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0172             ScriptPlugin::s_pszVarNameFilename,
0173             file.srcFilename());
0174     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0175             ScriptPlugin::s_pszVarNameExtension,
0176             file.srcExtension());
0177     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0178             ScriptPlugin::s_pszVarNameDirectory,
0179             file.srcDirectory());
0180 
0181     // User definitions, set them only on first file
0182     if (index == 0) {
0183         for (int i = 0; i < m_widget->listVariables->topLevelItemCount(); i++) {
0184             // TODO, we have to know the type of the variable!
0185             QTreeWidgetItem *item = m_widget->listVariables->topLevelItem(i);
0186             if (item) {
0187                 EVarType eVarType = static_cast<EVarType>(item->data(1, Qt::UserRole).toInt());
0188                 const QString &name  = item->text(0);
0189                 const QString &value = item->text(1);
0190                 switch (eVarType) {
0191                 default:
0192                 case eVarType_String:
0193                     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0194                             name, value.toUtf8().data());
0195                     break;
0196                 case eVarType_Int:
0197                     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0198                             name, value.toInt());
0199                     break;
0200                 case eVarType_Double:
0201                     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0202                             name, value.toDouble());
0203                     break;
0204                 case eVarType_Bool:
0205                     m_interpreter->globalObject().setProperty(m_interpreter->globalContext(),
0206                             name,
0207                             (value.toLower() == "true" ? true : false));
0208                     break;
0209 
0210                 }
0211             }
0212         }
0213     }
0214 }
0215 
0216 void ScriptPlugin::insertVariable(const char *name)
0217 {
0218     m_widget->textCode->insertPlainText(QString(name));
0219 }
0220 
0221 void ScriptPlugin::slotEnableControls()
0222 {
0223     bool bEnable = (m_widget->listVariables->selectedItems().count() != 0);
0224     m_widget->buttonRemove->setEnabled(bEnable);
0225 
0226     bEnable = !m_widget->textCode->toPlainText().isEmpty();
0227     m_widget->buttonSave->setEnabled(bEnable);
0228 }
0229 
0230 void ScriptPlugin::slotAdd()
0231 {
0232     QPointer<QDialog> dialog = new QDialog();
0233     Ui::ScriptPluginDialog dlg;
0234 
0235     QStringList types;
0236 
0237     types << i18n("String");
0238     types << i18n("Int");
0239     types << i18n("Double");
0240     types << i18n("Boolean");
0241 
0242     dlg.setupUi(dialog);
0243     dlg.comboType->addItems(types);
0244 
0245     if (dialog->exec() == QDialog::Accepted) {
0246         QString name  = dlg.lineName->text();
0247         QString value = dlg.lineValue->text();
0248 
0249         // Build a Java script statement
0250         QString script = name + " = " + value + ';';
0251 
0252         KJSInterpreter interpreter;
0253         KJSResult result = m_interpreter->evaluate(script, nullptr);
0254         if (result.isException()) {
0255             KMessageBox::error(m_parent,
0256                                i18n("A JavaScript error has occurred: ") +
0257                                result.errorMessage(), this->name());
0258         } else {
0259             QTreeWidgetItem *item = new QTreeWidgetItem();
0260             item->setText(0, name);
0261             item->setText(1, value);
0262             item->setData(1, Qt::UserRole, QVariant(dlg.comboType->currentIndex()));
0263 
0264             m_widget->listVariables->addTopLevelItem(item);
0265         }
0266     }
0267     delete dialog;
0268 }
0269 
0270 void ScriptPlugin::slotRemove()
0271 {
0272     QTreeWidgetItem *item = m_widget->listVariables->currentItem();
0273     if (item) {
0274         m_widget->listVariables->invisibleRootItem()->removeChild(item);
0275         delete item;
0276     }
0277 }
0278 
0279 void ScriptPlugin::slotLoad()
0280 {
0281     if (!m_widget->textCode->toPlainText().isEmpty() &&
0282             KMessageBox::questionYesNo(m_parent,
0283                                        i18n("All currently entered definitions will be lost. Do you want to continue?"), {},
0284                                        KStandardGuiItem::cont(),
0285                                        KStandardGuiItem::cancel())
0286             == KMessageBox::No) {
0287         return;
0288     }
0289 
0290     QUrl url = QFileDialog::getOpenFileUrl(m_parent, i18n("Select file"),
0291                                            QUrl(ScriptPlugin::s_pszFileDialogLocation));
0292 
0293     if (!url.isEmpty()) {
0294         // Also support remote files
0295         KIO::StoredTransferJob *job = KIO::storedGet(url);
0296         KJobWidgets::setWindow(job, m_parent);
0297         if (job->exec()) {
0298             m_widget->textCode->setPlainText(QString::fromLocal8Bit(job->data()));
0299         } else {
0300             KMessageBox::error(m_parent, job->errorString());
0301         }
0302     }
0303 
0304     slotEnableControls();
0305 }
0306 
0307 void ScriptPlugin::slotSave()
0308 {
0309     QUrl url = QFileDialog::getSaveFileUrl(m_parent, i18n("Select file"),
0310                                            QUrl(ScriptPlugin::s_pszFileDialogLocation));
0311 
0312     if (!url.isEmpty()) {
0313         KIO::StatJob *statJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0);
0314         statJob->exec();
0315         if (statJob->error() != KIO::ERR_DOES_NOT_EXIST) {
0316             int m = KMessageBox::warningYesNo(m_parent, i18n("The file %1 already exists. "
0317                                               "Do you want to overwrite it?", url.toDisplayString(QUrl::PreferLocalFile)), {},
0318                                               KStandardGuiItem::overwrite(),
0319                                               KStandardGuiItem::cancel());
0320 
0321             if (m == KMessageBox::No) {
0322                 return;
0323             }
0324         }
0325 
0326         if (url.isLocalFile()) {
0327             QFile file(url.path());
0328             if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0329                 QTextStream out(&file);
0330                 out << m_widget->textCode->toPlainText();
0331                 out.flush();
0332                 file.close();
0333             } else {
0334                 KMessageBox::error(m_parent, i18n("Unable to open %1 for writing.", url.path()));
0335             }
0336         } else {
0337             KIO::StoredTransferJob *job = KIO::storedPut(m_widget->textCode->toPlainText().toLocal8Bit(), url, -1);
0338             KJobWidgets::setWindow(job, m_parent);
0339             job->exec();
0340             if (job->error()) {
0341                 KMessageBox::error(m_parent, job->errorString());
0342             }
0343         }
0344     }
0345 
0346     slotEnableControls();
0347 }
0348 
0349 void ScriptPlugin::slotTest()
0350 {
0351 }
0352 
0353 void ScriptPlugin::slotInsertIndex()
0354 {
0355     this->insertVariable(ScriptPlugin::s_pszVarNameIndex);
0356 }
0357 
0358 void ScriptPlugin::slotInsertUrl()
0359 {
0360     this->insertVariable(ScriptPlugin::s_pszVarNameUrl);
0361 }
0362 
0363 void ScriptPlugin::slotInsertFilename()
0364 {
0365     this->insertVariable(ScriptPlugin::s_pszVarNameFilename);
0366 }
0367 
0368 void ScriptPlugin::slotInsertExtension()
0369 {
0370     this->insertVariable(ScriptPlugin::s_pszVarNameExtension);
0371 }
0372 
0373 void ScriptPlugin::slotInsertDirectory()
0374 {
0375     this->insertVariable(ScriptPlugin::s_pszVarNameDirectory);
0376 }
0377 
0378 void ScriptPlugin::loadConfig(KConfigGroup &group)
0379 {
0380     QStringList  variableNames;
0381     QStringList  variableValues;
0382     QVariantList variableTypes;
0383 
0384     variableNames  = group.readEntry("JavaScriptVariableNames",  variableNames);
0385     variableValues = group.readEntry("JavaScriptVariableValues", variableValues);
0386     variableTypes  = group.readEntry("JavaScriptVariableTypes", variableTypes);
0387 
0388     int min = qMin(variableNames.count(), variableValues.count());
0389     min = qMin(min, variableTypes.count());
0390 
0391     for (int i = 0; i < min; i++) {
0392         QTreeWidgetItem *item = new QTreeWidgetItem();
0393         item->setText(0, variableNames[i]);
0394         item->setText(1, variableValues[i]);
0395         item->setData(1, Qt::UserRole, variableTypes[i]);
0396 
0397         m_widget->listVariables->addTopLevelItem(item);
0398     }
0399 
0400     m_widget->textCode->setPlainText(group.readEntry("JavaScriptDefinitions", QString()));
0401 }
0402 
0403 void ScriptPlugin::saveConfig(KConfigGroup &group) const
0404 {
0405     QStringList  variableNames;
0406     QStringList  variableValues;
0407     QVariantList variableTypes;
0408 
0409     for (int i = 0; i < m_widget->listVariables->topLevelItemCount(); i++) {
0410         QTreeWidgetItem *item = m_widget->listVariables->topLevelItem(i);
0411         if (item) {
0412             variableNames  << item->text(0);
0413             variableValues << item->text(1);
0414             variableTypes  << item->data(1, Qt::UserRole);
0415         }
0416     }
0417 
0418     group.writeEntry("JavaScriptVariableNames",  variableNames);
0419     group.writeEntry("JavaScriptVariableValues", variableValues);
0420     group.writeEntry("JavaScriptVariableTypes",  variableTypes);
0421     group.writeEntry("JavaScriptDefinitions", m_widget->textCode->toPlainText());
0422 }
0423