diff options
| author | 2018-07-27 23:51:12 -0400 | |
|---|---|---|
| committer | 2018-07-27 23:51:12 -0400 | |
| commit | feda8a7db8d1d7c5439aa8f8feef7cc0dd2b59a0 (patch) | |
| tree | 1e50f5f666f419143f510d5ded00fe2006b7bd85 /SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp | |
| parent | d9aa870e5d509cc7309ab82dd102a937ab58613a (diff) | |
New upstream version 3.2.1+dfsg1upstream/3.2.1+dfsg1
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp')
| -rw-r--r-- | SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp new file mode 100644 index 0000000..ca45eff --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp @@ -0,0 +1,439 @@ +#include "sqliteextensioneditor.h" +#include "sqliteextensioneditormodel.h" +#include "ui_sqliteextensioneditor.h" +#include "selectabledbmodel.h" +#include "dbtree/dbtree.h" +#include "dbtree/dbtreemodel.h" +#include "common/unused.h" +#include "uiutils.h" +#include "uiconfig.h" +#include "db/db.h" +#include "services/dbmanager.h" +#include "services/notifymanager.h" +#include "common/lazytrigger.h" +#include <QDesktopServices> +#include <QFileDialog> +#include <QSortFilterProxyModel> + +SqliteExtensionEditor::SqliteExtensionEditor(QWidget *parent) : + MdiChild(parent), + ui(new Ui::SqliteExtensionEditor) +{ + init(); +} + +SqliteExtensionEditor::~SqliteExtensionEditor() +{ + delete ui; + probingDb->closeQuiet(); +} + +bool SqliteExtensionEditor::restoreSessionNextTime() +{ + return false; +} + +bool SqliteExtensionEditor::isUncommitted() const +{ + return model->isModified(); +} + +QString SqliteExtensionEditor::getQuitUncommittedConfirmMessage() const +{ + return tr("Extension manager window has uncommitted modifications."); +} + +QVariant SqliteExtensionEditor::saveSession() +{ + return QVariant(); +} + +bool SqliteExtensionEditor::restoreSession(const QVariant& sessionValue) +{ + UNUSED(sessionValue); + return true; +} + +Icon*SqliteExtensionEditor::getIconNameForMdiWindow() +{ + return ICONS.EXTENSION; +} + +QString SqliteExtensionEditor::getTitleForMdiWindow() +{ + return tr("Extension manager"); +} + +void SqliteExtensionEditor::createActions() +{ + createAction(COMMIT, ICONS.COMMIT, tr("Commit all extension changes"), this, SLOT(commit()), ui->toolbar); + createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all extension changes"), this, SLOT(rollback()), ui->toolbar); + ui->toolbar->addSeparator(); + createAction(ADD, ICONS.EXTENSION_ADD, tr("Add new extension"), this, SLOT(newExtension()), ui->toolbar); + createAction(DELETE, ICONS.EXTENSION_DELETE, tr("Remove selected extension"), this, SLOT(deleteExtension()), ui->toolbar); + ui->toolbar->addSeparator(); + createAction(HELP, ICONS.HELP, tr("Editing extensions manual"), this, SLOT(help()), ui->toolbar); +} + +void SqliteExtensionEditor::setupDefShortcuts() +{ +} + +QToolBar* SqliteExtensionEditor::getToolBar(int toolbar) const +{ + UNUSED(toolbar); + return ui->toolbar; +} + +void SqliteExtensionEditor::init() +{ + ui->setupUi(this); + initActions(); + + statusUpdateTrigger = new LazyTrigger(500, this, SLOT(updateCurrentExtensionState())); + + model = new SqliteExtensionEditorModel(this); + extensionFilterModel = new QSortFilterProxyModel(this); + extensionFilterModel->setSourceModel(model); + ui->extensionList->setModel(extensionFilterModel); + + dbListModel = new SelectableDbModel(this); + dbListModel->setDisabledVersion(2); + dbListModel->setSourceModel(DBTREE->getModel()); + ui->databaseList->setModel(dbListModel); + ui->databaseList->expandAll(); + + model->setData(SQLITE_EXTENSIONS->getAllExtensions()); + + connect(ui->extensionList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(extensionSelected(QItemSelection,QItemSelection))); + connect(ui->extensionList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateState())); + connect(ui->fileEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified())); + connect(ui->initEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified())); + connect(ui->allDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified())); + connect(ui->selectedDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified())); + connect(ui->fileBrowse, SIGNAL(clicked()), this, SLOT(browseForFile())); + connect(ui->fileEdit, SIGNAL(textChanged(QString)), statusUpdateTrigger, SLOT(schedule())); + connect(ui->fileEdit, SIGNAL(textChanged(QString)), this, SLOT(generateName())); + connect(ui->initEdit, SIGNAL(textChanged(QString)), statusUpdateTrigger, SLOT(schedule())); + connect(dbListModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateModified())); + + probingDb = DBLIST->createInMemDb(true); + if (!probingDb->openQuiet()) + qWarning() << "Could not open in-memory dtabase for Extension manager window. Probing files will be impossible."; + + initStateForAll(); + updateState(); + updateCurrentExtensionState(); +} + +int SqliteExtensionEditor::getCurrentExtensionRow() const +{ + QModelIndexList idxList = ui->extensionList->selectionModel()->selectedIndexes(); + if (idxList.size() == 0) + return -1; + + return idxList.first().row(); +} + +void SqliteExtensionEditor::extensionDeselected(int row) +{ + statusUpdateTrigger->cancel(); + + model->setFilePath(row, ui->fileEdit->text()); + model->setInitFunction(row, ui->initEdit->text()); + model->setAllDatabases(row, ui->allDatabasesRadio->isChecked()); + model->setModified(row, currentModified); + + if (ui->selectedDatabasesRadio->isChecked()) + model->setDatabases(row, getCurrentDatabases()); + + model->setValid(row, validateExtension(row)); +} + +void SqliteExtensionEditor::extensionSelected(int row) +{ + updatesForSelection = true; + ui->fileEdit->setText(model->getFilePath(row)); + ui->initEdit->setText(model->getInitFunction(row)); + + // Databases + dbListModel->setDatabases(model->getDatabases(row)); + ui->databaseList->expandAll(); + + if (model->getAllDatabases(row)) + ui->allDatabasesRadio->setChecked(true); + else + ui->selectedDatabasesRadio->setChecked(true); + + updatesForSelection = false; + currentModified = model->isModified(row); + + updateCurrentExtensionState(); +} + +void SqliteExtensionEditor::clearEdits() +{ + ui->fileEdit->setText(QString::null); + ui->initEdit->setText(QString::null); + ui->allDatabasesRadio->setChecked(true); +} + +void SqliteExtensionEditor::selectExtension(int row) +{ + if (!model->isValidRowIndex(row)) + return; + + ui->extensionList->selectionModel()->setCurrentIndex(model->index(row), QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); +} + +QStringList SqliteExtensionEditor::getCurrentDatabases() const +{ + return dbListModel->getDatabases(); +} + +bool SqliteExtensionEditor::tryToLoad(const QString& filePath, const QString& initFunc, QString* resultError) +{ + if (!probingDb->isOpen()) + { + qWarning() << "Probing database is closed. Cannot evaluate if file" << filePath << "is loadable."; + return true; + } + + bool loadedOk = probingDb->loadExtension(filePath, initFunc.isEmpty() ? QString() : initFunc); + if (!loadedOk && resultError) + *resultError = probingDb->getErrorText(); + + probingDb->closeQuiet(); + probingDb->openQuiet(); + + return loadedOk; +} + +bool SqliteExtensionEditor::validateExtension(bool* fileOk, bool* initOk, QString* fileError) +{ + QString filePath = ui->fileEdit->text(); + QString initFunc = ui->initEdit->text(); + return validateExtension(filePath, initFunc, fileOk, initOk, fileError); +} + +bool SqliteExtensionEditor::validateExtension(int row) +{ + QString filePath = model->getFilePath(row); + QString initFunc = model->getInitFunction(row); + return validateExtension(filePath, initFunc); +} + +bool SqliteExtensionEditor::validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk, bool* initOk, QString* fileError) +{ + bool localFileOk = true; + bool localInitOk = true; + + QFileInfo fileInfo(filePath); + if (!fileInfo.exists() || !fileInfo.isReadable()) + { + localFileOk = false; + *fileError = tr("File with given path does not exist or is not readable."); + } + else + localFileOk = tryToLoad(filePath, initFunc, fileError); + + if (!localFileOk && fileError && fileError->isEmpty()) + *fileError = tr("Unable to load extension: %1").arg(filePath); + + static const QRegularExpression initFuncRegExp("^[a-zA-Z0-9_]*$"); + localInitOk = initFuncRegExp.match(initFunc).hasMatch(); + + if (fileOk) + *fileOk = localFileOk; + + if (initOk) + *initOk = localInitOk; + + return localFileOk && localInitOk; +} + +void SqliteExtensionEditor::initStateForAll() +{ + for (int i = 0, total = model->rowCount(); i < total; ++i) + { + model->setName(i, QFileInfo(model->getFilePath(i)).baseName()); + model->setValid(i, validateExtension(i)); + } +} + +void SqliteExtensionEditor::help() +{ + static const QString url = QStringLiteral("https://github.com/pawelsalawa/sqlitestudio/wiki/User_Manual#sqlite-extensions"); + QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode)); +} + +void SqliteExtensionEditor::commit() +{ + int row = getCurrentExtensionRow(); + if (model->isValidRowIndex(row)) + extensionDeselected(row); + + QList<SqliteExtensionManager::ExtensionPtr> extensions = model->getExtensions(); + + SQLITE_EXTENSIONS->setExtensions(extensions); + model->clearModified(); + currentModified = false; + + if (model->isValidRowIndex(row)) + selectExtension(row); + + updateState(); +} + +void SqliteExtensionEditor::rollback() +{ + int selectedBefore = getCurrentExtensionRow(); + + model->setData(SQLITE_EXTENSIONS->getAllExtensions()); + currentModified = false; + clearEdits(); + + if (model->isValidRowIndex(selectedBefore)) + selectExtension(selectedBefore); + + updateState(); +} + +void SqliteExtensionEditor::newExtension() +{ + model->addExtension(SqliteExtensionManager::ExtensionPtr::create()); + selectExtension(model->rowCount() - 1); +} + +void SqliteExtensionEditor::deleteExtension() +{ + nameGenerationActive = false; + int row = getCurrentExtensionRow(); + model->deleteExtension(row); + clearEdits(); + + row = getCurrentExtensionRow(); + if (model->isValidRowIndex(row)) + extensionSelected(row); + else + updateCurrentExtensionState(); + + nameGenerationActive = true; + updateState(); +} + +void SqliteExtensionEditor::updateState() +{ + bool modified = model->isModified() || currentModified; + bool valid = model->isValid(); + + actionMap[COMMIT]->setEnabled(modified && valid); + actionMap[ROLLBACK]->setEnabled(modified); + actionMap[DELETE]->setEnabled(ui->extensionList->selectionModel()->selectedIndexes().size() > 0); + ui->databaseList->setEnabled(ui->selectedDatabasesRadio->isChecked()); +} + +void SqliteExtensionEditor::updateCurrentExtensionState() +{ + int row = getCurrentExtensionRow(); + bool validRow = model->isValidRowIndex(row); + ui->rightWidget->setEnabled(validRow); + if (!validRow) + { + setValidState(ui->fileEdit, true); + setValidState(ui->initEdit, true); + return; + } + + bool fileOk = true; + bool initOk = true; + QString fileError; + bool allOk = validateExtension(&fileOk, &initOk, &fileError); + + // Display results + setValidState(ui->fileEdit, fileOk, fileError); + setValidState(ui->initEdit, initOk, tr("Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore.")); + ui->databasesGroup->setEnabled(allOk); + model->setValid(row, allOk); + + updateState(); +} + +void SqliteExtensionEditor::extensionSelected(const QItemSelection& selected, const QItemSelection& deselected) +{ + int deselCnt = deselected.indexes().size(); + int selCnt = selected.indexes().size(); + + if (deselCnt > 0) + extensionDeselected(deselected.indexes().first().row()); + + if (selCnt > 0) + extensionSelected(selected.indexes().first().row()); + + if (deselCnt > 0 && selCnt == 0) + { + currentModified = false; + clearEdits(); + } +} + +void SqliteExtensionEditor::updateModified() +{ + if (updatesForSelection) + return; + + int row = getCurrentExtensionRow(); + if (model->isValidRowIndex(row)) + { + bool fileDiff = model->getFilePath(row) != ui->fileEdit->text(); + bool initDiff = model->getInitFunction(row) != ui->initEdit->text(); + bool allDatabasesDiff = model->getAllDatabases(row) != ui->allDatabasesRadio->isChecked(); + bool dbDiff = getCurrentDatabases().toSet() != model->getDatabases(row).toSet(); // QSet to ignore order + + currentModified = (fileDiff || initDiff || allDatabasesDiff || dbDiff); + } + + statusUpdateTrigger->schedule(); +} + +void SqliteExtensionEditor::generateName() +{ + if (!nameGenerationActive) + return; + + int row = getCurrentExtensionRow(); + if (model->isValidRowIndex(row)) + model->setName(row, QFileInfo(ui->fileEdit->text()).baseName()); +} + +void SqliteExtensionEditor::applyFilter(const QString& value) +{ + int row = getCurrentExtensionRow(); + ui->extensionList->selectionModel()->clearSelection(); + + extensionFilterModel->setFilterFixedString(value); + + selectExtension(row); +} + +void SqliteExtensionEditor::browseForFile() +{ + QString dir = getFileDialogInitPath(); + QString filter = +#if defined(Q_OS_WIN) + tr("Dynamic link libraries (*.dll);;All files (*)"); +#elif defined(Q_OS_LINUX) + tr("Shared objects (*.so);;All files (*)"); +#elif defined(Q_OS_OSX) + tr("Dynamic libraries (*.dylib);;All files (*)"); +#else + tr("All files (*)"); +#endif + QString filePath = QFileDialog::getOpenFileName(this, tr("Open file"), dir, filter); + if (filePath.isNull()) + return; + + setFileDialogInitPathByFile(filePath); + + ui->fileEdit->setText(filePath); +} |
