#include "customscriptseditor.h" #include "ui_customscriptseditor.h" #include "ui_customscriptslistitem.h" #include "config.h" #include "editor.h" #include "shortcut.h" #include #include // TODO: System for turning scripts on or off // // TODO: Better URL colors on dark themes // TODO: Save window state CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) : QMainWindow(parent), ui(new Ui::CustomScriptsEditor), baseDir(userConfig.getProjectDir() + QDir::separator()) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); // This property seems to be reset if we don't set it programmatically ui->list->setDragDropMode(QAbstractItemView::NoDragDrop); for (auto path : userConfig.getCustomScripts()) this->displayScript(path); this->importDir = userConfig.getProjectDir(); connect(ui->button_AddNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::addNewScript); connect(ui->button_ReloadScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::reloadScripts); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CustomScriptsEditor::dialogButtonClicked); this->initShortcuts(); } CustomScriptsEditor::~CustomScriptsEditor() { ui->list->clear(); delete ui; } void CustomScriptsEditor::initShortcuts() { auto *shortcut_remove = new Shortcut({QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(removeSelectedScripts())); shortcut_remove->setObjectName("shortcut_remove"); shortcut_remove->setWhatsThis("Remove Selected Scripts"); // TODO: Prevent ambiguity with MainWindow auto *shortcut_open = new Shortcut(QKeySequence("Ctrl+O"), this, SLOT(openSelectedScripts())); shortcut_open->setObjectName("shortcut_open"); shortcut_open->setWhatsThis("Open Selected Scripts"); auto *shortcut_addNew = new Shortcut(QKeySequence(), this, SLOT(addNewScript())); shortcut_addNew->setObjectName("shortcut_addNew"); shortcut_addNew->setWhatsThis("Add New Script..."); auto *shortcut_reload = new Shortcut(QKeySequence(), this, SLOT(reloadScripts())); shortcut_reload->setObjectName("shortcut_reload"); shortcut_reload->setWhatsThis("Reload Scripts"); //connect(new QShortcut(QKeySequence("Backspace"), this), &QShortcut::activated, this, &CustomScriptsEditor::removeSelectedScripts); //connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &CustomScriptsEditor::openSelectedScripts); shortcutsConfig.load(); shortcutsConfig.setDefaultShortcuts(shortcutableObjects()); applyUserShortcuts(); } QObjectList CustomScriptsEditor::shortcutableObjects() const { QObjectList shortcutable_objects; for (auto *action : findChildren()) if (!action->objectName().isEmpty()) shortcutable_objects.append(qobject_cast(action)); for (auto *shortcut : findChildren()) if (!shortcut->objectName().isEmpty()) shortcutable_objects.append(qobject_cast(shortcut)); return shortcutable_objects; } // TODO: Connect to shorcuts editor void CustomScriptsEditor::applyUserShortcuts() { for (auto *action : findChildren()) if (!action->objectName().isEmpty()) action->setShortcuts(shortcutsConfig.userShortcuts(action)); for (auto *shortcut : findChildren()) if (!shortcut->objectName().isEmpty()) shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut)); } void CustomScriptsEditor::displayScript(const QString &filepath) { auto item = new QListWidgetItem(); auto buttons = new CustomScriptsListItem(); buttons->ui->lineEdit_filepath->setText(filepath); item->setSizeHint(buttons->sizeHint()); connect(buttons->ui->b_Choose, &QAbstractButton::clicked, [this, item](bool) { this->replaceScript(item); }); connect(buttons->ui->b_Edit, &QAbstractButton::clicked, [this, item](bool) { this->openScript(item); }); connect(buttons->ui->b_Delete, &QAbstractButton::clicked, [this, item](bool) { this->removeScript(item); }); connect(buttons->ui->lineEdit_filepath, &QLineEdit::textEdited, [this](const QString&) { this->hasUnsavedChanges = true; }); // Per the Qt manual, for performance reasons QListWidget::setItemWidget shouldn't be used with non-static items. // There's an assumption here that users won't have enough scripts for that to be a problem. ui->list->addItem(item); ui->list->setItemWidget(item, buttons); } QString CustomScriptsEditor::getListItemFilepath(QListWidgetItem * item) const { auto widget = dynamic_cast(ui->list->itemWidget(item)); if (!widget) return QString(); QString path = widget->ui->lineEdit_filepath->text(); QFileInfo fileInfo(path); if (fileInfo.isRelative()) path.prepend(this->baseDir); return path; } void CustomScriptsEditor::setListItemFilepath(QListWidgetItem * item, QString filepath) const { auto widget = dynamic_cast(ui->list->itemWidget(item)); if (!widget) return; if (filepath.startsWith(this->baseDir)) filepath.remove(0, this->baseDir.length()); widget->ui->lineEdit_filepath->setText(filepath); } QString CustomScriptsEditor::chooseScript(QString dir) { return QFileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)"); } void CustomScriptsEditor::addNewScript() { QString filepath = this->chooseScript(this->importDir); if (filepath.isEmpty()) return; this->importDir = filepath; if (filepath.startsWith(this->baseDir)) filepath.remove(0, this->baseDir.length()); this->displayScript(filepath); this->hasUnsavedChanges = true; } void CustomScriptsEditor::removeScript(QListWidgetItem * item) { ui->list->takeItem(ui->list->row(item)); this->hasUnsavedChanges = true; } void CustomScriptsEditor::removeSelectedScripts() { QList items = ui->list->selectedItems(); if (items.length() == 0) return; for (auto item : items) this->removeScript(item); } void CustomScriptsEditor::replaceScript(QListWidgetItem * item) { const QString filepath = this->chooseScript(this->getListItemFilepath(item)); if (filepath.isEmpty()) return; this->setListItemFilepath(item, filepath); this->hasUnsavedChanges = true; } void CustomScriptsEditor::openScript(QListWidgetItem * item) { const QString path = this->getListItemFilepath(item); QFileInfo fileInfo(path); if (!fileInfo.exists() || !fileInfo.isFile()){ QMessageBox::warning(this, "", QString("Failed to open script '%1'").arg(path)); return; } Editor::openInTextEditor(path); } void CustomScriptsEditor::openSelectedScripts() { for (auto item : ui->list->selectedItems()) this->openScript(item); } void CustomScriptsEditor::reloadScripts() { if (this->hasUnsavedChanges) { if (this->prompt("Scripts have been modified, save changes and reload the script engine?", QMessageBox::Yes) == QMessageBox::No) return; this->save(); } emit reloadScriptEngine(); // TODO: Catch this signal } void CustomScriptsEditor::save() { if (!this->hasUnsavedChanges) return; // TODO: Set new paths in config this->hasUnsavedChanges = false; this->reloadScripts(); } int CustomScriptsEditor::prompt(const QString &text, QMessageBox::StandardButton defaultButton) { QMessageBox messageBox(this); messageBox.setText(text); messageBox.setIcon(QMessageBox::Question); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | defaultButton); messageBox.setDefaultButton(defaultButton); return messageBox.exec(); } void CustomScriptsEditor::dialogButtonClicked(QAbstractButton *button) { if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) this->save(); close(); // All buttons (OK and Cancel) close the window } void CustomScriptsEditor::closeEvent(QCloseEvent* event) { if (this->hasUnsavedChanges) { int result = this->prompt("Scripts have been modified, save changes?", QMessageBox::Cancel); if (result == QMessageBox::Cancel) { event->ignore(); return; } if (result == QMessageBox::Yes) this->save(); } /* // TODO porymapConfig.setProjectSettingsEditorGeometry( this->saveGeometry(), this->saveState() ); */ }