From 3565aad630864ecdbe53fdaa501ea708555b3c7c Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sun, 30 Apr 2023 18:30:36 -0400 Subject: New upstream version 3.4.4+dfsg. --- .../guiSQLiteStudio/dialogs/aboutdialog.cpp | 49 +- .../guiSQLiteStudio/dialogs/aboutdialog.h | 3 - .../guiSQLiteStudio/dialogs/aboutdialog.ui | 134 ++-- .../guiSQLiteStudio/dialogs/bindparamsdialog.cpp | 7 +- .../guiSQLiteStudio/dialogs/bindparamsdialog.h | 2 +- .../guiSQLiteStudio/dialogs/columndialog.cpp | 46 +- .../guiSQLiteStudio/dialogs/columndialog.ui | 4 +- .../guiSQLiteStudio/dialogs/configdialog.cpp | 446 ++++++++++--- .../guiSQLiteStudio/dialogs/configdialog.h | 32 +- .../guiSQLiteStudio/dialogs/configdialog.ui | 716 ++++++++++++++++++--- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp | 87 ++- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h | 6 +- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui | 17 +- .../guiSQLiteStudio/dialogs/exportdialog.cpp | 3 +- .../guiSQLiteStudio/dialogs/importdialog.cpp | 8 +- .../guiSQLiteStudio/dialogs/indexdialog.cpp | 24 +- .../guiSQLiteStudio/dialogs/indexdialog.h | 1 + .../guiSQLiteStudio/dialogs/languagedialog.cpp | 6 + .../guiSQLiteStudio/dialogs/languagedialog.h | 3 + .../guiSQLiteStudio/dialogs/newversiondialog.ui | 4 +- .../dialogs/populateconfigdialog.cpp | 4 +- .../guiSQLiteStudio/dialogs/populatedialog.cpp | 13 +- .../dialogs/triggercolumnsdialog.cpp | 5 +- .../guiSQLiteStudio/dialogs/triggerdialog.cpp | 17 +- .../guiSQLiteStudio/dialogs/triggerdialog.h | 1 + 25 files changed, 1275 insertions(+), 363 deletions(-) (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs') diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp index 7e084fb..26a9f52 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp @@ -1,16 +1,18 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" #include "common/utils.h" -#include "sqlitestudio.h" #include "iconmanager.h" #include "services/extralicensemanager.h" #include "services/pluginmanager.h" +#include "services/sqliteextensionmanager.h" #include "formmanager.h" #include "iconmanager.h" +#include "mainwindow.h" #include #include #include #include +#include AboutDialog::AboutDialog(InitialMode initialMode, QWidget *parent) : QDialog(parent), @@ -38,8 +40,8 @@ void AboutDialog::init(InitialMode initialMode) case DistributionType::PORTABLE: distName = tr("Portable distribution."); break; - case DistributionType::OSX_BOUNDLE: - distName = tr("MacOS X application boundle distribution."); + case DistributionType::OSX_BUNDLE: + distName = tr("MacOS X application bundle distribution."); break; case DistributionType::OS_MANAGED: distName = tr("Operating system managed distribution."); @@ -75,28 +77,21 @@ void AboutDialog::init(InitialMode initialMode) licenseContents.clear(); // Environment - ui->appDirEdit->setText(qApp->applicationDirPath()); - ui->cfgDirEdit->setText(CFG->getConfigDir()); - ui->pluginDirList->addItems(filterResourcePaths(PLUGINS->getPluginDirs())); - ui->iconDirList->addItems(filterResourcePaths(ICONMANAGER->getIconDirs())); - ui->formDirList->addItems(filterResourcePaths(FORMS->getFormDirs())); + ui->appDirEdit->setText(toNativePath(qApp->applicationDirPath())); + ui->cfgDirEdit->setText(toNativePath(CFG->getConfigDir())); + ui->pluginDirList->setPlainText(filterResourcePaths(PLUGINS->getPluginDirs()).join("\n")); + ui->iconDirList->setPlainText(filterResourcePaths(ICONMANAGER->getIconDirs()).join("\n")); + ui->formDirList->setPlainText(filterResourcePaths(FORMS->getFormDirs()).join("\n")); + ui->extensionDirList->setPlainText(filterResourcePaths(SQLITE_EXTENSIONS->getExtensionDirs()).join("\n")); ui->qtVerEdit->setText(QT_VERSION_STR); ui->sqlite3Edit->setText(CFG->getSqlite3Version()); - - QAction* copyAct; - for (QListWidget* w : {ui->pluginDirList, ui->iconDirList, ui->formDirList}) - { - copyAct = new QAction(tr("Copy"), w); - w->addAction(copyAct); - connect(copyAct, SIGNAL(triggered()), this, SLOT(copy())); - } } void AboutDialog::buildIndex() { static const QString entryTpl = QStringLiteral("
  • %1
  • "); QStringList entries; - for (const QString& idx : indexContents) + for (QString& idx : indexContents) entries += entryTpl.arg(idx); licenseContents.prepend(tr("

    Table of contents:

      %2
    ").arg(entries.join(""))); @@ -135,24 +130,8 @@ QStringList AboutDialog::filterResourcePaths(const QStringList& paths) if (path.startsWith(":")) continue; - output << path; + QString newPath = toNativePath(path); + output << newPath; } return output; } - -void AboutDialog::copy() -{ - QListWidget* list = dynamic_cast(sender()->parent()); - if (!list) - return; - - QList items = list->selectedItems(); - if (items.size() == 0) - return; - - QStringList lines; - for (QListWidgetItem* item : items) - lines << item->text(); - - QApplication::clipboard()->setText(lines.join("\n")); -} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h index a43daec..73d891d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h @@ -34,9 +34,6 @@ class GUI_API_EXPORT AboutDialog : public QDialog Ui::AboutDialog *ui = nullptr; QStringList indexContents; QString licenseContents; - - private slots: - void copy(); }; #endif // ABOUTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui index 16a6e1f..fda2099 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui @@ -7,7 +7,7 @@ 0 0 741 - 447 + 500 @@ -17,7 +17,7 @@ - 0 + 2 @@ -55,70 +55,51 @@ Environment - - - - Icon directories - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + true - - - - Qt::ActionsContextMenu - - - QAbstractItemView::ExtendedSelection + + + + - - + + - Form directories + Configuration directory Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::ActionsContextMenu - - - QAbstractItemView::ExtendedSelection - - - - - + + - Plugin directories + Qt version: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Configuration directory - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + true - - + + - Application directory + Icon directories Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -126,12 +107,16 @@ - - - Qt::ActionsContextMenu + + + true - - QAbstractItemView::ExtendedSelection + + + + + + true @@ -142,31 +127,34 @@ - - + + - Qt version: + + + + + + + + Plugin directories Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - + Application directory - - - - - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + SQLite 3 version: @@ -176,10 +164,30 @@ - - + + - + Form directories + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + SQLite extension directories + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp index 1d7ba66..90f816f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp @@ -44,20 +44,21 @@ void BindParamsDialog::init() void BindParamsDialog::initEditors() { QStringList paramNames; - for (BindParam* param : bindParams) + for (BindParam*& param : bindParams) paramNames << param->originalName; MultiEditor* firstEditor = nullptr; MultiEditor* multiEditor = nullptr; QVector> paramHistory = CFG->getBindParamHistory(paramNames); - for (BindParam* param : bindParams) + for (BindParam*& param : bindParams) { multiEditor = initEditor(param, paramHistory.size() > param->position ? paramHistory[param->position].second : QVariant()); if (firstEditor == nullptr) firstEditor = multiEditor; } - firstEditor->focusThisEditor(); + if (firstEditor) + firstEditor->focusThisEditor(); } MultiEditor* BindParamsDialog::initEditor(BindParam* param, const QVariant& cachedValue) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h index 666ffc4..99adca6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h @@ -27,7 +27,7 @@ class BindParamsDialog : public QDialog static const int margins = 2; static const int spacing = 2; - static const int minimumFieldHeight = 80; + static const int minimumFieldHeight = 120; Ui::BindParamsDialog *ui; QVector bindParams; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp index ebf9253..a217da6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp @@ -1,5 +1,6 @@ #include "columndialog.h" #include "common/unused.h" +#include "services/notifymanager.h" #include "ui_columndialog.h" #include "columndialogconstraintsmodel.h" #include "iconmanager.h" @@ -36,10 +37,6 @@ void ColumnDialog::init() ui->scale->setStrict(true, true); ui->precision->setStrict(true, true); - ui->typeCombo->addItem(""); - for (DataType::Enum type : DataType::getAllTypes()) - ui->typeCombo->addItem(DataType::toString(type)); - connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateDataType())); constraintsModel = new ColumnDialogConstraintsModel(); @@ -54,7 +51,7 @@ void ColumnDialog::init() connect(ui->constraintsView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editConstraint(QModelIndex))); connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateValidations())); connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateState())); - connect(ui->typeCombo, SIGNAL(currentTextChanged(const QString&)), this, SLOT(updateValidations())); + connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidations())); connect(ui->scale, SIGNAL(modified()), this, SLOT(updateValidations())); connect(ui->precision, SIGNAL(modified()), this, SLOT(updateValidations())); @@ -617,7 +614,7 @@ void ColumnDialog::updateValidations() setValidState(tb, true); } - for (SqliteCreateTable::Column::Constraint* constr : column->constraints) + for (SqliteCreateTable::Column::Constraint*& constr : column->constraints) updateConstraint(constr); updateTypeValidations(); @@ -640,11 +637,40 @@ void ColumnDialog::setColumn(SqliteCreateTable::Column* value) constraintsModel->setColumn(column.data()); ui->name->setText(value->name); - if (value->type) + + SqliteCreateTable* createTable = dynamic_cast(value->parentStatement()); + if (createTable->strict) + { + ui->typeCombo->setEditable(false); + for (DataType::Enum& type : DataType::getStrictValues()) + ui->typeCombo->addItem(DataType::toString(type)); + + ui->scale->setVisible(false); + ui->precision->setVisible(false); + ui->sizeLabel->setVisible(false); + ui->sizeCommaLabel->setVisible(false); + + if (value->type) + { + int idx = ui->typeCombo->findText(value->type->name, Qt::MatchFixedString); + if (idx > -1) + ui->typeCombo->setCurrentIndex(idx); + else + notifyError(tr("Could not match valid STRICT table datatype from declared type: %1.").arg(value->type->name)); + } + } + else { - ui->typeCombo->setEditText(value->type->name); - ui->scale->setValue(value->type->scale, false); - ui->precision->setValue(value->type->precision, false); + ui->typeCombo->addItem(""); + for (DataType::Enum& type : DataType::getAllTypesForUiDropdown()) + ui->typeCombo->addItem(DataType::toString(type)); + + if (value->type) + { + ui->typeCombo->setEditText(value->type->name); + ui->scale->setValue(value->type->scale, false); + ui->precision->setValue(value->type->precision, false); + } } updateValidations(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui index 6094ab1..8ff75fd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui @@ -7,7 +7,7 @@ 0 0 467 - 393 + 431 @@ -37,7 +37,7 @@ - + , diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp index be45873..8c80f6d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -1,6 +1,5 @@ #include "configdialog.h" #include "ui_configdialog.h" -#include "services/config.h" #include "uiconfig.h" #include "customconfigwidgetplugin.h" #include "services/pluginmanager.h" @@ -17,14 +16,18 @@ #include "plugins/confignotifiableplugin.h" #include "mainwindow.h" #include "common/unused.h" -#include "sqlitestudio.h" #include "configmapper.h" #include "datatype.h" #include "uiutils.h" +#include "common/utils.h" #include "translations.h" #include "plugins/uiconfiguredplugin.h" #include "dbtree/dbtree.h" #include "common/compatibility.h" +#include "windows/editorwindow.h" +#include "syntaxhighlighterplugin.h" +#include "sqleditor.h" +#include "style.h" #include #include #include @@ -39,7 +42,9 @@ #include #include #include +#include #include +#include #define GET_FILTER_STRING(Widget, WidgetType, Method) \ if (qobject_cast(Widget))\ @@ -50,6 +55,18 @@ if (w##WidgetType)\ return getFilterString(w##WidgetType) + " " + Widget->toolTip(); +class ConfigDialogItemDelegate : public QItemDelegate +{ + public: + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QVariant userData = index.data(Qt::UserRole); + bool isCategory = userData.isValid() && userData.toBool(); + QSize size = QItemDelegate::sizeHint(option, index); + return isCategory ? QSize(size.width(), size.height() * 1.7) : size; + } +}; + ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigDialog) @@ -59,12 +76,14 @@ ConfigDialog::ConfigDialog(QWidget *parent) : ConfigDialog::~ConfigDialog() { + rollbackColorsConfig(); + // Cancel transaction on CfgMain objects from plugins rollbackPluginConfigs(); // Notify plugins about dialog being closed UiConfiguredPlugin* cfgPlugin = nullptr; - for (Plugin* plugin : PLUGINS->getLoadedPlugins()) + for (Plugin*& plugin : PLUGINS->getLoadedPlugins()) { cfgPlugin = dynamic_cast(plugin); if (!cfgPlugin) @@ -74,14 +93,14 @@ ConfigDialog::~ConfigDialog() } // Delete UI and other resources + qDeleteAll(colorPreviewEditors); delete ui; safe_delete(configMapper); - for (ConfigMapper* mapper : pluginConfigMappers.values()) + for (ConfigMapper*& mapper : pluginConfigMappers) delete mapper; pluginConfigMappers.clear(); - } void ConfigDialog::configureDataEditors(const QString& dataTypeString) @@ -161,6 +180,23 @@ QString ConfigDialog::getFilterString(QTableWidget *widget) return strList.join(" "); } +void ConfigDialog::showEvent(QShowEvent* event) +{ + UNUSED(event); + ui->categoriesTree->resizeColumnToContents(0); + int adjustedColumnWidth = ui->categoriesTree->columnWidth(0) + 4; + if (adjustedColumnWidth > ui->categoriesTree->width()) { + if (adjustedColumnWidth > (width() / 2)) + adjustedColumnWidth = width() / 2; + + QList sizes = ui->splitter->sizes(); + int rightPartSize = sizes[0] + sizes[1] - adjustedColumnWidth; + sizes[0] = adjustedColumnWidth; + sizes[1] = rightPartSize; + ui->splitter->setSizes(sizes); + } +} + void ConfigDialog::init() { ui->setupUi(this); @@ -184,12 +220,15 @@ void ConfigDialog::init() initDataEditors(); initShortcuts(); initLangs(); + initTooltips(); + initColors(); connect(ui->categoriesTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(switchPage(QTreeWidgetItem*))); connect(ui->previewTabs, SIGNAL(currentChanged(int)), this, SLOT(updateStylePreview())); connect(ui->activeStyleCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateStylePreview())); connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); connect(ui->hideBuiltInPluginsCheck, SIGNAL(toggled(bool)), this, SLOT(updateBuiltInPluginsVisibility())); + connect(ui->codeColorsResetBtn, SIGNAL(pressed()), this, SLOT(resetCodeSyntaxColors())); QList entries; entries << CFG_UI.General.SortObjects @@ -199,12 +238,13 @@ void ConfigDialog::init() << CFG_UI.General.ShowSystemObjects << CFG_UI.General.ShowVirtualTableLabels; - for (CfgEntry* cfg : entries) + for (CfgEntry*& cfg : entries) connect(cfg, SIGNAL(changed(QVariant)), this, SLOT(markRequiresSchemasRefresh())); QStringList styles = QStyleFactory::keys(); styles.sort(Qt::CaseInsensitive); ui->activeStyleCombo->addItems(styles); + ui->activeStyleCombo->setCurrentText(STYLE->name()); connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(pageSwitched())); @@ -214,8 +254,14 @@ void ConfigDialog::init() ui->updatesGroup->setVisible(false); #endif + resettingColors = true; load(); + resettingColors = false; + colorChanged(); updateStylePreview(); + updateColorsAfterLoad(); + + ui->categoriesTree->expandAll(); } void ConfigDialog::load() @@ -229,14 +275,28 @@ void ConfigDialog::load() void ConfigDialog::save() { if (MainWindow::getInstance()->currentStyle().compare(ui->activeStyleCombo->currentText(), Qt::CaseInsensitive) != 0) + { + QList unmodifiedColors = prepareCodeSyntaxColorsForStyle(); + bool wasDark = STYLE->isDark(); + MainWindow::getInstance()->setStyle(ui->activeStyleCombo->currentText()); + if (STYLE->isDark() != wasDark) + { + resettingColors = true; // to avoid mass events of color change on syntax page + adjustSyntaxColorsForStyle(unmodifiedColors); + resettingColors = false; + colorChanged(); + } + } + QString loadedPlugins = collectLoadedPlugins(); storeSelectedFormatters(); CFG->beginMassSave(); CFG_CORE.General.LoadedPlugins.set(loadedPlugins); configMapper->saveFromWidget(ui->stackedWidget, true); commitPluginConfigs(); + commitColorsConfig(); CFG->commitMassSave(); if (requiresSchemasRefresh) @@ -302,7 +362,7 @@ void ConfigDialog::applyFilter(const QString &filter) QColor disabledColor = ui->categoriesTree->palette().color(QPalette::Disabled, QPalette::WindowText); if (filter.isEmpty()) { - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) item->setForeground(0, normalColor); return; @@ -318,10 +378,11 @@ void ConfigDialog::applyFilter(const QString &filter) QHash pageToCategoryItem = buildPageToCategoryItemMap(); QSet matchedCategories; - for (QWidget* page : pageToCategoryItem.keys()) + for (auto it = pageToCategoryItem.keyBegin(), end = pageToCategoryItem.keyEnd(); it != end; ++it) { - for (QWidget* matched : matchedWidgets) + for (QWidget* matched : qAsConst(matchedWidgets)) { + QWidget* page = *it; if (page->isAncestorOf(matched)) { if (!pageToCategoryItem.contains(page)) @@ -336,10 +397,10 @@ void ConfigDialog::applyFilter(const QString &filter) } } - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) item->setForeground(0, disabledColor); - for (QTreeWidgetItem* item : matchedCategories) + for (QTreeWidgetItem* item : qAsConst(matchedCategories)) { item->setForeground(0, normalColor); while ((item = item->parent()) != nullptr) @@ -350,7 +411,7 @@ void ConfigDialog::applyFilter(const QString &filter) QHash ConfigDialog::buildPageToCategoryItemMap() const { QHash pageNameToCategoryItem; - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) pageNameToCategoryItem[item->statusTip(0)] = item; QWidget* page = nullptr; @@ -393,7 +454,7 @@ QList ConfigDialog::getDefaultEditorsForType(DataType: }); QList results; - for (const PluginWithPriority& p: sortedPlugins) + for (PluginWithPriority& p: sortedPlugins) results << p.second; return results; @@ -427,7 +488,7 @@ void ConfigDialog::updateDataTypeEditors() ui->dataEditorsAvailableList->sortItems(); - for (MultiEditorWidgetPlugin* plugin : sortedPlugins) + for (MultiEditorWidgetPlugin*& plugin : sortedPlugins) addDataTypeEditor(plugin); } @@ -557,7 +618,7 @@ void ConfigDialog::addDataType(const QString& typeStr) void ConfigDialog::rollbackPluginConfigs() { CfgMain* mainCfg = nullptr; - for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + for (UiConfiguredPlugin*& plugin : pluginConfigMappers.keys()) { mainCfg = plugin->getMainUiConfig(); if (mainCfg) @@ -565,24 +626,68 @@ void ConfigDialog::rollbackPluginConfigs() } } +void ConfigDialog::rollbackColorsConfig() +{ + CFG_UI.Colors.rollback(); +} + void ConfigDialog::commitPluginConfigs() { CfgMain* mainCfg = nullptr; - for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + for (auto it = pluginConfigMappers.keyBegin(), end = pluginConfigMappers.keyEnd(); it != end; ++it) { - mainCfg = plugin->getMainUiConfig(); + mainCfg = (*it)->getMainUiConfig(); if (mainCfg) { mainCfg->commit(); mainCfg->begin(); // be prepared for further changes after "Apply" } } + + auto it = highlightingPluginForPreviewEditor.iterator(); + while (it.hasNext()) + { + auto item = it.next(); + if (item.value()->getLanguageName() == "SQL") + continue; + + item.value()->refreshFormats(); + } +} + +void ConfigDialog::commitColorsConfig() +{ + CFG_UI.Colors.commit(); + CFG_UI.Colors.begin(); } void ConfigDialog::connectMapperSignals(ConfigMapper* mapper) { - connect(mapper, SIGNAL(modified()), this, SLOT(markModified())); - connect(mapper, SIGNAL(notifyEnabledWidgetModified(QWidget*, CfgEntry*, const QVariant&)), this, SLOT(notifyPluginsAboutModification(QWidget*, CfgEntry*, const QVariant&))); + connect(mapper, SIGNAL(modified(QWidget*)), this, SLOT(markModified())); + connect(mapper, SIGNAL(notifyEnabledWidgetModified(QWidget*,CfgEntry*,QVariant)), this, SLOT(notifyPluginsAboutModification(QWidget*,CfgEntry*,QVariant))); +} + +QList ConfigDialog::getShortcutsCfgMains() const +{ + static const QString metaName = CFG_SHORTCUTS_METANAME; + QList mains; + for (CfgMain*& cfgMain : CfgMain::getInstances()) + { + if (cfgMain->getMetaName() != metaName) + continue; + + mains << cfgMain; + } + return mains; +} + +QList ConfigDialog::getShortcutsCfgCategories() const +{ + QList categories; + for (CfgMain*& cfgMain : getShortcutsCfgMains()) + categories.append(cfgMain->getCategories().values()); + + return categories; } void ConfigDialog::updateDataTypeListState() @@ -792,23 +897,23 @@ void ConfigDialog::detailsClicked(const QString& pluginName) // Rows QStringList rows; - rows << row.arg(tr("Description:", "plugin details")).arg(PLUGINS->getDescription(pluginName)); - rows << row.arg(tr("Category:", "plugin details")).arg(type->getTitle()); - rows << row.arg(tr("Version:", "plugin details")).arg(PLUGINS->getPrintableVersion(pluginName)); - rows << row.arg(tr("Author:", "plugin details")).arg(PLUGINS->getAuthor(pluginName)); + rows << row.arg(tr("Description:", "plugin details"), PLUGINS->getDescription(pluginName)); + rows << row.arg(tr("Category:", "plugin details"), type->getTitle()); + rows << row.arg(tr("Version:", "plugin details"), PLUGINS->getPrintableVersion(pluginName)); + rows << row.arg(tr("Author:", "plugin details"), PLUGINS->getAuthor(pluginName)); rows << hline; - rows << row.arg(tr("Internal name:", "plugin details")).arg(pluginName); - rows << row.arg(tr("Dependencies:", "plugin details")).arg(PLUGINS->getDependencies(pluginName).join(", ")); - rows << row.arg(tr("Conflicts:", "plugin details")).arg(PLUGINS->getConflicts(pluginName).join(", ")); + rows << row.arg(tr("Internal name:", "plugin details"), pluginName); + rows << row.arg(tr("Dependencies:", "plugin details"), PLUGINS->getDependencies(pluginName).join(", ")); + rows << row.arg(tr("Conflicts:", "plugin details"), PLUGINS->getConflicts(pluginName).join(", ")); // Message - QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName)).arg(rows.join("")); + QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName), rows.join("")); QMessageBox::information(this, tr("Plugin details"), pluginDetails); } void ConfigDialog::failedToLoadPlugin(const QString& pluginName) { - QTreeWidgetItem* theItem = itemToPluginNameMap.valueByRight(pluginName); + QTreeWidgetItem* theItem = pluginListItemToPluginNameMap.valueByRight(pluginName); if (!theItem) { qWarning() << "Plugin" << pluginName << "failed to load, but it could not be found on the plugins list in ConfigDialog."; @@ -833,7 +938,7 @@ void ConfigDialog::loadUnloadPlugin(QTreeWidgetItem* item, int column) if (column != 0) return; - QString pluginName = itemToPluginNameMap.valueByLeft(item); + QString pluginName = pluginListItemToPluginNameMap.valueByLeft(item); if (PLUGINS->isBuiltIn(pluginName)) return; @@ -866,6 +971,10 @@ void ConfigDialog::pluginAboutToUnload(Plugin* plugin, PluginType* type) if (notifiablePlugin && notifiablePlugins.contains(notifiablePlugin)) notifiablePlugins.removeOne(notifiablePlugin); + // Update code colors page + if (type->isForPluginType()) + highlighterPluginUnloaded(dynamic_cast(plugin)); + // Deinit page deinitPluginPage(plugin); @@ -879,6 +988,14 @@ void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfi if (type->isForPluginType()) codeFormatterLoaded(); + // Update code colors page + if (type->isForPluginType()) + highlighterPluginLoaded(dynamic_cast(plugin)); + + QTreeWidgetItem* listItem = pluginListItemToPluginNameMap.valueByRight(plugin->getName()); + if (listItem && listItem->checkState(0) == Qt::Unchecked) + listItem->setCheckState(0, Qt::Checked); + // Init page if (!initPluginPage(plugin, skipConfigLoading)) return; @@ -901,7 +1018,9 @@ void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfi void ConfigDialog::pluginUnloaded(const QString& pluginName, PluginType* type) { - UNUSED(pluginName); + QTreeWidgetItem* item = pluginListItemToPluginNameMap.valueByRight(pluginName); + if (item && item->checkState(0) == Qt::Checked) + item->setCheckState(0, Qt::Unchecked); // Update formatters page if (type->isForPluginType()) @@ -918,7 +1037,7 @@ void ConfigDialog::updatePluginCategoriesVisibility() void ConfigDialog::updateBuiltInPluginsVisibility() { bool hideBuiltIn = ui->hideBuiltInPluginsCheck->isChecked(); - QHashIterator it = itemToPluginNameMap.iterator(); + QHashIterator it = pluginListItemToPluginNameMap.iterator(); while (it.hasNext()) { it.next(); @@ -929,6 +1048,13 @@ void ConfigDialog::updateBuiltInPluginsVisibility() } } +void ConfigDialog::refreshColorsInSyntaxHighlighters() +{ + auto it = highlightingPluginForPreviewEditor.iterator(); + while (it.hasNext()) + it.next().value()->refreshFormats(); +} + void ConfigDialog::applyShortcutsFilter(const QString &filter) { QTreeWidgetItem* categoryItem = nullptr; @@ -964,10 +1090,144 @@ void ConfigDialog::markRequiresSchemasRefresh() void ConfigDialog::notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value) { - for (ConfigNotifiablePlugin* plugin : notifiablePlugins) + for (ConfigNotifiablePlugin*& plugin : notifiablePlugins) plugin->configModified(key, value); } +void ConfigDialog::resetCodeSyntaxColors() +{ + resettingColors = true; + for (QWidget*& widget : configMapper->getAllConfigWidgets(ui->commonCodeColorsGroup)) + configMapper->applyConfigDefaultValueToWidget(widget); + + resettingColors = false; + colorChanged(); +} + +void ConfigDialog::colorChanged() +{ + refreshColorsInSyntaxHighlighters(); + for (QSyntaxHighlighter*& highligter : colorPreviewHighlighters) + highligter->rehighlight(); + + if (codePreviewSqlEditor) + codePreviewSqlEditor->colorsConfigChanged(); +} + +void ConfigDialog::adjustSyntaxColorsForStyle(QList& unmodifiedColors) +{ + for (QWidget*& w : unmodifiedColors) + configMapper->applyConfigDefaultValueToWidget(w); +} + +void ConfigDialog::highlighterPluginLoaded(SyntaxHighlighterPlugin* plugin) +{ + QPlainTextEdit* editor = nullptr; + if (plugin->getLanguageName() == "SQL") + { + // For SQL there is assumed just one, built-in plugin + codePreviewSqlEditor = new SqlEditor(ui->codeColorsPreviewTabWidget); + codePreviewSqlEditor->setShowLineNumbers(false); + codePreviewSqlEditor->setCurrentQueryHighlighting(true); + editor = codePreviewSqlEditor; + } + else + { + editor = new QPlainTextEdit(ui->codeColorsPreviewTabWidget); + editor->setFont(CFG_UI.Fonts.SqlEditor.get()); + colorPreviewHighlighters << plugin->createSyntaxHighlighter(editor); + } + + editor->setPlainText(plugin->previewSampleCode()); + editor->setReadOnly(true); + colorPreviewEditors << editor; + highlightingPluginForPreviewEditor.insert(editor, plugin); + ui->codeColorsPreviewTabWidget->addTab(editor, plugin->getLanguageName()); +} + +void ConfigDialog::highlighterPluginUnloaded(SyntaxHighlighterPlugin* plugin) +{ + QPlainTextEdit* editor = highlightingPluginForPreviewEditor.valueByRight(plugin); + if (!editor) + { + qCritical() << "Highlighter plugin unloaded for which there is no preview editor! Application crash is possible. Plugin:" << plugin->getName(); + return; + } + + QTextDocument* document = editor->document(); + QSyntaxHighlighter* highlighter = findFirst(colorPreviewHighlighters, [document](auto highlighter) {return highlighter->document() == document;}); + + ui->codeColorsPreviewTabWidget->removeTab(ui->codeColorsPreviewTabWidget->indexOf(editor)); + colorPreviewHighlighters.removeOne(highlighter); + colorPreviewEditors.removeOne(editor); + delete editor; + + highlightingPluginForPreviewEditor.removeRight(plugin); +} + +QList ConfigDialog::prepareCodeSyntaxColorsForStyle() +{ + QList unmodified; + for (QWidget*& w : configMapper->getAllConfigWidgets(ui->commonCodeColorsGroup)) + { + CfgEntry* entry = configMapper->getConfigForWidget(w); + if (entry->getDefaultValue() == entry->get()) + unmodified << w; + } + return unmodified; +} + +void ConfigDialog::initColors() +{ + CFG_UI.Colors.begin(); + connect(configMapper, &ConfigMapper::modified, this, + [this](QWidget* widget) + { + CfgEntry* key = configMapper->getBindConfigForWidget(widget); + if (!key) + { + qCritical() << "Missing CfgEntry in Colors configuration for widget" << widget->objectName(); + return; + } + + if (key->getCategory() != CFG_UI.Colors) + return; + + configMapper->saveFromWidget(widget, key); + if (key->getName().endsWith("Custom")) + toggleColorButtonState(key); + + if (resettingColors) + return; + + colorChanged(); + }); +} + +void ConfigDialog::updateColorsAfterLoad() +{ + QHash entries = CFG_UI.Colors.getEntries(); + auto it = entries.begin(); + while (it != entries.end()) + { + if (it.key().endsWith("Custom")) + toggleColorButtonState(it.value()); + + it++; + } +} + +void ConfigDialog::toggleColorButtonState(CfgEntry* colorCheckEntry) +{ + CfgEntry* colorKey = colorCheckEntry->getCategory()->getEntryByName(colorCheckEntry->getName().chopped(6)); + if (colorKey) + { + QWidget* button = configMapper->getBindWidgetForConfig(colorKey); + if (button) + button->setEnabled(colorCheckEntry->get().toBool()); + } +} + void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem) { categoryItem->setHidden(categoryItem->childCount() == 0); @@ -976,7 +1236,7 @@ void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryIte QString ConfigDialog::collectLoadedPlugins() const { QStringList loaded; - QHashIterator it = itemToPluginNameMap.iterator(); + QHashIterator it = pluginListItemToPluginNameMap.iterator(); while (it.hasNext()) { it.next(); @@ -1147,8 +1407,7 @@ QTreeWidgetItem* ConfigDialog::createPluginsTypeItem(const QString& widgetName, if (item->statusTip(0) == widgetName) return item; } - return nullptr; - + return new QTreeWidgetItem({title}); } QTreeWidgetItem* ConfigDialog::getItemByTitle(const QString& title) const @@ -1195,23 +1454,24 @@ void ConfigDialog::initPlugins() // Recreate QTreeWidgetItem *typeItem = nullptr; - for (PluginType* pluginType : PLUGINS->getPluginTypes()) + for (PluginType*& pluginType : PLUGINS->getPluginTypes()) { typeItem = createPluginsTypeItem(pluginType->getConfigUiForm(), pluginType->getTitle()); - if (!typeItem) - continue; item->addChild(typeItem); pluginTypeToItemMap[pluginType] = typeItem; - for (Plugin* plugin : pluginType->getLoadedPlugins()) + for (Plugin*& plugin : pluginType->getLoadedPlugins()) pluginLoaded(plugin, pluginType, true); } updatePluginCategoriesVisibility(); + // Using old connect() syntax, becase the pointer syntax does not work correctly with signals declared in an interface class. + // At least that's the case with Qt 5.15.2. connect(PLUGINS, SIGNAL(loaded(Plugin*,PluginType*)), this, SLOT(pluginLoaded(Plugin*,PluginType*))); connect(PLUGINS, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginAboutToUnload(Plugin*,PluginType*))); + connect(PLUGINS, SIGNAL(unloaded(QString,PluginType*)), this, SLOT(pluginUnloaded(QString,PluginType*))); } void ConfigDialog::initPluginsPage() @@ -1255,7 +1515,7 @@ void ConfigDialog::initPluginsPage() categoryRow = 0; QList pluginTypes = PLUGINS->getPluginTypes(); sSort(pluginTypes, PluginType::nameLessThan); - for (PluginType* pluginType : pluginTypes) + for (PluginType*& pluginType : pluginTypes) { category = new QTreeWidgetItem({pluginType->getTitle()}); font.setItalic(false); @@ -1275,7 +1535,7 @@ void ConfigDialog::initPluginsPage() itemRow = 0; pluginNames = pluginType->getAllPluginNames(); sSort(pluginNames); - for (const QString& pluginName : pluginNames) + for (QString& pluginName : pluginNames) { builtIn = PLUGINS->isBuiltIn(pluginName); title = PLUGINS->getTitle(pluginName); @@ -1290,10 +1550,10 @@ void ConfigDialog::initPluginsPage() category->addChild(item); - itemToPluginNameMap.insert(item, pluginName); + pluginListItemToPluginNameMap.insert(item, pluginName); // Details button - detailsLabel = new QLabel(QString("%2 ").arg(pluginName).arg(tr("Details")), ui->pluginsList); + detailsLabel = new QLabel(QString("%2 ").arg(pluginName, tr("Details")), ui->pluginsList); detailsLabel->setAlignment(Qt::AlignRight); itemIndex = ui->pluginsList->model()->index(itemRow, 1, categoryIndex); ui->pluginsList->setIndexWidget(itemIndex, detailsLabel); @@ -1322,10 +1582,10 @@ void ConfigDialog::initPluginsPage() bool ConfigDialog::initPluginPage(Plugin* plugin, bool skipConfigLoading) { - if (!dynamic_cast(plugin)) + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + if (!cfgPlugin) return false; - UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); QString pluginName = plugin->getName(); QString formName = cfgPlugin->getConfigUiForm(); QWidget* widget = FORMS->createWidget(formName); @@ -1398,7 +1658,7 @@ void ConfigDialog::initDataEditors() sSort(dataTypeList); QListWidgetItem* item = nullptr; - for (const QString& type : dataTypeList) + for (QString& type : dataTypeList) { item = new QListWidgetItem(type); if (!DataType::getAllNames().contains(type)) @@ -1441,49 +1701,30 @@ void ConfigDialog::initShortcuts() ui->shortcutsTable->header()->setSectionResizeMode(2, QHeaderView::Fixed); ui->shortcutsTable->header()->resizeSection(1, 150); ui->shortcutsTable->header()->resizeSection(2, 26); + ui->shortcutsTable->header()->resizeSection(3, 26); ui->shortcutsFilterEdit->setClearButtonEnabled(true); new UserInputFilter(ui->shortcutsFilterEdit, this, SLOT(applyShortcutsFilter(QString))); - static const QString metaName = CFG_SHORTCUTS_METANAME; - QList categories; - for (CfgMain* cfgMain : CfgMain::getInstances()) - { - if (cfgMain->getMetaName() != metaName) - continue; - - for (CfgCategory* cat : cfgMain->getCategories().values()) - categories << cat; - } + QList categories = getShortcutsCfgCategories(); + ui->shortcutsTable->setItemDelegate(new ConfigDialogItemDelegate()); sSort(categories, [](CfgCategory* cat1, CfgCategory* cat2) -> bool { return cat1->getTitle().compare(cat2->getTitle()) < 0; }); - for (CfgCategory* cat : categories) + for (CfgCategory*& cat : categories) initShortcuts(cat); } void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) { - QTreeWidgetItem* item = nullptr; - QFont font; - QModelIndex categoryIndex; - QModelIndex itemIndex; - QKeySequenceEdit *sequenceEdit = nullptr; - QToolButton* clearButton = nullptr; - QString title; - QSize itemSize; - // Font and metrics - item = new QTreeWidgetItem({""}); - font = item->font(0); - - QFontMetrics fm(font); - itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); - - delete item; + QTreeWidgetItem item({""}); + QFont font = item.font(0); +// QFontMetrics fm(font); +// QSize itemSize = QSize(-1, -1); // Creating... QBrush categoryBg = ui->shortcutsTable->palette().button(); @@ -1493,47 +1734,63 @@ void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) font.setItalic(false); font.setBold(true); category->setFont(0, font); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { + category->setData(i, Qt::UserRole, true); category->setBackground(i, categoryBg); category->setForeground(i, categoryFg); } - category->setSizeHint(0, itemSize); +// category->setSizeHint(0, itemSize); category->setFlags(category->flags() ^ Qt::ItemIsSelectable); ui->shortcutsTable->addTopLevelItem(category); int categoryRow = ui->shortcutsTable->topLevelItemCount() - 1; - categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); + QModelIndex categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); int itemRow = 0; QStringList entryNames = cfgCategory->getEntries().keys(); sSort(entryNames); - for (const QString& entryName : entryNames) + for (QString& entryName : entryNames) { + CfgEntry* cfgEntry = cfgCategory->getEntries()[entryName]; + // Title - title = cfgCategory->getEntries()[entryName]->getTitle(); - item = new QTreeWidgetItem(category, {title}); + QString title = cfgEntry->getTitle(); + new QTreeWidgetItem(category, {title}); // Key edit - sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); + QKeySequenceEdit* sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); sequenceEdit->setFixedWidth(150); - sequenceEdit->setProperty("cfg", cfgCategory->getEntries()[entryName]->getFullKey()); - itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); + sequenceEdit->setProperty("cfg", cfgEntry->getFullKey()); + QModelIndex itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); ui->shortcutsTable->setIndexWidget(itemIndex, sequenceEdit); configMapper->addExtraWidget(sequenceEdit); // Clear button - clearButton = new QToolButton(ui->shortcutsTable); + QToolButton* clearButton = new QToolButton(ui->shortcutsTable); clearButton->setIcon(ICONS.CLEAR_LINEEDIT); - connect(clearButton, &QToolButton::clicked, [this, sequenceEdit]() + clearButton->setToolTip(tr("Clear hotkey for this action")); + connect(clearButton, &QToolButton::clicked, this, [this, sequenceEdit]() { sequenceEdit->clear(); this->markModified(); - }); itemIndex = ui->shortcutsTable->model()->index(itemRow, 2, categoryIndex); ui->shortcutsTable->setIndexWidget(itemIndex, clearButton); + // Restore default value button + QToolButton* defaultButton = new QToolButton(ui->shortcutsTable); + defaultButton->setIcon(ICONS.RESTORE_DEFAULT); + defaultButton->setToolTip(tr("Restore original hotkey for this action")); + connect(defaultButton, &QToolButton::clicked, this, [this, sequenceEdit, cfgEntry]() + { + cfgEntry->reset(); + sequenceEdit->setKeySequence(QKeySequence::fromString(cfgEntry->get().toString())); + this->markModified(); + }); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 3, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, defaultButton); + itemRow++; } @@ -1545,7 +1802,9 @@ void ConfigDialog::initLangs() QMap langs = getAvailableLanguages(); int idx = 0; int selected = -1; - for (const QString& lang : langs.keys()) + QStringList langList = langs.keys(); + strSort(langList, Qt::CaseInsensitive); + for (QString& lang : langList) { ui->langCombo->addItem(lang, langs[lang]); if (langs[lang] == SQLITESTUDIO->getCurrentLang()) @@ -1557,6 +1816,21 @@ void ConfigDialog::initLangs() ui->langCombo->setCurrentIndex(selected); } +void ConfigDialog::initTooltips() +{ + ui->execQueryUnderCursorCheck->setToolTip(ui->execQueryUnderCursorCheck->toolTip().arg( + GET_SHORTCUT_ENTRY(EditorWindow, EXEC_ONE_QUERY)->get().toString(), + GET_SHORTCUT_ENTRY(EditorWindow, EXEC_ALL_QUERIES)->get().toString() + )); + + setValidStateTooltip(ui->commonCodeColorsGroup, + tr("Here you can configure colors for code syntax highlighting. " + "They are shared across different languages - not only for SQL, but also JavaScript and others. " + "By default a theme-based color is used. To define your own color, enable a custom color " + "by selecting a checkbox next to a particular color.")); + +} + bool ConfigDialog::isPluginCategoryItem(QTreeWidgetItem *item) const { return item->parent() && item->parent()->parent() && item->parent()->parent() == getPluginsCategoryItem(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h index f0ab1de..7f5abb3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h @@ -26,6 +26,13 @@ class ConfigMapper; class MultiEditorWidgetPlugin; class ConfigNotifiablePlugin; class UiConfiguredPlugin; +class SyntaxHighlighterPlugin; +class QPlainTextEdit; +class QSyntaxHighlighter; +class SqlEditor; + +#define CFG_UI_NAME "Ui" +#define CFG_COLORS_NAME "Colors" class GUI_API_EXPORT ConfigDialog : public QDialog { @@ -43,6 +50,9 @@ class GUI_API_EXPORT ConfigDialog : public QDialog static QString getFilterString(QListWidget* widget); static QString getFilterString(QTableWidget* widget); + protected: + void showEvent(QShowEvent* event); + private: void init(); void load(); @@ -57,6 +67,10 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void initShortcuts(); void initShortcuts(CfgCategory* cfgCategory); void initLangs(); + void initTooltips(); + void initColors(); + void updateColorsAfterLoad(); + void toggleColorButtonState(CfgEntry* colorCheckEntry); void applyStyle(QWidget* widget, QStyle* style); QTreeWidgetItem* getPluginsCategoryItem() const; QTreeWidgetItem* getPluginsCategoryItem(PluginType* type) const; @@ -83,13 +97,23 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void setPluginNamesForDataTypeItem(QListWidgetItem* typeItem, const QStringList& pluginNames); void addDataType(const QString& typeStr); void rollbackPluginConfigs(); + void rollbackColorsConfig(); void commitPluginConfigs(); + void commitColorsConfig(); void connectMapperSignals(ConfigMapper* mapper); + QList getShortcutsCfgMains() const; + QList getShortcutsCfgCategories() const; + void refreshColorsInSyntaxHighlighters(); + void colorChanged(); + QList prepareCodeSyntaxColorsForStyle(); + void adjustSyntaxColorsForStyle(QList& unmodifiedColors); + void highlighterPluginLoaded(SyntaxHighlighterPlugin* plugin); + void highlighterPluginUnloaded(SyntaxHighlighterPlugin* plugin); Ui::ConfigDialog *ui = nullptr; QStyle* previewStyle = nullptr; QHash nameToPage; - BiHash itemToPluginNameMap; + BiHash pluginListItemToPluginNameMap; QHash pluginTypeToItemMap; QHash pluginToItemMap; QHash formatterLangToPluginComboMap; @@ -102,6 +126,11 @@ class GUI_API_EXPORT ConfigDialog : public QDialog bool modifiedFlag = false; QList notifiablePlugins; bool requiresSchemasRefresh = false; + QList colorPreviewEditors; + SqlEditor* codePreviewSqlEditor = nullptr; + QList colorPreviewHighlighters; + BiHash highlightingPluginForPreviewEditor; + bool resettingColors = false; private slots: void refreshFormattersPage(); @@ -138,6 +167,7 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void applyShortcutsFilter(const QString& filter); void markRequiresSchemasRefresh(); void notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value); + void resetCodeSyntaxColors(); public slots: void accept(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui index 4452f63..475d7b0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui @@ -6,8 +6,8 @@ 0 0 - 770 - 539 + 866 + 580 @@ -148,6 +148,18 @@ :/icons/img/config_font.png:/icons/img/config_font.png + + + Code colors + + + codeColorsPage + + + + :/icons/img/config_colors.png:/icons/img/config_colors.png + + @@ -161,6 +173,18 @@ :/icons/img/database_online.png:/icons/img/database_online.png + + + Code assistant + + + codeAssistantPage + + + + :/icons/img/code_assistant.png:/icons/img/code_assistant.png + + Data browsing @@ -217,7 +241,7 @@ - 1 + 4 @@ -395,8 +419,8 @@ 0 0 - 564 - 540 + 433 + 718 @@ -406,41 +430,16 @@ Data browsing and editing - - - - - 150 - 16777215 - - - - 1 - - - 99999 - - - General.NumberOfRowsPerPage - - - - + - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - - 10 + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - 99999 - - - 600 + 999999 - General.MaxInitialColumnWith + General.PopulateHistorySize @@ -452,16 +451,42 @@ - + - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + Number of memorized table populating configurations + + + + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> - Limit initial data column width to (in pixels): + Limit number of rows for in case of dozens of columns + + + General.LimitRowsForManyColumns + + + + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + Show column and row details tooltip in data view + + + General.ShowDataViewTooltips - + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> @@ -474,65 +499,118 @@ - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + + 150 + 16777215 + + + + 1 - 999999 + 99999 - General.PopulateHistorySize + General.NumberOfRowsPerPage - - + + - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - Number of memorized table populating configurations + Keep NULL value when entering empty value + + + General.KeepNullWhenEmptyValue - - + + - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> - Show column and row details tooltip in data view + Use scientific notation for real numbers in the grid view - General.ShowDataViewTooltips + General.UseSciFormatForDoubles - - + + + + + + + Data column width + + + + - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> - Keep NULL value when entering empty value + Enlarge column when entering value longer than current width - General.KeepNullWhenEmptyValue + General.EnlargeColumnForValue - - + + + + + 150 + 16777215 + + - <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + 10 + + + 99999 + + + 600 + + + General.MaxInitialColumnWith + + + + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> - Limit number of rows for in case of dozens of columns + Limit automatic data column width to (in pixels): + + + + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + Keep at least the width to show complete column name - General.LimitRowsForManyColumns + General.ColumnWidthForName @@ -851,13 +929,13 @@ - - + + - Number of queries kept in the history. + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - History size: + Number of memorized query parameters @@ -877,7 +955,7 @@ - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> Execute only the query under the cursor @@ -887,13 +965,39 @@ - - + + - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + Number of queries kept in the history. - Number of memorized query parameters + History size: + + + + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + Wrap lines in SQL editor + + + General.SqlEditorWrapWords + + + + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + Highlight current query + + + General.SqlEditorCurrQueryHighlight @@ -941,7 +1045,7 @@ Allow multiple instances of the application at the same time - General.AllowMultipleSessions + General.AllowMultipleSessions @@ -985,6 +1089,387 @@ + + + + 15 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Code syntax colors + + + + + + Keyword foreground + + + Colors.SyntaxKeywordFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxForeground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxBlobFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxKeywordFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxValidObject + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxCommentFg + + + + + + + Regular foreground + + + Colors.SyntaxForegroundCustom + + + + + + + Comment foreground + + + Colors.SyntaxCommentFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxNumberFg + + + + + + + Number foreground + + + Colors.SyntaxNumberFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxParenthesisBg + + + + + + + Valid objects foreground + + + Colors.SyntaxValidObjectCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxStringFg + + + + + + + + 50 + 16777215 + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + + + Colors.SyntaxCurrentQueryBg + + + + + + + BLOB value foreground + + + Colors.SyntaxBlobFgCustom + + + + + + + String foreground + + + Colors.SyntaxStringFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxCurrentLineBg + + + + + + + + + + Colors.SyntaxParenthesisFg + + + + + + + Bind parameter foreground + + + Colors.SyntaxBindParamFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxBindParamFg + + + + + + + Current line background + + + Colors.SyntaxCurrentLineBgCustom + + + + + + + Current query background + + + Colors.SyntaxCurrentQueryBgCustom + + + + + + + Matched parenthesis background + + + Colors.SyntaxParenthesisBgCustom + + + + + + + Matched parenthesis foreground + + + Colors.SyntaxParenthesisFgCustom + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Reset to defaults + + + + + + + + @@ -1053,7 +1538,12 @@ - + + + + + + @@ -1084,8 +1574,8 @@ 0 0 - 339 - 264 + 364 + 283 @@ -1503,10 +1993,13 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Abcdefgh</span></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Abcdefgh</span></p></body></html> @@ -1596,8 +2089,8 @@ p, li { white-space: pre-wrap; } 0 0 - 206 - 298 + 222 + 313 @@ -1700,6 +2193,45 @@ p, li { white-space: pre-wrap; } + + + + + + Code assistant settings + + + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + Automatically trigger the assistant after a dot is typed after an object name + + + CodeAssistant.AutoTrigger + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + @@ -1717,19 +2249,13 @@ p, li { white-space: pre-wrap; } - - - - 0 - 0 - 100 - 30 - - - - + + ConfigRadioButton + QRadioButton +
    common/configradiobutton.h
    +
    FontEdit QWidget @@ -1737,9 +2263,9 @@ p, li { white-space: pre-wrap; } 1 - ConfigRadioButton - QRadioButton -
    common/configradiobutton.h
    + ColorButton + QPushButton +
    common/colorbutton.h
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp index 1e56258..e6a53db 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp @@ -1,4 +1,6 @@ #include "dbdialog.h" +#include "common/immediatetooltip.h" +#include "services/notifymanager.h" #include "ui_dbdialog.h" #include "services/pluginmanager.h" #include "plugins/dbplugin.h" @@ -18,6 +20,8 @@ #include #include #include +#include +#include DbDialog::DbDialog(Mode mode, QWidget *parent) : QDialog(parent), @@ -42,14 +46,32 @@ void DbDialog::setPermanent(bool perm) ui->permamentCheckBox->setChecked(perm); } + +void DbDialog::dragEnterEvent(QDragEnterEvent* e) +{ + if (e->mimeData()->hasUrls()) + e->acceptProposedAction(); +} + +void DbDialog::dropEvent(QDropEvent* e) +{ + if (!e->isAccepted() && e->mimeData()->hasUrls()) + { + setPath(e->mimeData()->urls().first().toLocalFile()); + e->accept(); + } +} + QString DbDialog::getPath() { - return ui->fileEdit->text(); + QString newPath = QDir::fromNativeSeparators(ui->fileEdit->text()); + return newPath; } void DbDialog::setPath(const QString& path) { - ui->fileEdit->setText(path); + QString newPath = QDir::toNativeSeparators(path); + ui->fileEdit->setText(newPath); } QString DbDialog::getName() @@ -82,7 +104,7 @@ void DbDialog::showEvent(QShowEvent *e) int idx = ui->typeCombo->findText(db->getTypeLabel()); ui->typeCombo->setCurrentIndex(idx); - ui->fileEdit->setText(db->getPath()); + setPath(db->getPath()); ui->nameEdit->setText(db->getName()); disableTypeAutodetection = false; } @@ -111,8 +133,7 @@ void DbDialog::showEvent(QShowEvent *e) void DbDialog::init() { ui->setupUi(this); - - ui->browseCreateButton->setIcon(ICONS.PLUS); + connIconTooltip = new ImmediateTooltip(ui->testConnIcon); for (DbPlugin* dbPlugin : PLUGINS->getLoadedPlugins()) dbPlugins[dbPlugin->getLabel()] = dbPlugin; @@ -122,12 +143,10 @@ void DbDialog::init() typeLabels.sort(Qt::CaseInsensitive); ui->typeCombo->addItems(typeLabels); - ui->browseCreateButton->setVisible(true); ui->testConnIcon->setVisible(false); connect(ui->fileEdit, SIGNAL(textChanged(QString)), this, SLOT(fileChanged(QString))); connect(ui->nameEdit, SIGNAL(textEdited(QString)), this, SLOT(nameModified(QString))); - connect(ui->browseCreateButton, SIGNAL(clicked()), this, SLOT(browseClicked())); connect(ui->browseOpenButton, SIGNAL(clicked()), this, SLOT(browseClicked())); connect(ui->testConnButton, SIGNAL(clicked()), this, SLOT(testConnectionClicked())); connect(ui->typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(dbTypeChanged(int))); @@ -145,7 +164,7 @@ void DbDialog::updateOptions() setUpdatesEnabled(false); // Remove olds - for (QWidget* w : optionWidgets) + for (QWidget*& w : optionWidgets) { ui->optionsGrid->removeWidget(w); delete w; @@ -153,8 +172,7 @@ void DbDialog::updateOptions() customBrowseHandler = nullptr; ui->pathGroup->setTitle(tr("File")); - ui->browseOpenButton->setToolTip(tr("Browse for existing database file on local computer")); - ui->browseCreateButton->setVisible(true); + ui->browseOpenButton->setToolTip(tr("Select new or existing file on local computer")); optionWidgets.clear(); optionKeyToWidget.clear(); @@ -191,7 +209,6 @@ void DbDialog::addOption(const DbPluginOption& option, int& row) // This option does not add any editor, but has it's own label for path edit. row--; ui->pathGroup->setTitle(option.label); - ui->browseCreateButton->setVisible(false); if (!option.toolTip.isEmpty()) ui->browseOpenButton->setToolTip(option.toolTip); @@ -450,7 +467,7 @@ void DbDialog::updateType() if (disableTypeAutodetection) return; - DbPlugin* validPlugin = SQLITESTUDIO->getDbManager()->getPluginForDbFile(ui->fileEdit->text()); + DbPlugin* validPlugin = SQLITESTUDIO->getDbManager()->getPluginForDbFile(getPath()); if (!validPlugin || validPlugin->getLabel() == ui->typeCombo->currentText()) return; @@ -476,14 +493,20 @@ QHash DbDialog::collectOptions() return options; } -bool DbDialog::testDatabase() +bool DbDialog::testDatabase(QString& errorMsg) { if (ui->typeCombo->currentIndex() < 0) + { + errorMsg = tr("Database type not selected."); return false; + } - QString path = ui->fileEdit->text(); + QString path = getPath(); if (path.isEmpty()) + { + errorMsg = tr("Database path not specified."); return false; + } QUrl url(path); if (url.scheme().isEmpty()) @@ -491,7 +514,7 @@ bool DbDialog::testDatabase() QHash options = collectOptions(); DbPlugin* plugin = dbPlugins[ui->typeCombo->currentText()]; - Db* testDb = plugin->getInstance("", path, options); + Db* testDb = plugin->getInstance("", path, options, &errorMsg); bool res = false; if (testDb) @@ -499,6 +522,7 @@ bool DbDialog::testDatabase() if (testDb->openForProbing()) { res = !testDb->exec("SELECT sqlite_version();")->getSingleCell().toString().isEmpty(); + errorMsg = testDb->getErrorText(); testDb->closeQuiet(); } delete testDb; @@ -546,7 +570,7 @@ bool DbDialog::validate() if (fileState) { - registeredDb = DBLIST->getByPath(ui->fileEdit->text()); + registeredDb = DBLIST->getByPath(getPath()); if (registeredDb && (mode == Mode::ADD || registeredDb != db)) { setValidState(ui->fileEdit, false, tr("This database is already on the list under name: %1").arg(registeredDb->getName())); @@ -601,6 +625,7 @@ void DbDialog::typeChanged(int index) { UNUSED(index); updateOptions(); + updateState(); } void DbDialog::valueForNameGenerationChanged() @@ -612,10 +637,9 @@ void DbDialog::valueForNameGenerationChanged() QString generatedName; DbPlugin* plugin = dbPlugins.count() > 0 ? dbPlugins[ui->typeCombo->currentText()] : nullptr; if (plugin) - generatedName = DBLIST->generateUniqueDbName(plugin, ui->fileEdit->text()); + generatedName = DBLIST->generateUniqueDbName(plugin, getPath()); else - generatedName = DBLIST->generateUniqueDbName(ui->fileEdit->text()); - + generatedName = DBLIST->generateUniqueDbName(getPath()); ui->nameEdit->setText(generatedName); } @@ -624,7 +648,7 @@ void DbDialog::browseForFile() { QString dir = getFileDialogInitPath(); QString path = QFileDialog::getOpenFileName(0, QString(), dir); - if (path.isNull()) + if (path.isEmpty()) return; QString key = helperToKey[dynamic_cast(sender())]; @@ -645,18 +669,16 @@ void DbDialog::browseClicked() { if (customBrowseHandler) { - QString newUrl = customBrowseHandler(ui->fileEdit->text()); + QString newUrl = customBrowseHandler(getPath()); if (!newUrl.isNull()) { - ui->fileEdit->setText(newUrl); + setPath(newUrl); updateState(); } return; } - bool createMode = (sender() == ui->browseCreateButton); - - QFileInfo fileInfo(ui->fileEdit->text()); + QFileInfo fileInfo(getPath()); QString dir; if (ui->fileEdit->text().isEmpty()) dir = getFileDialogInitPath(); @@ -667,7 +689,7 @@ void DbDialog::browseClicked() else dir = getFileDialogInitPath(); - QString path = getDbPath(createMode, dir); + QString path = getDbPath(dir); if (path.isNull()) return; @@ -679,8 +701,19 @@ void DbDialog::browseClicked() void DbDialog::testConnectionClicked() { - ui->testConnIcon->setPixmap(testDatabase() ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + QString errorMsg; + bool ok = testDatabase(errorMsg); + ui->testConnIcon->setPixmap(ok ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + connIconTooltip->setToolTip(ok ? QString() : errorMsg); ui->testConnIcon->setVisible(true); + if (!ok) + { + QString path = getPath(); + if (!path.isEmpty()) + notifyWarn(QString("%1: %2").arg(getPath(), errorMsg)); + else + notifyWarn(errorMsg); + } } void DbDialog::dbTypeChanged(int index) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h index 3d1a9da..76cd3ec 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h @@ -12,6 +12,7 @@ class DbPlugin; class QGridLayout; struct DbPluginOption; +class ImmediateTooltip; namespace Ui { class DbDialog; @@ -44,6 +45,8 @@ class GUI_API_EXPORT DbDialog : public QDialog protected: void changeEvent(QEvent *e); void showEvent(QShowEvent* e); + void dragEnterEvent(QDragEnterEvent* e); + void dropEvent(QDropEvent*e); private: void init(); @@ -53,7 +56,7 @@ class GUI_API_EXPORT DbDialog : public QDialog QVariant getValueFrom(DbPluginOption::Type type, QWidget* editor); void setValueFor(DbPluginOption::Type type, QWidget* editor, const QVariant& value); void updateType(); - bool testDatabase(); + bool testDatabase(QString& errorMsg); bool validate(); void updateState(); @@ -71,6 +74,7 @@ class GUI_API_EXPORT DbDialog : public QDialog bool disableTypeAutodetection = false; bool doAutoTest = false; bool nameManuallyEdited = false; + ImmediateTooltip* connIconTooltip = nullptr; static const constexpr int ADDITIONAL_ROWS_BEGIN_INDEX = 1; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui index 9878bec..f0ebe2a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui @@ -16,6 +16,9 @@ 0 + + true + Database @@ -45,20 +48,6 @@ - - - - Create new database file - - - - - - - :/icons/img/plus.png:/icons/img/plus.png - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp index 7dda03e..135bc9d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp @@ -187,6 +187,7 @@ void ExportDialog::initQueryPage() return dbOk && queryOk; }); + ui->queryEdit->setAlwaysEnforceErrorsChecking(true); connect(ui->queryEdit, SIGNAL(errorsChecked(bool)), ui->queryPage, SIGNAL(completeChanged())); connect(ui->queryEdit, SIGNAL(textChanged()), ui->queryPage, SIGNAL(completeChanged())); @@ -619,7 +620,7 @@ void ExportDialog::updatePluginOptions(ExportPlugin* plugin, int& optionsRow) configMapper = new ConfigMapper(cfgMain); configMapper->bindToConfig(pluginOptionsWidget); - connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(updateValidation())); plugin->validateOptions(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp index 565feb4..f443f18 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp @@ -1,12 +1,9 @@ #include "importdialog.h" #include "dblistmodel.h" #include "dbobjlistmodel.h" -#include "common/widgetstateindicator.h" #include "uiutils.h" #include "common/widgetcover.h" #include "services/dbmanager.h" -#include "services/pluginmanager.h" -#include "sqlitestudio.h" #include "plugins/importplugin.h" #include "ui_importdialog.h" #include "configmapper.h" @@ -14,6 +11,8 @@ #include "common/utils.h" #include "uiconfig.h" #include "themetuner.h" +#include "iconmanager.h" +#include "mainwindow.h" #include #include #include @@ -203,6 +202,7 @@ void ImportDialog::initDataSourcePage() void ImportDialog::removeOldOptions() { + pluginConfigOk.clear(); safe_delete(configMapper); safe_delete(pluginOptionsWidget); } @@ -284,7 +284,7 @@ void ImportDialog::updatePluginOptions(int& rows) configMapper = new ConfigMapper(cfgMain); configMapper->bindToConfig(pluginOptionsWidget); - connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(updateValidation())); updateValidation(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp index ebf9beb..d5249d0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -13,7 +13,6 @@ #include "indexexprcolumndialog.h" #include "windows/editorwindow.h" #include "services/codeformatter.h" -#include "common/compatibility.h" #include #include #include @@ -37,6 +36,10 @@ IndexDialog::IndexDialog(Db* db, const QString& index, QWidget* parent) : { existingIndex = true; init(); + + bool sysIdx = isSystemIndex(index); + ui->indexTab->setDisabled(sysIdx); + ui->ddlTab->setDisabled(sysIdx); } IndexDialog::~IndexDialog() @@ -709,15 +712,32 @@ void IndexDialog::preReject() preRejected = true; } +QString IndexDialog::getOriginalDdl() const +{ + SqliteCreateIndex* initialCreateIndex = originalCreateIndex->typeClone(); + initialCreateIndex->rebuildTokens(); + QString initialDdl = initialCreateIndex->detokenize(); + delete initialCreateIndex; + return initialDdl; +} + void IndexDialog::accept() { + QString initialDdl = getOriginalDdl(); rebuildCreateIndex(); + QString ddl = createIndex->detokenize(); + if (initialDdl == ddl) + { + // Nothing changed. Just close. + QDialog::accept(); + return; + } QStringList sqls; if (existingIndex) sqls << QString("DROP INDEX %1").arg(wrapObjIfNeeded(originalCreateIndex->index)); - sqls << createIndex->detokenize(); + sqls << ddl; if (!CFG_UI.General.DontShowDdlPreview.get()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h index 2e586d4..dd68137 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h @@ -123,6 +123,7 @@ class GUI_API_EXPORT IndexDialog : public QDialog QStringList getExistingColumnExprs(const QString& exceptThis = QString()) const; QStringList getTableColumns() const; void preReject(); + QString getOriginalDdl() const; bool existingIndex = false; Db* db = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp index d9f05ff..b68ce10 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp @@ -1,3 +1,4 @@ +#include "iconmanager.h" #include "languagedialog.h" #include "ui_languagedialog.h" #include "uiconfig.h" @@ -43,3 +44,8 @@ void LanguageDialog::askedForDefaultLanguage() { CFG_UI.General.LanguageAsked.set(true); } + +void LanguageDialog::showEvent(QShowEvent*) +{ + setWindowIcon(ICONS.SQLITESTUDIO_APP); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h index d5fbed4..a11fc8a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h @@ -13,6 +13,9 @@ class GUI_API_EXPORT LanguageDialog : public QDialog { Q_OBJECT + protected: + void showEvent(QShowEvent*); + public: explicit LanguageDialog(QWidget *parent = 0); ~LanguageDialog(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui index 12794ce..045c245 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui @@ -18,7 +18,6 @@ - 75 true @@ -35,12 +34,11 @@ 16 - 75 true - 0.0.0 + 0.0.0 Qt::AlignCenter diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp index 5dc506f..f4d93a4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp @@ -2,9 +2,9 @@ #include "ui_populateconfigdialog.h" #include "plugins/populateplugin.h" #include "services/populatemanager.h" -#include "sqlitestudio.h" #include "formmanager.h" #include "configmapper.h" +#include "mainwindow.h" #include "uiutils.h" #include #include @@ -55,7 +55,7 @@ void PopulateConfigDialog::init() ui->headerLabel->setText(headerString ); configMapper = new ConfigMapper(engine->getConfig()); - connect(configMapper, SIGNAL(modified()), this, SLOT(validateEngine())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(validateEngine())); connect(POPULATE_MANAGER, SIGNAL(validationResultFromPlugin(bool,CfgEntry*,QString)), this, SLOT(validationResultFromPlugin(bool,CfgEntry*,QString))); connect(POPULATE_MANAGER, SIGNAL(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool)), this, SLOT(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool))); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp index aae0d58..89fff04 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp @@ -32,8 +32,13 @@ PopulateDialog::~PopulateDialog() void PopulateDialog::setDbAndTable(Db* db, const QString& table) { + QString oldTable = ui->tableCombo->currentText(); ui->databaseCombo->setCurrentText(db->getName()); ui->tableCombo->setCurrentText(table); + + // #4177 + if (oldTable == table) + refreshColumns(); } void PopulateDialog::init() @@ -48,7 +53,7 @@ void PopulateDialog::init() return p1->getTitle().compare(p2->getTitle()) < 0; }); - for (PopulatePlugin* plugin : plugins) + for (PopulatePlugin*& plugin : plugins) { pluginByName[plugin->getName()] = plugin; pluginTitles << plugin->getTitle(); @@ -103,7 +108,7 @@ void PopulateDialog::rebuildEngines(const QHashtableCombo->currentText().isNull(); bool colCountOk = false; - for (const ColumnEntry& entry : columnEntries) + for (ColumnEntry& entry : columnEntries) { if (entry.check->isChecked()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp index 7890b3c..896ef39 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp @@ -1,10 +1,13 @@ #include "triggercolumnsdialog.h" #include "ui_triggercolumnsdialog.h" -#include "uiutils.h" #include TriggerColumnsDialog::TriggerColumnsDialog(QWidget *parent, int globalX, int globalY) : +#ifdef Q_OS_OSX + QDialog(parent), +#else QDialog(parent, Qt::Popup), +#endif globalX(globalX), globalY(globalY), ui(new Ui::TriggerColumnsDialog) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp index 86862e2..5091613 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp @@ -1,12 +1,10 @@ #include "triggerdialog.h" #include "ui_triggerdialog.h" -#include "parser/ast/sqliteselect.h" #include "services/notifymanager.h" #include "parser/ast/sqliteexpr.h" #include "triggercolumnsdialog.h" #include "common/utils_sql.h" #include "schemaresolver.h" -#include "parser/parser.h" #include "iconmanager.h" #include "db/chainexecutor.h" #include "dbtree/dbtree.h" @@ -208,11 +206,14 @@ void TriggerDialog::readTrigger() if (createTrigger->queries.size() > 0) { QStringList sqls; - for (SqliteQuery* query : createTrigger->queries) + for (SqliteQuery*& query : createTrigger->queries) sqls << query->detokenize(); ui->codeEdit->setPlainText(sqls.join(";\n")+";"); } + + rebuildTrigger(); + originalDdl = ddl; } void TriggerDialog::setupVirtualSqls() @@ -299,7 +300,7 @@ void TriggerDialog::rebuildTrigger() if (actionType == SqliteCreateTrigger::Event::UPDATE_OF) { QStringList colNames; - for (const QString& colName : selectedColumns) + for (QString& colName : selectedColumns) colNames << wrapObjIfNeeded(colName); columns = " "+colNames.join(", "); @@ -321,7 +322,7 @@ void TriggerDialog::rebuildTrigger() if (!scope.isNull()) scope.prepend(" "); - ddl = tempDdl.arg(trigName).arg(when).arg(action).arg(columns).arg(target).arg(scope).arg(precondition).arg(queries); + ddl = tempDdl.arg(trigName, when, action, columns, target, scope, precondition, queries); } void TriggerDialog::updateState() @@ -385,6 +386,12 @@ void TriggerDialog::tableChanged(const QString& newValue) void TriggerDialog::accept() { rebuildTrigger(); + if (originalDdl == ddl) + { + // Nothing changed. Just close. + QDialog::accept(); + return; + } QStringList sqls; if (existingTrigger) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h index 712ea5e..1eb7064 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h @@ -50,6 +50,7 @@ class GUI_API_EXPORT TriggerDialog : public QDialog QStringList targetColumns; QStringList selectedColumns; QString ddl; + QString originalDdl; SqliteCreateTriggerPtr createTrigger; Ui::TriggerDialog *ui = nullptr; -- cgit v1.2.3